Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/prusa3d/PrusaSlicer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEnrico Turri <enricoturri@seznam.cz>2019-10-17 09:09:23 +0300
committerEnrico Turri <enricoturri@seznam.cz>2019-10-17 09:09:23 +0300
commit93ae170113c8d5112c69d39762525b848085f8b2 (patch)
tree13c1f4a97a5dd96d43c1b6a6662bbf81c5eeacfa
parente6403a74efaa15472689f460bd9a754d6129c348 (diff)
parent0ee78543a435b87261e37e94f8a87633dccbe42e (diff)
Merge branch 'master' of https://github.com/prusa3d/PrusaSlicer into et_3dconnexion
-rw-r--r--CMakeLists.txt16
-rw-r--r--cmake/modules/FindNLopt.cmake (renamed from src/libnest2d/cmake_modules/FindNLopt.cmake)18
-rw-r--r--cmake/modules/FindTBB.cmake37
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/libnest2d/CMakeLists.txt129
-rw-r--r--src/libnest2d/cmake_modules/DownloadNLopt.cmake35
-rw-r--r--src/libnest2d/cmake_modules/DownloadProject.CMakeLists.cmake.in17
-rw-r--r--src/libnest2d/cmake_modules/DownloadProject.cmake182
-rw-r--r--src/libnest2d/cmake_modules/FindClipper.cmake58
-rw-r--r--src/libnest2d/include/libnest2d.h134
-rw-r--r--src/libnest2d/include/libnest2d/backends/clipper/CMakeLists.txt73
-rw-r--r--src/libnest2d/include/libnest2d/geometry_traits_nfp.hpp451
-rw-r--r--src/libnest2d/include/libnest2d/libnest2d.hpp949
-rw-r--r--src/libnest2d/include/libnest2d/nester.hpp869
-rw-r--r--src/libnest2d/include/libnest2d/optimizers/nlopt/CMakeLists.txt61
-rw-r--r--src/libnest2d/include/libnest2d/placers/placer_boilerplate.hpp2
-rw-r--r--src/libnest2d/include/libnest2d/selections/selection_boilerplate.hpp10
-rw-r--r--src/libnest2d/src/libnest2d.cpp15
-rw-r--r--src/libnest2d/tests/CMakeLists.txt60
-rw-r--r--src/libnest2d/tests/printer_parts.cpp3175
-rw-r--r--src/libnest2d/tests/printer_parts.h14
-rw-r--r--src/libnest2d/tests/test.cpp1062
-rw-r--r--src/libslic3r/CMakeLists.txt4
-rw-r--r--src/libslic3r/Config.cpp6
-rw-r--r--src/libslic3r/Config.hpp8
-rw-r--r--src/libslic3r/ExPolygon.hpp13
-rw-r--r--src/libslic3r/Fill/Fill.hpp2
-rw-r--r--src/libslic3r/Fill/FillBase.hpp1
-rw-r--r--src/libslic3r/GCode/CoolingBuffer.hpp2
-rw-r--r--src/libslic3r/GCodeReader.hpp2
-rw-r--r--src/libslic3r/GCodeWriter.cpp2
-rw-r--r--src/libslic3r/Geometry.cpp89
-rw-r--r--src/libslic3r/Geometry.hpp9
-rw-r--r--src/libslic3r/Line.cpp6
-rw-r--r--src/libslic3r/Point.hpp42
-rw-r--r--src/libslic3r/Polygon.cpp53
-rw-r--r--src/libslic3r/Polygon.hpp2
-rw-r--r--src/libslic3r/PrintConfig.cpp35
-rw-r--r--src/libslic3r/PrintConfig.hpp18
-rw-r--r--src/libslic3r/Time.cpp2
-rw-r--r--src/libslic3r/TriangleMesh.cpp24
-rw-r--r--src/libslic3r/TriangleMesh.hpp6
-rw-r--r--src/slic3r/GUI/Preset.cpp26
-rw-r--r--src/slic3r/GUI/Preset.hpp4
-rw-r--r--tests/CMakeLists.txt18
-rw-r--r--tests/data/fff_print_tests/test_gcodewriter/config_lift_unlift.ini30
-rw-r--r--tests/data/test_3mf/Geräte/Büchse.3mfbin0 -> 1416 bytes
-rw-r--r--tests/example/CMakeLists.txt7
-rw-r--r--tests/fff_print/CMakeLists.txt20
-rw-r--r--tests/fff_print/fff_print_tests.cpp4
-rw-r--r--tests/fff_print/test_data.cpp327
-rw-r--r--tests/fff_print/test_data.hpp77
-rw-r--r--tests/fff_print/test_fill.cpp474
-rw-r--r--tests/fff_print/test_flow.cpp203
-rw-r--r--tests/fff_print/test_gcodewriter.cpp124
-rw-r--r--tests/fff_print/test_model.cpp61
-rw-r--r--tests/fff_print/test_print.cpp144
-rw-r--r--tests/fff_print/test_printgcode.cpp274
-rw-r--r--tests/fff_print/test_printobject.cpp86
-rw-r--r--tests/fff_print/test_skirt_brim.cpp263
-rw-r--r--tests/fff_print/test_trianglemesh.cpp433
-rw-r--r--tests/libnest2d/CMakeLists.txt3
-rw-r--r--tests/libnest2d/libnest2d_tests_main.cpp51
-rw-r--r--tests/libslic3r/CMakeLists.txt12
-rw-r--r--tests/libslic3r/libslic3r_tests.cpp15
-rw-r--r--tests/libslic3r/test_3mf.cpp20
-rw-r--r--tests/libslic3r/test_geometry.cpp375
-rw-r--r--tests/libslic3r/test_polygon.cpp44
-rw-r--r--tests/sla_print/CMakeLists.txt5
-rw-r--r--tests/timeutils/CMakeLists.txt9
-rw-r--r--xs/xsp/Config.xsp3
71 files changed, 4828 insertions, 5978 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 93339eb0b..7b1d73f89 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -291,14 +291,14 @@ if(SLIC3R_STATIC)
endif()
set(TBB_DEBUG 1)
find_package(TBB REQUIRED)
-include_directories(${TBB_INCLUDE_DIRS})
-add_definitions(${TBB_DEFINITIONS})
-if(MSVC)
- # Suppress implicit linking of the TBB libraries by the Visual Studio compiler.
- add_definitions(-D__TBB_NO_IMPLICIT_LINKAGE)
-endif()
+# include_directories(${TBB_INCLUDE_DIRS})
+# add_definitions(${TBB_DEFINITIONS})
+# if(MSVC)
+# # Suppress implicit linking of the TBB libraries by the Visual Studio compiler.
+# add_definitions(-D__TBB_NO_IMPLICIT_LINKAGE)
+# endif()
# The Intel TBB library will use the std::exception_ptr feature of C++11.
-add_definitions(-DTBB_USE_CAPTURED_EXCEPTION=0)
+# add_definitions(-DTBB_USE_CAPTURED_EXCEPTION=0)
find_package(CURL REQUIRED)
include_directories(${CURL_INCLUDE_DIRS})
@@ -375,6 +375,8 @@ add_custom_target(pot
COMMENT "Generate pot file from strings in the source tree"
)
+find_package(NLopt 1.4 REQUIRED)
+
# 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/src/libnest2d/cmake_modules/FindNLopt.cmake b/cmake/modules/FindNLopt.cmake
index 2f813b6aa..912ce8d30 100644
--- a/src/libnest2d/cmake_modules/FindNLopt.cmake
+++ b/cmake/modules/FindNLopt.cmake
@@ -21,8 +21,7 @@
set(NLopt_FOUND FALSE)
set(NLopt_ERROR_REASON "")
set(NLopt_DEFINITIONS "")
-set(NLopt_LIBS)
-
+unset(NLopt_LIBS CACHE)
set(NLopt_DIR $ENV{NLOPT})
if(NOT NLopt_DIR)
@@ -48,15 +47,14 @@ if(NOT NLopt_DIR)
set(NLopt_ERROR_REASON "${NLopt_ERROR_REASON} Cannot find NLopt header file '${_NLopt_HEADER_FILE_NAME}'.")
endif()
unset(_NLopt_HEADER_FILE_NAME)
- unset(_NLopt_HEADER_FILE)
-
+
if(NOT NLopt_FOUND)
set(NLopt_ERROR_REASON "${NLopt_ERROR_REASON} NLopt not found in system directories (and environment variable NLOPT is not set).")
else()
get_filename_component(NLopt_INCLUDE_DIR ${_NLopt_HEADER_FILE} DIRECTORY )
endif()
-
+ unset(_NLopt_HEADER_FILE CACHE)
else()
@@ -95,7 +93,7 @@ else()
set(NLopt_ERROR_REASON "${NLopt_ERROR_REASON} Cannot find NLopt header file '${_NLopt_HEADER_FILE_NAME}' in '${NLopt_INCLUDE_DIR}'.")
endif()
unset(_NLopt_HEADER_FILE_NAME)
- unset(_NLopt_HEADER_FILE)
+ unset(_NLopt_HEADER_FILE CACHE)
endif()
@@ -114,10 +112,10 @@ if(NLopt_FOUND)
message(STATUS "Found NLopt in '${NLopt_DIR}'.")
message(STATUS "Using NLopt include directory '${NLopt_INCLUDE_DIR}'.")
message(STATUS "Using NLopt library '${NLopt_LIBS}'.")
- add_library(Nlopt::Nlopt INTERFACE IMPORTED)
- set_target_properties(Nlopt::Nlopt PROPERTIES INTERFACE_LINK_LIBRARIES ${NLopt_LIBS})
- set_target_properties(Nlopt::Nlopt PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${NLopt_INCLUDE_DIR})
- set_target_properties(Nlopt::Nlopt PROPERTIES INTERFACE_COMPILE_DEFINITIONS "${NLopt_DEFINITIONS}")
+ add_library(NLopt::nlopt INTERFACE IMPORTED)
+ set_target_properties(NLopt::nlopt PROPERTIES INTERFACE_LINK_LIBRARIES ${NLopt_LIBS})
+ set_target_properties(NLopt::nlopt PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${NLopt_INCLUDE_DIR})
+ set_target_properties(NLopt::nlopt PROPERTIES INTERFACE_COMPILE_DEFINITIONS "${NLopt_DEFINITIONS}")
# target_link_libraries(Nlopt::Nlopt INTERFACE ${NLopt_LIBS})
# target_include_directories(Nlopt::Nlopt INTERFACE ${NLopt_INCLUDE_DIR})
# target_compile_definitions(Nlopt::Nlopt INTERFACE ${NLopt_DEFINITIONS})
diff --git a/cmake/modules/FindTBB.cmake b/cmake/modules/FindTBB.cmake
index e5115ab44..b09bafda3 100644
--- a/cmake/modules/FindTBB.cmake
+++ b/cmake/modules/FindTBB.cmake
@@ -250,26 +250,23 @@ if(NOT TBB_FOUND)
endif()
endforeach()
- unset(TBB_STATIC_SUFFIX)
-
##################################
# Set compile flags and libraries
##################################
set(TBB_DEFINITIONS_RELEASE "")
- set(TBB_DEFINITIONS_DEBUG "-DTBB_USE_DEBUG=1")
+ set(TBB_DEFINITIONS_DEBUG "TBB_USE_DEBUG=1")
if(TBB_LIBRARIES_${TBB_BUILD_TYPE})
- set(TBB_DEFINITIONS "${TBB_DEFINITIONS_${TBB_BUILD_TYPE}}")
set(TBB_LIBRARIES "${TBB_LIBRARIES_${TBB_BUILD_TYPE}}")
- elseif(TBB_LIBRARIES_RELEASE)
- set(TBB_DEFINITIONS "${TBB_DEFINITIONS_RELEASE}")
- set(TBB_LIBRARIES "${TBB_LIBRARIES_RELEASE}")
- elseif(TBB_LIBRARIES_DEBUG)
- set(TBB_DEFINITIONS "${TBB_DEFINITIONS_DEBUG}")
- set(TBB_LIBRARIES "${TBB_LIBRARIES_DEBUG}")
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
@@ -280,25 +277,23 @@ if(NOT TBB_FOUND)
##################################
if(NOT CMAKE_VERSION VERSION_LESS 3.0 AND TBB_FOUND)
- add_library(tbb UNKNOWN IMPORTED)
- set_target_properties(tbb PROPERTIES
+ 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 PROPERTIES
- INTERFACE_COMPILE_DEFINITIONS "$<$<OR:$<CONFIG:Debug>,$<CONFIG:RelWithDebInfo>>:TBB_USE_DEBUG=1>"
+ set_target_properties(TBB::tbb PROPERTIES
+ INTERFACE_COMPILE_DEFINITIONS "${TBB_DEFINITIONS};$<$<OR:$<CONFIG:Debug>,$<CONFIG:RelWithDebInfo>>:${TBB_DEFINITIONS_DEBUG}>;$<$<CONFIG:Release>:${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}
)
- elseif(TBB_LIBRARIES_RELEASE)
- set_target_properties(tbb PROPERTIES IMPORTED_LOCATION ${TBB_LIBRARIES_RELEASE})
- else()
- set_target_properties(tbb PROPERTIES
- INTERFACE_COMPILE_DEFINITIONS "${TBB_DEFINITIONS_DEBUG}"
- IMPORTED_LOCATION ${TBB_LIBRARIES_DEBUG}
- )
+ 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")
endif()
endif()
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index c087e57e2..530512cbb 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -16,7 +16,6 @@ add_subdirectory(semver)
add_subdirectory(libigl)
# Adding libnest2d project for bin packing...
-set(LIBNEST2D_UNITTESTS ON CACHE BOOL "Force generating unittests for libnest2d")
add_subdirectory(libnest2d)
add_subdirectory(libslic3r)
diff --git a/src/libnest2d/CMakeLists.txt b/src/libnest2d/CMakeLists.txt
index 0d091c171..6484da3d0 100644
--- a/src/libnest2d/CMakeLists.txt
+++ b/src/libnest2d/CMakeLists.txt
@@ -1,106 +1,31 @@
-cmake_minimum_required(VERSION 3.0)
-
-project(Libnest2D)
-
-if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
- # Update if necessary
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-long-long ")
-endif()
-
-set(CMAKE_CXX_STANDARD 11)
-set(CMAKE_CXX_STANDARD_REQUIRED)
-
-# Add our own cmake module path.
-list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake_modules/)
-
-option(LIBNEST2D_HEADER_ONLY "If enabled static library will not be built." ON)
-
-set(GEOMETRY_BACKENDS clipper boost eigen)
-set(LIBNEST2D_GEOMETRIES clipper CACHE STRING "Geometry backend")
-set_property(CACHE LIBNEST2D_GEOMETRIES PROPERTY STRINGS ${GEOMETRY_BACKENDS})
-list(FIND GEOMETRY_BACKENDS ${LIBNEST2D_GEOMETRIES} GEOMETRY_TYPE)
-if(${GEOMETRY_TYPE} EQUAL -1)
- message(FATAL_ERROR "Option ${LIBNEST2D_GEOMETRIES} not supported, valid entries are ${GEOMETRY_BACKENDS}")
-endif()
-
-set(OPTIMIZERS nlopt optimlib)
-set(LIBNEST2D_OPTIMIZER nlopt CACHE STRING "Optimization backend")
-set_property(CACHE LIBNEST2D_OPTIMIZER PROPERTY STRINGS ${OPTIMIZERS})
-list(FIND OPTIMIZERS ${LIBNEST2D_OPTIMIZER} OPTIMIZER_TYPE)
-if(${OPTIMIZER_TYPE} EQUAL -1)
- message(FATAL_ERROR "Option ${LIBNEST2D_OPTIMIZER} not supported, valid entries are ${OPTIMIZERS}")
-endif()
-
-add_library(libnest2d INTERFACE)
-
-set(SRC_DIR ${PROJECT_SOURCE_DIR}/include)
-
set(LIBNEST2D_SRCFILES
- ${SRC_DIR}/libnest2d/libnest2d.hpp # Templates only
- ${SRC_DIR}/libnest2d/geometry_traits.hpp
- ${SRC_DIR}/libnest2d/geometry_traits_nfp.hpp
- ${SRC_DIR}/libnest2d/common.hpp
- ${SRC_DIR}/libnest2d/optimizer.hpp
- ${SRC_DIR}/libnest2d/utils/metaloop.hpp
- ${SRC_DIR}/libnest2d/utils/rotfinder.hpp
- ${SRC_DIR}/libnest2d/utils/rotcalipers.hpp
- ${SRC_DIR}/libnest2d/utils/bigint.hpp
- ${SRC_DIR}/libnest2d/utils/rational.hpp
- ${SRC_DIR}/libnest2d/placers/placer_boilerplate.hpp
- ${SRC_DIR}/libnest2d/placers/bottomleftplacer.hpp
- ${SRC_DIR}/libnest2d/placers/nfpplacer.hpp
- ${SRC_DIR}/libnest2d/selections/selection_boilerplate.hpp
- ${SRC_DIR}/libnest2d/selections/filler.hpp
- ${SRC_DIR}/libnest2d/selections/firstfit.hpp
- ${SRC_DIR}/libnest2d/selections/djd_heuristic.hpp
+ include/libnest2d/libnest2d.hpp
+ include/libnest2d/nester.hpp
+ include/libnest2d/geometry_traits.hpp
+ include/libnest2d/geometry_traits_nfp.hpp
+ include/libnest2d/common.hpp
+ include/libnest2d/optimizer.hpp
+ include/libnest2d/utils/metaloop.hpp
+ include/libnest2d/utils/rotfinder.hpp
+ include/libnest2d/utils/rotcalipers.hpp
+ include/libnest2d/placers/placer_boilerplate.hpp
+ include/libnest2d/placers/bottomleftplacer.hpp
+ include/libnest2d/placers/nfpplacer.hpp
+ include/libnest2d/selections/selection_boilerplate.hpp
+ #include/libnest2d/selections/filler.hpp
+ include/libnest2d/selections/firstfit.hpp
+ #include/libnest2d/selections/djd_heuristic.hpp
+ include/libnest2d/backends/clipper/geometries.hpp
+ include/libnest2d/backends/clipper/clipper_polygon.hpp
+ include/libnest2d/optimizers/nlopt/nlopt_boilerplate.hpp
+ include/libnest2d/optimizers/nlopt/simplex.hpp
+ include/libnest2d/optimizers/nlopt/subplex.hpp
+ include/libnest2d/optimizers/nlopt/genetic.hpp
+ src/libnest2d.cpp
)
-set(TBB_STATIC ON)
-find_package(TBB QUIET)
-if(TBB_FOUND)
- message(STATUS "Parallelization with Intel TBB")
- target_include_directories(libnest2d INTERFACE ${TBB_INCLUDE_DIRS})
- target_compile_definitions(libnest2d INTERFACE ${TBB_DEFINITIONS} -DUSE_TBB)
- if(MSVC)
- # Suppress implicit linking of the TBB libraries by the Visual Studio compiler.
- target_compile_definitions(libnest2d INTERFACE -D__TBB_NO_IMPLICIT_LINKAGE)
- endif()
- # The Intel TBB library will use the std::exception_ptr feature of C++11.
- target_compile_definitions(libnest2d INTERFACE -DTBB_USE_CAPTURED_EXCEPTION=0)
-
- find_package(Threads REQUIRED)
- target_link_libraries(libnest2d INTERFACE
- tbb # VS debug mode needs linking this way:
- # ${TBB_LIBRARIES}
- ${CMAKE_DL_LIBS}
- Threads::Threads
- )
-else()
- find_package(OpenMP QUIET)
-
- if(OpenMP_CXX_FOUND)
- message(STATUS "Parallelization with OpenMP")
- target_include_directories(libnest2d INTERFACE OpenMP::OpenMP_CXX)
- target_link_libraries(libnest2d INTERFACE OpenMP::OpenMP_CXX)
- else()
- message("Parallelization with C++11 threads")
- find_package(Threads REQUIRED)
- target_link_libraries(libnest2d INTERFACE Threads::Threads)
- endif()
-endif()
-
-add_subdirectory(${SRC_DIR}/libnest2d/backends/${LIBNEST2D_GEOMETRIES})
-target_link_libraries(libnest2d INTERFACE ${LIBNEST2D_GEOMETRIES}Backend)
-
-add_subdirectory(${SRC_DIR}/libnest2d/optimizers/${LIBNEST2D_OPTIMIZER})
-target_link_libraries(libnest2d INTERFACE ${LIBNEST2D_OPTIMIZER}Optimizer)
-
-# target_sources(libnest2d INTERFACE ${LIBNEST2D_SRCFILES})
-target_include_directories(libnest2d INTERFACE ${SRC_DIR})
+add_library(libnest2d ${LIBNEST2D_SRCFILES})
-if(NOT LIBNEST2D_HEADER_ONLY)
- set(LIBNAME libnest2d_${LIBNEST2D_GEOMETRIES}_${LIBNEST2D_OPTIMIZER})
- add_library(${LIBNAME} ${PROJECT_SOURCE_DIR}/src/libnest2d.cpp)
- target_link_libraries(${LIBNAME} PUBLIC libnest2d)
- target_compile_definitions(${LIBNAME} PUBLIC LIBNEST2D_STATIC)
-endif()
+target_include_directories(libnest2d PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include)
+target_link_libraries(libnest2d PUBLIC clipper NLopt::nlopt TBB::tbb Boost::boost)
+target_compile_definitions(libnest2d PUBLIC USE_TBB LIBNEST2D_STATIC LIBNEST2D_OPTIMIZER_nlopt LIBNEST2D_GEOMETRIES_clipper)
diff --git a/src/libnest2d/cmake_modules/DownloadNLopt.cmake b/src/libnest2d/cmake_modules/DownloadNLopt.cmake
deleted file mode 100644
index 62b2b4c1a..000000000
--- a/src/libnest2d/cmake_modules/DownloadNLopt.cmake
+++ /dev/null
@@ -1,35 +0,0 @@
-include(DownloadProject)
-
-if (CMAKE_VERSION VERSION_LESS 3.2)
- set(UPDATE_DISCONNECTED_IF_AVAILABLE "")
-else()
- set(UPDATE_DISCONNECTED_IF_AVAILABLE "UPDATE_DISCONNECTED 1")
-endif()
-
-set(URL_NLOPT "https://github.com/stevengj/nlopt.git"
- CACHE STRING "Location of the nlopt git repository")
-
-# set(NLopt_DIR ${CMAKE_BINARY_DIR}/nlopt)
-include(DownloadProject)
-download_project( PROJ nlopt
- GIT_REPOSITORY ${URL_NLOPT}
- GIT_TAG v2.5.0
- # CMAKE_CACHE_ARGS -DBUILD_SHARED_LIBS:BOOL=OFF -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX=${NLopt_DIR}
- ${UPDATE_DISCONNECTED_IF_AVAILABLE}
-)
-
-set(SHARED_LIBS_STATE BUILD_SHARED_LIBS)
-set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
-set(NLOPT_PYTHON OFF CACHE BOOL "" FORCE)
-set(NLOPT_OCTAVE OFF CACHE BOOL "" FORCE)
-set(NLOPT_MATLAB OFF CACHE BOOL "" FORCE)
-set(NLOPT_GUILE OFF CACHE BOOL "" FORCE)
-set(NLOPT_SWIG OFF CACHE BOOL "" FORCE)
-set(NLOPT_LINK_PYTHON OFF CACHE BOOL "" FORCE)
-
-add_subdirectory(${nlopt_SOURCE_DIR} ${nlopt_BINARY_DIR})
-
-set(NLopt_LIBS nlopt)
-set(NLopt_INCLUDE_DIR ${nlopt_BINARY_DIR}
- ${nlopt_BINARY_DIR}/src/api)
-set(SHARED_LIBS_STATE ${SHARED_STATE}) \ No newline at end of file
diff --git a/src/libnest2d/cmake_modules/DownloadProject.CMakeLists.cmake.in b/src/libnest2d/cmake_modules/DownloadProject.CMakeLists.cmake.in
deleted file mode 100644
index d5cf3c1d9..000000000
--- a/src/libnest2d/cmake_modules/DownloadProject.CMakeLists.cmake.in
+++ /dev/null
@@ -1,17 +0,0 @@
-# Distributed under the OSI-approved MIT License. See accompanying
-# file LICENSE or https://github.com/Crascit/DownloadProject for details.
-
-cmake_minimum_required(VERSION 2.8.2)
-
-project(${DL_ARGS_PROJ}-download NONE)
-
-include(ExternalProject)
-ExternalProject_Add(${DL_ARGS_PROJ}-download
- ${DL_ARGS_UNPARSED_ARGUMENTS}
- SOURCE_DIR "${DL_ARGS_SOURCE_DIR}"
- BINARY_DIR "${DL_ARGS_BINARY_DIR}"
- CONFIGURE_COMMAND ""
- BUILD_COMMAND ""
- INSTALL_COMMAND ""
- TEST_COMMAND ""
-) \ No newline at end of file
diff --git a/src/libnest2d/cmake_modules/DownloadProject.cmake b/src/libnest2d/cmake_modules/DownloadProject.cmake
deleted file mode 100644
index 1709e09ad..000000000
--- a/src/libnest2d/cmake_modules/DownloadProject.cmake
+++ /dev/null
@@ -1,182 +0,0 @@
-# Distributed under the OSI-approved MIT License. See accompanying
-# file LICENSE or https://github.com/Crascit/DownloadProject for details.
-#
-# MODULE: DownloadProject
-#
-# PROVIDES:
-# download_project( PROJ projectName
-# [PREFIX prefixDir]
-# [DOWNLOAD_DIR downloadDir]
-# [SOURCE_DIR srcDir]
-# [BINARY_DIR binDir]
-# [QUIET]
-# ...
-# )
-#
-# Provides the ability to download and unpack a tarball, zip file, git repository,
-# etc. at configure time (i.e. when the cmake command is run). How the downloaded
-# and unpacked contents are used is up to the caller, but the motivating case is
-# to download source code which can then be included directly in the build with
-# add_subdirectory() after the call to download_project(). Source and build
-# directories are set up with this in mind.
-#
-# The PROJ argument is required. The projectName value will be used to construct
-# the following variables upon exit (obviously replace projectName with its actual
-# value):
-#
-# projectName_SOURCE_DIR
-# projectName_BINARY_DIR
-#
-# The SOURCE_DIR and BINARY_DIR arguments are optional and would not typically
-# need to be provided. They can be specified if you want the downloaded source
-# and build directories to be located in a specific place. The contents of
-# projectName_SOURCE_DIR and projectName_BINARY_DIR will be populated with the
-# locations used whether you provide SOURCE_DIR/BINARY_DIR or not.
-#
-# The DOWNLOAD_DIR argument does not normally need to be set. It controls the
-# location of the temporary CMake build used to perform the download.
-#
-# The PREFIX argument can be provided to change the base location of the default
-# values of DOWNLOAD_DIR, SOURCE_DIR and BINARY_DIR. If all of those three arguments
-# are provided, then PREFIX will have no effect. The default value for PREFIX is
-# CMAKE_BINARY_DIR.
-#
-# The QUIET option can be given if you do not want to show the output associated
-# with downloading the specified project.
-#
-# In addition to the above, any other options are passed through unmodified to
-# ExternalProject_Add() to perform the actual download, patch and update steps.
-# The following ExternalProject_Add() options are explicitly prohibited (they
-# are reserved for use by the download_project() command):
-#
-# CONFIGURE_COMMAND
-# BUILD_COMMAND
-# INSTALL_COMMAND
-# TEST_COMMAND
-#
-# Only those ExternalProject_Add() arguments which relate to downloading, patching
-# and updating of the project sources are intended to be used. Also note that at
-# least one set of download-related arguments are required.
-#
-# If using CMake 3.2 or later, the UPDATE_DISCONNECTED option can be used to
-# prevent a check at the remote end for changes every time CMake is run
-# after the first successful download. See the documentation of the ExternalProject
-# module for more information. It is likely you will want to use this option if it
-# is available to you. Note, however, that the ExternalProject implementation contains
-# bugs which result in incorrect handling of the UPDATE_DISCONNECTED option when
-# using the URL download method or when specifying a SOURCE_DIR with no download
-# method. Fixes for these have been created, the last of which is scheduled for
-# inclusion in CMake 3.8.0. Details can be found here:
-#
-# https://gitlab.kitware.com/cmake/cmake/commit/bdca68388bd57f8302d3c1d83d691034b7ffa70c
-# https://gitlab.kitware.com/cmake/cmake/issues/16428
-#
-# If you experience build errors related to the update step, consider avoiding
-# the use of UPDATE_DISCONNECTED.
-#
-# EXAMPLE USAGE:
-#
-# include(DownloadProject)
-# download_project(PROJ googletest
-# GIT_REPOSITORY https://github.com/google/googletest.git
-# GIT_TAG master
-# UPDATE_DISCONNECTED 1
-# QUIET
-# )
-#
-# add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR})
-#
-#========================================================================================
-
-
-set(_DownloadProjectDir "${CMAKE_CURRENT_LIST_DIR}")
-
-include(CMakeParseArguments)
-
-function(download_project)
-
- set(options QUIET)
- set(oneValueArgs
- PROJ
- PREFIX
- DOWNLOAD_DIR
- SOURCE_DIR
- BINARY_DIR
- # Prevent the following from being passed through
- CONFIGURE_COMMAND
- BUILD_COMMAND
- INSTALL_COMMAND
- TEST_COMMAND
- )
- set(multiValueArgs "")
-
- cmake_parse_arguments(DL_ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
-
- # Hide output if requested
- if (DL_ARGS_QUIET)
- set(OUTPUT_QUIET "OUTPUT_QUIET")
- else()
- unset(OUTPUT_QUIET)
- message(STATUS "Downloading/updating ${DL_ARGS_PROJ}")
- endif()
-
- # Set up where we will put our temporary CMakeLists.txt file and also
- # the base point below which the default source and binary dirs will be.
- # The prefix must always be an absolute path.
- if (NOT DL_ARGS_PREFIX)
- set(DL_ARGS_PREFIX "${CMAKE_BINARY_DIR}")
- else()
- get_filename_component(DL_ARGS_PREFIX "${DL_ARGS_PREFIX}" ABSOLUTE
- BASE_DIR "${CMAKE_CURRENT_BINARY_DIR}")
- endif()
- if (NOT DL_ARGS_DOWNLOAD_DIR)
- set(DL_ARGS_DOWNLOAD_DIR "${DL_ARGS_PREFIX}/${DL_ARGS_PROJ}-download")
- endif()
-
- # Ensure the caller can know where to find the source and build directories
- if (NOT DL_ARGS_SOURCE_DIR)
- set(DL_ARGS_SOURCE_DIR "${DL_ARGS_PREFIX}/${DL_ARGS_PROJ}-src")
- endif()
- if (NOT DL_ARGS_BINARY_DIR)
- set(DL_ARGS_BINARY_DIR "${DL_ARGS_PREFIX}/${DL_ARGS_PROJ}-build")
- endif()
- set(${DL_ARGS_PROJ}_SOURCE_DIR "${DL_ARGS_SOURCE_DIR}" PARENT_SCOPE)
- set(${DL_ARGS_PROJ}_BINARY_DIR "${DL_ARGS_BINARY_DIR}" PARENT_SCOPE)
-
- # The way that CLion manages multiple configurations, it causes a copy of
- # the CMakeCache.txt to be copied across due to it not expecting there to
- # be a project within a project. This causes the hard-coded paths in the
- # cache to be copied and builds to fail. To mitigate this, we simply
- # remove the cache if it exists before we configure the new project. It
- # is safe to do so because it will be re-generated. Since this is only
- # executed at the configure step, it should not cause additional builds or
- # downloads.
- file(REMOVE "${DL_ARGS_DOWNLOAD_DIR}/CMakeCache.txt")
-
- # Create and build a separate CMake project to carry out the download.
- # If we've already previously done these steps, they will not cause
- # anything to be updated, so extra rebuilds of the project won't occur.
- # Make sure to pass through CMAKE_MAKE_PROGRAM in case the main project
- # has this set to something not findable on the PATH.
- configure_file("${_DownloadProjectDir}/DownloadProject.CMakeLists.cmake.in"
- "${DL_ARGS_DOWNLOAD_DIR}/CMakeLists.txt")
- execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}"
- -D "CMAKE_MAKE_PROGRAM:FILE=${CMAKE_MAKE_PROGRAM}"
- .
- RESULT_VARIABLE result
- ${OUTPUT_QUIET}
- WORKING_DIRECTORY "${DL_ARGS_DOWNLOAD_DIR}"
- )
- if(result)
- message(FATAL_ERROR "CMake step for ${DL_ARGS_PROJ} failed: ${result}")
- endif()
- execute_process(COMMAND ${CMAKE_COMMAND} --build .
- RESULT_VARIABLE result
- ${OUTPUT_QUIET}
- WORKING_DIRECTORY "${DL_ARGS_DOWNLOAD_DIR}"
- )
- if(result)
- message(FATAL_ERROR "Build step for ${DL_ARGS_PROJ} failed: ${result}")
- endif()
-
-endfunction() \ No newline at end of file
diff --git a/src/libnest2d/cmake_modules/FindClipper.cmake b/src/libnest2d/cmake_modules/FindClipper.cmake
deleted file mode 100644
index 01b6b99d5..000000000
--- a/src/libnest2d/cmake_modules/FindClipper.cmake
+++ /dev/null
@@ -1,58 +0,0 @@
-# Find Clipper library (http://www.angusj.com/delphi/clipper.php).
-# The following variables are set
-#
-# CLIPPER_FOUND
-# CLIPPER_INCLUDE_DIRS
-# CLIPPER_LIBRARIES
-#
-# It searches the environment variable $CLIPPER_PATH automatically.
-
-FIND_PATH(CLIPPER_INCLUDE_DIRS clipper.hpp
- $ENV{CLIPPER_PATH}
- $ENV{CLIPPER_PATH}/cpp/
- $ENV{CLIPPER_PATH}/include/
- $ENV{CLIPPER_PATH}/include/polyclipping/
- ${PROJECT_SOURCE_DIR}/python/pymesh/third_party/include/
- ${PROJECT_SOURCE_DIR}/python/pymesh/third_party/include/polyclipping/
- ${CMAKE_PREFIX_PATH}/include/polyclipping
- ${CMAKE_PREFIX_PATH}/include/
- /opt/local/include/
- /opt/local/include/polyclipping/
- /usr/local/include/
- /usr/local/include/polyclipping/
- /usr/include
- /usr/include/polyclipping/)
-
-FIND_LIBRARY(CLIPPER_LIBRARIES polyclipping
- $ENV{CLIPPER_PATH}
- $ENV{CLIPPER_PATH}/cpp/
- $ENV{CLIPPER_PATH}/cpp/build/
- $ENV{CLIPPER_PATH}/lib/
- $ENV{CLIPPER_PATH}/lib/polyclipping/
- ${PROJECT_SOURCE_DIR}/python/pymesh/third_party/lib/
- ${PROJECT_SOURCE_DIR}/python/pymesh/third_party/lib/polyclipping/
- ${CMAKE_PREFIX_PATH}/lib/
- ${CMAKE_PREFIX_PATH}/lib/polyclipping/
- /opt/local/lib/
- /opt/local/lib/polyclipping/
- /usr/local/lib/
- /usr/local/lib/polyclipping/
- /usr/lib/polyclipping)
-
-include(FindPackageHandleStandardArgs)
-FIND_PACKAGE_HANDLE_STANDARD_ARGS(Clipper
- "Clipper library cannot be found. Consider set CLIPPER_PATH environment variable"
- CLIPPER_INCLUDE_DIRS
- CLIPPER_LIBRARIES)
-
-MARK_AS_ADVANCED(
- CLIPPER_INCLUDE_DIRS
- CLIPPER_LIBRARIES)
-
-if(CLIPPER_FOUND)
- add_library(Clipper::Clipper INTERFACE IMPORTED)
- set_target_properties(Clipper::Clipper PROPERTIES INTERFACE_LINK_LIBRARIES ${CLIPPER_LIBRARIES})
- set_target_properties(Clipper::Clipper PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${CLIPPER_INCLUDE_DIRS})
- #target_link_libraries(Clipper::Clipper INTERFACE ${CLIPPER_LIBRARIES})
- #target_include_directories(Clipper::Clipper INTERFACE ${CLIPPER_INCLUDE_DIRS})
-endif()
diff --git a/src/libnest2d/include/libnest2d.h b/src/libnest2d/include/libnest2d.h
deleted file mode 100644
index 76b133f4b..000000000
--- a/src/libnest2d/include/libnest2d.h
+++ /dev/null
@@ -1,134 +0,0 @@
-#ifndef LIBNEST2D_H
-#define LIBNEST2D_H
-
-// The type of backend should be set conditionally by the cmake configuriation
-// for now we set it statically to clipper backend
-#ifdef LIBNEST2D_BACKEND_CLIPPER
-#include <libnest2d/backends/clipper/geometries.hpp>
-#endif
-
-#ifdef LIBNEST2D_OPTIMIZER_NLOPT
-// We include the stock optimizers for local and global optimization
-#include <libnest2d/optimizers/nlopt/subplex.hpp> // Local subplex for NfpPlacer
-#include <libnest2d/optimizers/nlopt/genetic.hpp> // Genetic for min. bounding box
-#endif
-
-#include <libnest2d/libnest2d.hpp>
-#include <libnest2d/placers/bottomleftplacer.hpp>
-#include <libnest2d/placers/nfpplacer.hpp>
-#include <libnest2d/selections/firstfit.hpp>
-#include <libnest2d/selections/filler.hpp>
-#include <libnest2d/selections/djd_heuristic.hpp>
-
-namespace libnest2d {
-
-using Point = PointImpl;
-using Coord = TCoord<PointImpl>;
-using Box = _Box<PointImpl>;
-using Segment = _Segment<PointImpl>;
-using Circle = _Circle<PointImpl>;
-
-using Item = _Item<PolygonImpl>;
-using Rectangle = _Rectangle<PolygonImpl>;
-using PackGroup = _PackGroup<PolygonImpl>;
-
-using FillerSelection = selections::_FillerSelection<PolygonImpl>;
-using FirstFitSelection = selections::_FirstFitSelection<PolygonImpl>;
-using DJDHeuristic = selections::_DJDHeuristic<PolygonImpl>;
-
-template<class Bin> // Generic placer for arbitrary bin types
-using _NfpPlacer = placers::_NofitPolyPlacer<PolygonImpl, Bin>;
-
-// NfpPlacer is with Box bin
-using NfpPlacer = _NfpPlacer<Box>;
-
-// This supports only box shaped bins
-using BottomLeftPlacer = placers::_BottomLeftPlacer<PolygonImpl>;
-
-#ifdef LIBNEST2D_STATIC
-
-extern template class Nester<NfpPlacer, FirstFitSelection>;
-extern template class Nester<BottomLeftPlacer, FirstFitSelection>;
-extern template std::size_t Nester<NfpPlacer, FirstFitSelection>::execute(
- std::vector<Item>::iterator, std::vector<Item>::iterator);
-extern template std::size_t Nester<BottomLeftPlacer, FirstFitSelection>::execute(
- std::vector<Item>::iterator, std::vector<Item>::iterator);
-
-#endif
-
-template<class Placer = NfpPlacer, class Selector = FirstFitSelection>
-struct NestConfig {
- typename Placer::Config placer_config;
- typename Selector::Config selector_config;
- using Placement = typename Placer::Config;
- using Selection = typename Selector::Config;
-
- NestConfig() = default;
- NestConfig(const typename Placer::Config &cfg) : placer_config{cfg} {}
- NestConfig(const typename Selector::Config &cfg) : selector_config{cfg} {}
- NestConfig(const typename Placer::Config & pcfg,
- const typename Selector::Config &scfg)
- : placer_config{pcfg}, selector_config{scfg} {}
-};
-
-struct NestControl {
- ProgressFunction progressfn;
- StopCondition stopcond = []{ return false; };
-
- NestControl() = default;
- NestControl(ProgressFunction pr) : progressfn{std::move(pr)} {}
- NestControl(StopCondition sc) : stopcond{std::move(sc)} {}
- NestControl(ProgressFunction pr, StopCondition sc)
- : progressfn{std::move(pr)}, stopcond{std::move(sc)}
- {}
-};
-
-template<class Placer = NfpPlacer,
- class Selector = FirstFitSelection,
- class Iterator = std::vector<Item>::iterator>
-std::size_t nest(Iterator from, Iterator to,
- const typename Placer::BinType & bin,
- Coord dist = 0,
- const NestConfig<Placer, Selector> &cfg = {},
- NestControl ctl = {})
-{
- _Nester<Placer, Selector> nester{bin, dist, cfg.placer_config, cfg.selector_config};
- if(ctl.progressfn) nester.progressIndicator(ctl.progressfn);
- if(ctl.stopcond) nester.stopCondition(ctl.stopcond);
- return nester.execute(from, to);
-}
-
-#ifdef LIBNEST2D_STATIC
-
-extern template class Nester<NfpPlacer, FirstFitSelection>;
-extern template class Nester<BottomLeftPlacer, FirstFitSelection>;
-extern template std::size_t nest(std::vector<Item>::iterator from,
- std::vector<Item>::iterator from to,
- const Box & bin,
- Coord dist,
- const NestConfig<NfpPlacer, FirstFitSelection> &cfg,
- NestControl ctl);
-extern template std::size_t nest(std::vector<Item>::iterator from,
- std::vector<Item>::iterator from to,
- const Box & bin,
- Coord dist,
- const NestConfig<BottomLeftPlacer, FirstFitSelection> &cfg,
- NestControl ctl);
-
-#endif
-
-template<class Placer = NfpPlacer,
- class Selector = FirstFitSelection,
- class Container = std::vector<Item>>
-std::size_t nest(Container&& cont,
- const typename Placer::BinType & bin,
- Coord dist = 0,
- const NestConfig<Placer, Selector> &cfg = {},
- NestControl ctl = {})
-{
- return nest<Placer, Selector>(cont.begin(), cont.end(), bin, dist, cfg, ctl);
-}
-
-}
-
-#endif // LIBNEST2D_H
diff --git a/src/libnest2d/include/libnest2d/backends/clipper/CMakeLists.txt b/src/libnest2d/include/libnest2d/backends/clipper/CMakeLists.txt
deleted file mode 100644
index 202089356..000000000
--- a/src/libnest2d/include/libnest2d/backends/clipper/CMakeLists.txt
+++ /dev/null
@@ -1,73 +0,0 @@
-if(NOT TARGET clipper) # If there is a clipper target in the parent project we are good to go.
-
- find_package(Clipper 6.1)
-
- if(NOT CLIPPER_FOUND)
- find_package(Subversion QUIET)
- if(Subversion_FOUND)
-
- set(URL_CLIPPER "https://svn.code.sf.net/p/polyclipping/code/trunk/cpp"
- CACHE STRING "Clipper source code repository location.")
-
- message(STATUS "Clipper not found so it will be downloaded.")
- # Silently download and build the library in the build dir
-
- if (CMAKE_VERSION VERSION_LESS 3.2)
- set(UPDATE_DISCONNECTED_IF_AVAILABLE "")
- else()
- set(UPDATE_DISCONNECTED_IF_AVAILABLE "UPDATE_DISCONNECTED 1")
- endif()
-
- include(DownloadProject)
- download_project( PROJ clipper_library
- SVN_REPOSITORY ${URL_CLIPPER}
- SVN_REVISION -r540
- #SOURCE_SUBDIR cpp
- INSTALL_COMMAND ""
- CONFIGURE_COMMAND "" # Not working, I will just add the source files
- ${UPDATE_DISCONNECTED_IF_AVAILABLE}
- )
-
- # This is not working and I dont have time to fix it
- # add_subdirectory(${clipper_library_SOURCE_DIR}/cpp
- # ${clipper_library_BINARY_DIR}
- # )
-
- add_library(clipperBackend STATIC
- ${clipper_library_SOURCE_DIR}/clipper.cpp
- ${clipper_library_SOURCE_DIR}/clipper.hpp)
-
- target_include_directories(clipperBackend INTERFACE ${clipper_library_SOURCE_DIR})
- else()
- message(FATAL_ERROR "Can't find clipper library and no SVN client found to download.
- You can download the clipper sources and define a clipper target in your project, that will work for libnest2d.")
- endif()
- else()
- add_library(clipperBackend INTERFACE)
- target_link_libraries(clipperBackend INTERFACE Clipper::Clipper)
- endif()
-else()
- # set(CLIPPER_INCLUDE_DIRS "" PARENT_SCOPE)
- # set(CLIPPER_LIBRARIES clipper PARENT_SCOPE)
- add_library(clipperBackend INTERFACE)
- target_link_libraries(clipperBackend INTERFACE clipper)
-endif()
-
-# Clipper backend is not enough on its own, it still needs some functions
-# from Boost geometry
-if(NOT Boost_FOUND)
- find_package(Boost 1.58 REQUIRED)
- # TODO automatic download of boost geometry headers
-endif()
-
-target_link_libraries(clipperBackend INTERFACE Boost::boost )
-#target_sources(ClipperBackend INTERFACE
-# ${CMAKE_CURRENT_SOURCE_DIR}/geometries.hpp
-# ${CMAKE_CURRENT_SOURCE_DIR}/clipper_polygon.hpp
-# ${SRC_DIR}/libnest2d/utils/boost_alg.hpp )
-
-target_compile_definitions(clipperBackend INTERFACE LIBNEST2D_BACKEND_CLIPPER)
-
-# And finally plug the clipperBackend into libnest2d
-# target_link_libraries(libnest2d INTERFACE clipperBackend)
-
diff --git a/src/libnest2d/include/libnest2d/geometry_traits_nfp.hpp b/src/libnest2d/include/libnest2d/geometry_traits_nfp.hpp
index 4a2c69bca..29a1ccd04 100644
--- a/src/libnest2d/include/libnest2d/geometry_traits_nfp.hpp
+++ b/src/libnest2d/include/libnest2d/geometry_traits_nfp.hpp
@@ -299,9 +299,456 @@ inline NfpResult<RawShape> nfpConvexOnly(const RawShape& sh,
template<class RawShape>
NfpResult<RawShape> nfpSimpleSimple(const RawShape& cstationary,
- const RawShape& cother)
+ const RawShape& cother)
{
- return {};
+
+ // Algorithms are from the original algorithm proposed in paper:
+ // https://eprints.soton.ac.uk/36850/1/CORMSIS-05-05.pdf
+
+ // /////////////////////////////////////////////////////////////////////////
+ // Algorithm 1: Obtaining the minkowski sum
+ // /////////////////////////////////////////////////////////////////////////
+
+ // I guess this is not a full minkowski sum of the two input polygons by
+ // definition. This yields a subset that is compatible with the next 2
+ // algorithms.
+
+ using Result = NfpResult<RawShape>;
+ using Vertex = TPoint<RawShape>;
+ using Coord = TCoord<Vertex>;
+ using Edge = _Segment<Vertex>;
+ namespace sl = shapelike;
+ using std::signbit;
+ using std::sort;
+ using std::vector;
+ using std::ref;
+ using std::reference_wrapper;
+
+ // TODO The original algorithms expects the stationary polygon in
+ // counter clockwise and the orbiter in clockwise order.
+ // So for preventing any further complication, I will make the input
+ // the way it should be, than make my way around the orientations.
+
+ // Reverse the stationary contour to counter clockwise
+ auto stcont = sl::contour(cstationary);
+ {
+ std::reverse(sl::begin(stcont), sl::end(stcont));
+ stcont.pop_back();
+ auto it = std::min_element(sl::begin(stcont), sl::end(stcont),
+ [](const Vertex& v1, const Vertex& v2) {
+ return getY(v1) < getY(v2);
+ });
+ std::rotate(sl::begin(stcont), it, sl::end(stcont));
+ sl::addVertex(stcont, sl::front(stcont));
+ }
+ RawShape stationary;
+ sl::contour(stationary) = stcont;
+
+ // Reverse the orbiter contour to counter clockwise
+ auto orbcont = sl::contour(cother);
+ {
+ std::reverse(orbcont.begin(), orbcont.end());
+
+ // Step 1: Make the orbiter reverse oriented
+
+ orbcont.pop_back();
+ auto it = std::min_element(orbcont.begin(), orbcont.end(),
+ [](const Vertex& v1, const Vertex& v2) {
+ return getY(v1) < getY(v2);
+ });
+
+ std::rotate(orbcont.begin(), it, orbcont.end());
+ orbcont.emplace_back(orbcont.front());
+
+ for(auto &v : orbcont) v = -v;
+
+ }
+
+ // Copy the orbiter (contour only), we will have to work on it
+ RawShape orbiter;
+ sl::contour(orbiter) = orbcont;
+
+ // An edge with additional data for marking it
+ struct MarkedEdge {
+ Edge e; Radians turn_angle = 0; bool is_turning_point = false;
+ MarkedEdge() = default;
+ MarkedEdge(const Edge& ed, Radians ta, bool tp):
+ e(ed), turn_angle(ta), is_turning_point(tp) {}
+
+ // debug
+ std::string label;
+ };
+
+ // Container for marked edges
+ using EdgeList = vector<MarkedEdge>;
+
+ EdgeList A, B;
+
+ // This is how an edge list is created from the polygons
+ auto fillEdgeList = [](EdgeList& L, const RawShape& ppoly, int dir) {
+ auto& poly = sl::contour(ppoly);
+
+ L.reserve(sl::contourVertexCount(poly));
+
+ if(dir > 0) {
+ auto it = poly.begin();
+ auto nextit = std::next(it);
+
+ double turn_angle = 0;
+ bool is_turn_point = false;
+
+ while(nextit != poly.end()) {
+ L.emplace_back(Edge(*it, *nextit), turn_angle, is_turn_point);
+ it++; nextit++;
+ }
+ } else {
+ auto it = sl::rbegin(poly);
+ auto nextit = std::next(it);
+
+ double turn_angle = 0;
+ bool is_turn_point = false;
+
+ while(nextit != sl::rend(poly)) {
+ L.emplace_back(Edge(*it, *nextit), turn_angle, is_turn_point);
+ it++; nextit++;
+ }
+ }
+
+ auto getTurnAngle = [](const Edge& e1, const Edge& e2) {
+ auto phi = e1.angleToXaxis();
+ auto phi_prev = e2.angleToXaxis();
+ auto turn_angle = phi-phi_prev;
+ if(turn_angle > Pi) turn_angle -= TwoPi;
+ if(turn_angle < -Pi) turn_angle += TwoPi;
+ return turn_angle;
+ };
+
+ auto eit = L.begin();
+ auto enext = std::next(eit);
+
+ eit->turn_angle = getTurnAngle(L.front().e, L.back().e);
+
+ while(enext != L.end()) {
+ enext->turn_angle = getTurnAngle( enext->e, eit->e);
+ eit->is_turning_point =
+ signbit(enext->turn_angle) != signbit(eit->turn_angle);
+ ++eit; ++enext;
+ }
+
+ L.back().is_turning_point = signbit(L.back().turn_angle) !=
+ signbit(L.front().turn_angle);
+
+ };
+
+ // Step 2: Fill the edgelists
+ fillEdgeList(A, stationary, 1);
+ fillEdgeList(B, orbiter, 1);
+
+ int i = 1;
+ for(MarkedEdge& me : A) {
+ std::cout << "a" << i << ":\n\t"
+ << getX(me.e.first()) << " " << getY(me.e.first()) << "\n\t"
+ << getX(me.e.second()) << " " << getY(me.e.second()) << "\n\t"
+ << "Turning point: " << (me.is_turning_point ? "yes" : "no")
+ << std::endl;
+
+ me.label = "a"; me.label += std::to_string(i);
+ i++;
+ }
+
+ i = 1;
+ for(MarkedEdge& me : B) {
+ std::cout << "b" << i << ":\n\t"
+ << getX(me.e.first()) << " " << getY(me.e.first()) << "\n\t"
+ << getX(me.e.second()) << " " << getY(me.e.second()) << "\n\t"
+ << "Turning point: " << (me.is_turning_point ? "yes" : "no")
+ << std::endl;
+ me.label = "b"; me.label += std::to_string(i);
+ i++;
+ }
+
+ // A reference to a marked edge that also knows its container
+ struct MarkedEdgeRef {
+ reference_wrapper<MarkedEdge> eref;
+ reference_wrapper<vector<MarkedEdgeRef>> container;
+ Coord dir = 1; // Direction modifier
+
+ inline Radians angleX() const { return eref.get().e.angleToXaxis(); }
+ inline const Edge& edge() const { return eref.get().e; }
+ inline Edge& edge() { return eref.get().e; }
+ inline bool isTurningPoint() const {
+ return eref.get().is_turning_point;
+ }
+ inline bool isFrom(const vector<MarkedEdgeRef>& cont ) {
+ return &(container.get()) == &cont;
+ }
+ inline bool eq(const MarkedEdgeRef& mr) {
+ return &(eref.get()) == &(mr.eref.get());
+ }
+
+ MarkedEdgeRef(reference_wrapper<MarkedEdge> er,
+ reference_wrapper<vector<MarkedEdgeRef>> ec):
+ eref(er), container(ec), dir(1) {}
+
+ MarkedEdgeRef(reference_wrapper<MarkedEdge> er,
+ reference_wrapper<vector<MarkedEdgeRef>> ec,
+ Coord d):
+ eref(er), container(ec), dir(d) {}
+ };
+
+ using EdgeRefList = vector<MarkedEdgeRef>;
+
+ // Comparing two marked edges
+ auto sortfn = [](const MarkedEdgeRef& e1, const MarkedEdgeRef& e2) {
+ return e1.angleX() < e2.angleX();
+ };
+
+ EdgeRefList Aref, Bref; // We create containers for the references
+ Aref.reserve(A.size()); Bref.reserve(B.size());
+
+ // Fill reference container for the stationary polygon
+ std::for_each(A.begin(), A.end(), [&Aref](MarkedEdge& me) {
+ Aref.emplace_back( ref(me), ref(Aref) );
+ });
+
+ // Fill reference container for the orbiting polygon
+ std::for_each(B.begin(), B.end(), [&Bref](MarkedEdge& me) {
+ Bref.emplace_back( ref(me), ref(Bref) );
+ });
+
+ auto mink = [sortfn] // the Mink(Q, R, direction) sub-procedure
+ (const EdgeRefList& Q, const EdgeRefList& R, bool positive)
+ {
+
+ // Step 1 "merge sort_list(Q) and sort_list(R) to form merge_list(Q,R)"
+ // Sort the containers of edge references and merge them.
+ // Q could be sorted only once and be reused here but we would still
+ // need to merge it with sorted(R).
+
+ EdgeRefList merged;
+ EdgeRefList S, seq;
+ merged.reserve(Q.size() + R.size());
+
+ merged.insert(merged.end(), R.begin(), R.end());
+ std::stable_sort(merged.begin(), merged.end(), sortfn);
+ merged.insert(merged.end(), Q.begin(), Q.end());
+ std::stable_sort(merged.begin(), merged.end(), sortfn);
+
+ // Step 2 "set i = 1, k = 1, direction = 1, s1 = q1"
+ // we don't use i, instead, q is an iterator into Q. k would be an index
+ // into the merged sequence but we use "it" as an iterator for that
+
+ // here we obtain references for the containers for later comparisons
+ const auto& Rcont = R.begin()->container.get();
+ const auto& Qcont = Q.begin()->container.get();
+
+ // Set the initial direction
+ Coord dir = 1;
+
+ // roughly i = 1 (so q = Q.begin()) and s1 = q1 so S[0] = q;
+ if(positive) {
+ auto q = Q.begin();
+ S.emplace_back(*q);
+
+ // Roughly step 3
+
+ std::cout << "merged size: " << merged.size() << std::endl;
+ auto mit = merged.begin();
+ for(bool finish = false; !finish && q != Q.end();) {
+ ++q; // "Set i = i + 1"
+
+ while(!finish && mit != merged.end()) {
+ if(mit->isFrom(Rcont)) {
+ auto s = *mit;
+ s.dir = dir;
+ S.emplace_back(s);
+ }
+
+ if(mit->eq(*q)) {
+ S.emplace_back(*q);
+ if(mit->isTurningPoint()) dir = -dir;
+ if(q == Q.begin()) finish = true;
+ break;
+ }
+
+ mit += dir;
+ // __nfp::advance(mit, merged, dir > 0);
+ }
+ }
+ } else {
+ auto q = Q.rbegin();
+ S.emplace_back(*q);
+
+ // Roughly step 3
+
+ std::cout << "merged size: " << merged.size() << std::endl;
+ auto mit = merged.begin();
+ for(bool finish = false; !finish && q != Q.rend();) {
+ ++q; // "Set i = i + 1"
+
+ while(!finish && mit != merged.end()) {
+ if(mit->isFrom(Rcont)) {
+ auto s = *mit;
+ s.dir = dir;
+ S.emplace_back(s);
+ }
+
+ if(mit->eq(*q)) {
+ S.emplace_back(*q);
+ S.back().dir = -1;
+ if(mit->isTurningPoint()) dir = -dir;
+ if(q == Q.rbegin()) finish = true;
+ break;
+ }
+
+ mit += dir;
+ // __nfp::advance(mit, merged, dir > 0);
+ }
+ }
+ }
+
+
+ // Step 4:
+
+ // "Let starting edge r1 be in position si in sequence"
+ // whaaat? I guess this means the following:
+ auto it = S.begin();
+ while(!it->eq(*R.begin())) ++it;
+
+ // "Set j = 1, next = 2, direction = 1, seq1 = si"
+ // we don't use j, seq is expanded dynamically.
+ dir = 1;
+ auto next = std::next(R.begin()); seq.emplace_back(*it);
+
+ // Step 5:
+ // "If all si edges have been allocated to seqj" should mean that
+ // we loop until seq has equal size with S
+ auto send = it; //it == S.begin() ? it : std::prev(it);
+ while(it != S.end()) {
+ ++it; if(it == S.end()) it = S.begin();
+ if(it == send) break;
+
+ if(it->isFrom(Qcont)) {
+ seq.emplace_back(*it); // "If si is from Q, j = j + 1, seqj = si"
+
+ // "If si is a turning point in Q,
+ // direction = - direction, next = next + direction"
+ if(it->isTurningPoint()) {
+ dir = -dir;
+ next += dir;
+// __nfp::advance(next, R, dir > 0);
+ }
+ }
+
+ if(it->eq(*next) /*&& dir == next->dir*/) { // "If si = direction.rnext"
+ // "j = j + 1, seqj = si, next = next + direction"
+ seq.emplace_back(*it);
+ next += dir;
+// __nfp::advance(next, R, dir > 0);
+ }
+ }
+
+ return seq;
+ };
+
+ std::vector<EdgeRefList> seqlist;
+ seqlist.reserve(Bref.size());
+
+ EdgeRefList Bslope = Bref; // copy Bref, we will make a slope diagram
+
+ // make the slope diagram of B
+ std::sort(Bslope.begin(), Bslope.end(), sortfn);
+
+ auto slopeit = Bslope.begin(); // search for the first turning point
+ while(!slopeit->isTurningPoint() && slopeit != Bslope.end()) slopeit++;
+
+ if(slopeit == Bslope.end()) {
+ // no turning point means convex polygon.
+ seqlist.emplace_back(mink(Aref, Bref, true));
+ } else {
+ int dir = 1;
+
+ auto firstturn = Bref.begin();
+ while(!firstturn->eq(*slopeit)) ++firstturn;
+
+ assert(firstturn != Bref.end());
+
+ EdgeRefList bgroup; bgroup.reserve(Bref.size());
+ bgroup.emplace_back(*slopeit);
+
+ auto b_it = std::next(firstturn);
+ while(b_it != firstturn) {
+ if(b_it == Bref.end()) b_it = Bref.begin();
+
+ while(!slopeit->eq(*b_it)) {
+ __nfp::advance(slopeit, Bslope, dir > 0);
+ }
+
+ if(!slopeit->isTurningPoint()) {
+ bgroup.emplace_back(*slopeit);
+ } else {
+ if(!bgroup.empty()) {
+ if(dir > 0) bgroup.emplace_back(*slopeit);
+ for(auto& me : bgroup) {
+ std::cout << me.eref.get().label << ", ";
+ }
+ std::cout << std::endl;
+ seqlist.emplace_back(mink(Aref, bgroup, dir == 1 ? true : false));
+ bgroup.clear();
+ if(dir < 0) bgroup.emplace_back(*slopeit);
+ } else {
+ bgroup.emplace_back(*slopeit);
+ }
+
+ dir *= -1;
+ }
+ ++b_it;
+ }
+ }
+
+// while(it != Bref.end()) // This is step 3 and step 4 in one loop
+// if(it->isTurningPoint()) {
+// R = {R.last, it++};
+// auto seq = mink(Q, R, orientation);
+
+// // TODO step 6 (should be 5 shouldn't it?): linking edges from A
+// // I don't get this step
+
+// seqlist.insert(seqlist.end(), seq.begin(), seq.end());
+// orientation = !orientation;
+// } else ++it;
+
+// if(seqlist.empty()) seqlist = mink(Q, {Bref.begin(), Bref.end()}, true);
+
+ // /////////////////////////////////////////////////////////////////////////
+ // Algorithm 2: breaking Minkowski sums into track line trips
+ // /////////////////////////////////////////////////////////////////////////
+
+
+ // /////////////////////////////////////////////////////////////////////////
+ // Algorithm 3: finding the boundary of the NFP from track line trips
+ // /////////////////////////////////////////////////////////////////////////
+
+
+ for(auto& seq : seqlist) {
+ std::cout << "seqlist size: " << seq.size() << std::endl;
+ for(auto& s : seq) {
+ std::cout << (s.dir > 0 ? "" : "-") << s.eref.get().label << ", ";
+ }
+ std::cout << std::endl;
+ }
+
+ auto& seq = seqlist.front();
+ RawShape rsh;
+ Vertex top_nfp;
+ std::vector<Edge> edgelist; edgelist.reserve(seq.size());
+ for(auto& s : seq) {
+ edgelist.emplace_back(s.eref.get().e);
+ }
+
+ __nfp::buildPolygon(edgelist, rsh, top_nfp);
+
+ return Result(rsh, top_nfp);
}
// Specializable NFP implementation class. Specialize it if you have a faster
diff --git a/src/libnest2d/include/libnest2d/libnest2d.hpp b/src/libnest2d/include/libnest2d/libnest2d.hpp
index 91c98c62a..b6d7fcdcf 100644
--- a/src/libnest2d/include/libnest2d/libnest2d.hpp
+++ b/src/libnest2d/include/libnest2d/libnest2d.hpp
@@ -1,869 +1,134 @@
#ifndef LIBNEST2D_HPP
#define LIBNEST2D_HPP
-#include <memory>
-#include <vector>
-#include <map>
-#include <array>
-#include <algorithm>
-#include <functional>
-
-#include <libnest2d/geometry_traits.hpp>
+// The type of backend should be set conditionally by the cmake configuriation
+// for now we set it statically to clipper backend
+#ifdef LIBNEST2D_GEOMETRIES_clipper
+#include <libnest2d/backends/clipper/geometries.hpp>
+#endif
+
+#ifdef LIBNEST2D_OPTIMIZER_nlopt
+// We include the stock optimizers for local and global optimization
+#include <libnest2d/optimizers/nlopt/subplex.hpp> // Local subplex for NfpPlacer
+#include <libnest2d/optimizers/nlopt/genetic.hpp> // Genetic for min. bounding box
+#endif
+
+#include <libnest2d/nester.hpp>
+#include <libnest2d/placers/bottomleftplacer.hpp>
+#include <libnest2d/placers/nfpplacer.hpp>
+#include <libnest2d/selections/firstfit.hpp>
+#include <libnest2d/selections/filler.hpp>
+#include <libnest2d/selections/djd_heuristic.hpp>
namespace libnest2d {
-static const constexpr int BIN_ID_UNSET = -1;
-
-/**
- * \brief An item to be placed on a bin.
- *
- * It holds a copy of the original shape object but supports move construction
- * from the shape objects if its an rvalue reference. This way we can construct
- * the items without the cost of copying a potentially large amount of input.
- *
- * The results of some calculations are cached for maintaining fast run times.
- * For this reason, memory demands are much higher but this should pay off.
- */
-template<class RawShape>
-class _Item {
- using Coord = TCoord<TPoint<RawShape>>;
- using Vertex = TPoint<RawShape>;
- using Box = _Box<Vertex>;
-
- using VertexConstIterator = typename TContour<RawShape>::const_iterator;
-
- // The original shape that gets encapsulated.
- RawShape sh_;
-
- // Transformation data
- Vertex translation_{0, 0};
- Radians rotation_{0.0};
- Coord inflation_{0};
-
- // Info about whether the transformations will have to take place
- // This is needed because if floating point is used, it is hard to say
- // that a zero angle is not a rotation because of testing for equality.
- bool has_rotation_ = false, has_translation_ = false, has_inflation_ = false;
-
- // For caching the calculations as they can get pretty expensive.
- mutable RawShape tr_cache_;
- mutable bool tr_cache_valid_ = false;
- mutable double area_cache_ = 0;
- mutable bool area_cache_valid_ = false;
- mutable RawShape inflate_cache_;
- mutable bool inflate_cache_valid_ = false;
-
- enum class Convexity: char {
- UNCHECKED,
- C_TRUE,
- C_FALSE
- };
-
- mutable Convexity convexity_ = Convexity::UNCHECKED;
- mutable VertexConstIterator rmt_; // rightmost top vertex
- mutable VertexConstIterator lmb_; // leftmost bottom vertex
- mutable bool rmt_valid_ = false, lmb_valid_ = false;
- mutable struct BBCache {
- Box bb; bool valid;
- BBCache(): valid(false) {}
- } bb_cache_;
-
- int binid_{BIN_ID_UNSET}, priority_{0};
- bool fixed_{false};
-
-public:
-
- /// The type of the shape which was handed over as the template argument.
- using ShapeType = RawShape;
-
- /**
- * \brief Iterator type for the outer vertices.
- *
- * Only const iterators can be used. The _Item type is not intended to
- * modify the carried shapes from the outside. The main purpose of this type
- * is to cache the calculation results from the various operators it
- * supports. Giving out a non const iterator would make it impossible to
- * perform correct cache invalidation.
- */
- using Iterator = VertexConstIterator;
-
- /**
- * @brief Get the orientation of the polygon.
- *
- * The orientation have to be specified as a specialization of the
- * OrientationType struct which has a Value constant.
- *
- * @return The orientation type identifier for the _Item type.
- */
- static BP2D_CONSTEXPR Orientation orientation() {
- return OrientationType<RawShape>::Value;
- }
-
- /**
- * @brief Constructing an _Item form an existing raw shape. The shape will
- * be copied into the _Item object.
- * @param sh The original shape object.
- */
- explicit inline _Item(const RawShape& sh): sh_(sh) {}
-
- /**
- * @brief Construction of an item by moving the content of the raw shape,
- * assuming that it supports move semantics.
- * @param sh The original shape object.
- */
- explicit inline _Item(RawShape&& sh): sh_(std::move(sh)) {}
-
- /**
- * @brief Create an item from an initializer list.
- * @param il The initializer list of vertices.
- */
- inline _Item(const std::initializer_list< Vertex >& il):
- sh_(sl::create<RawShape>(il)) {}
-
- inline _Item(const TContour<RawShape>& contour,
- const THolesContainer<RawShape>& holes = {}):
- sh_(sl::create<RawShape>(contour, holes)) {}
-
- inline _Item(TContour<RawShape>&& contour,
- THolesContainer<RawShape>&& holes):
- sh_(sl::create<RawShape>(std::move(contour), std::move(holes))) {}
-
- inline bool isFixed() const noexcept { return fixed_; }
- inline void markAsFixedInBin(int binid)
- {
- fixed_ = binid >= 0;
- binid_ = binid;
- }
-
- inline void binId(int idx) { binid_ = idx; }
- inline int binId() const noexcept { return binid_; }
-
- inline void priority(int p) { priority_ = p; }
- inline int priority() const noexcept { return priority_; }
-
- /**
- * @brief Convert the polygon to string representation. The format depends
- * on the implementation of the polygon.
- * @return
- */
- inline std::string toString() const
- {
- return sl::toString(sh_);
- }
-
- /// Iterator tho the first contour vertex in the polygon.
- inline Iterator begin() const
- {
- return sl::cbegin(sh_);
- }
-
- /// Alias to begin()
- inline Iterator cbegin() const
- {
- return sl::cbegin(sh_);
- }
-
- /// Iterator to the last contour vertex.
- inline Iterator end() const
- {
- return sl::cend(sh_);
- }
-
- /// Alias to end()
- inline Iterator cend() const
- {
- return sl::cend(sh_);
- }
-
- /**
- * @brief Get a copy of an outer vertex within the carried shape.
- *
- * Note that the vertex considered here is taken from the original shape
- * that this item is constructed from. This means that no transformation is
- * applied to the shape in this call.
- *
- * @param idx The index of the requested vertex.
- * @return A copy of the requested vertex.
- */
- inline Vertex vertex(unsigned long idx) const
- {
- return sl::vertex(sh_, idx);
- }
-
- /**
- * @brief Modify a vertex.
- *
- * Note that this method will invalidate every cached calculation result
- * including polygon offset and transformations.
- *
- * @param idx The index of the requested vertex.
- * @param v The new vertex data.
- */
- inline void setVertex(unsigned long idx, const Vertex& v )
- {
- invalidateCache();
- sl::vertex(sh_, idx) = v;
- }
-
- /**
- * @brief Calculate the shape area.
- *
- * The method returns absolute value and does not reflect polygon
- * orientation. The result is cached, subsequent calls will have very little
- * cost.
- * @return The shape area in floating point double precision.
- */
- inline double area() const {
- double ret ;
- if(area_cache_valid_) ret = area_cache_;
- else {
- ret = sl::area(infaltedShape());
- area_cache_ = ret;
- area_cache_valid_ = true;
- }
- return ret;
- }
+using Point = PointImpl;
+using Coord = TCoord<PointImpl>;
+using Box = _Box<PointImpl>;
+using Segment = _Segment<PointImpl>;
+using Circle = _Circle<PointImpl>;
- inline bool isContourConvex() const {
- bool ret = false;
+using Item = _Item<PolygonImpl>;
+using Rectangle = _Rectangle<PolygonImpl>;
+using PackGroup = _PackGroup<PolygonImpl>;
- switch(convexity_) {
- case Convexity::UNCHECKED:
- ret = sl::isConvex(sl::contour(transformedShape()));
- convexity_ = ret? Convexity::C_TRUE : Convexity::C_FALSE;
- break;
- case Convexity::C_TRUE: ret = true; break;
- case Convexity::C_FALSE:;
- }
+using FillerSelection = selections::_FillerSelection<PolygonImpl>;
+using FirstFitSelection = selections::_FirstFitSelection<PolygonImpl>;
+using DJDHeuristic = selections::_DJDHeuristic<PolygonImpl>;
- return ret;
- }
+template<class Bin> // Generic placer for arbitrary bin types
+using _NfpPlacer = placers::_NofitPolyPlacer<PolygonImpl, Bin>;
- inline bool isHoleConvex(unsigned /*holeidx*/) const {
- return false;
- }
+// NfpPlacer is with Box bin
+using NfpPlacer = _NfpPlacer<Box>;
- inline bool areHolesConvex() const {
- return false;
- }
+// This supports only box shaped bins
+using BottomLeftPlacer = placers::_BottomLeftPlacer<PolygonImpl>;
- /// The number of the outer ring vertices.
- inline size_t vertexCount() const {
- return sl::contourVertexCount(sh_);
- }
+#ifdef LIBNEST2D_STATIC
- inline size_t holeCount() const {
- return sl::holeCount(sh_);
- }
+extern template class _Nester<NfpPlacer, FirstFitSelection>;
+extern template class _Nester<BottomLeftPlacer, FirstFitSelection>;
+extern template std::size_t _Nester<NfpPlacer, FirstFitSelection>::execute(
+ std::vector<Item>::iterator, std::vector<Item>::iterator);
+extern template std::size_t _Nester<BottomLeftPlacer, FirstFitSelection>::execute(
+ std::vector<Item>::iterator, std::vector<Item>::iterator);
- /**
- * @brief isPointInside
- * @param p
- * @return
- */
- inline bool isInside(const Vertex& p) const
- {
- return sl::isInside(p, transformedShape());
- }
+#endif
- inline bool isInside(const _Item& sh) const
- {
- return sl::isInside(transformedShape(), sh.transformedShape());
- }
-
- inline bool isInside(const RawShape& sh) const
- {
- return sl::isInside(transformedShape(), sh);
- }
-
- inline bool isInside(const _Box<TPoint<RawShape>>& box) const;
- inline bool isInside(const _Circle<TPoint<RawShape>>& box) const;
-
- inline void translate(const Vertex& d) BP2D_NOEXCEPT
- {
- translation(translation() + d);
- }
-
- inline void rotate(const Radians& rads) BP2D_NOEXCEPT
- {
- rotation(rotation() + rads);
- }
+template<class Placer = NfpPlacer, class Selector = FirstFitSelection>
+struct NestConfig {
+ typename Placer::Config placer_config;
+ typename Selector::Config selector_config;
+ using Placement = typename Placer::Config;
+ using Selection = typename Selector::Config;
- inline void inflation(Coord distance) BP2D_NOEXCEPT
- {
- inflation_ = distance;
- has_inflation_ = true;
- invalidateCache();
- }
-
- inline Coord inflation() const BP2D_NOEXCEPT {
- return inflation_;
- }
-
- inline void inflate(Coord distance) BP2D_NOEXCEPT
- {
- inflation(inflation() + distance);
- }
-
- inline Radians rotation() const BP2D_NOEXCEPT
- {
- return rotation_;
- }
-
- inline TPoint<RawShape> translation() const BP2D_NOEXCEPT
- {
- return translation_;
- }
-
- inline void rotation(Radians rot) BP2D_NOEXCEPT
- {
- if(rotation_ != rot) {
- rotation_ = rot; has_rotation_ = true; tr_cache_valid_ = false;
- rmt_valid_ = false; lmb_valid_ = false;
- bb_cache_.valid = false;
- }
- }
-
- inline void translation(const TPoint<RawShape>& tr) BP2D_NOEXCEPT
- {
- if(translation_ != tr) {
- translation_ = tr; has_translation_ = true; tr_cache_valid_ = false;
- //bb_cache_.valid = false;
- }
- }
-
- inline const RawShape& transformedShape() const
- {
- if(tr_cache_valid_) return tr_cache_;
-
- RawShape cpy = infaltedShape();
- if(has_rotation_) sl::rotate(cpy, rotation_);
- if(has_translation_) sl::translate(cpy, translation_);
- tr_cache_ = cpy; tr_cache_valid_ = true;
- rmt_valid_ = false; lmb_valid_ = false;
-
- return tr_cache_;
- }
-
- inline operator RawShape() const
- {
- return transformedShape();
- }
-
- inline const RawShape& rawShape() const BP2D_NOEXCEPT
- {
- return sh_;
- }
-
- inline void resetTransformation() BP2D_NOEXCEPT
- {
- has_translation_ = false; has_rotation_ = false; has_inflation_ = false;
- invalidateCache();
- }
-
- inline Box boundingBox() const {
- if(!bb_cache_.valid) {
- if(!has_rotation_)
- bb_cache_.bb = sl::boundingBox(infaltedShape());
- else {
- // TODO make sure this works
- auto rotsh = infaltedShape();
- sl::rotate(rotsh, rotation_);
- bb_cache_.bb = sl::boundingBox(rotsh);
- }
- bb_cache_.valid = true;
- }
-
- auto &bb = bb_cache_.bb; auto &tr = translation_;
- return {bb.minCorner() + tr, bb.maxCorner() + tr };
- }
-
- inline Vertex referenceVertex() const {
- return rightmostTopVertex();
- }
-
- inline Vertex rightmostTopVertex() const {
- if(!rmt_valid_ || !tr_cache_valid_) { // find max x and max y vertex
- auto& tsh = transformedShape();
- rmt_ = std::max_element(sl::cbegin(tsh), sl::cend(tsh), vsort);
- rmt_valid_ = true;
- }
- return *rmt_;
- }
-
- inline Vertex leftmostBottomVertex() const {
- if(!lmb_valid_ || !tr_cache_valid_) { // find min x and min y vertex
- auto& tsh = transformedShape();
- lmb_ = std::min_element(sl::cbegin(tsh), sl::cend(tsh), vsort);
- lmb_valid_ = true;
- }
- return *lmb_;
- }
-
- //Static methods:
-
- inline static bool intersects(const _Item& sh1, const _Item& sh2)
- {
- return sl::intersects(sh1.transformedShape(),
- sh2.transformedShape());
- }
-
- inline static bool touches(const _Item& sh1, const _Item& sh2)
- {
- return sl::touches(sh1.transformedShape(),
- sh2.transformedShape());
- }
-
-private:
-
- inline const RawShape& infaltedShape() const {
- if(has_inflation_ ) {
- if(inflate_cache_valid_) return inflate_cache_;
-
- inflate_cache_ = sh_;
- sl::offset(inflate_cache_, inflation_);
- inflate_cache_valid_ = true;
- return inflate_cache_;
- }
- return sh_;
- }
-
- inline void invalidateCache() const BP2D_NOEXCEPT
- {
- tr_cache_valid_ = false;
- lmb_valid_ = false; rmt_valid_ = false;
- area_cache_valid_ = false;
- inflate_cache_valid_ = false;
- bb_cache_.valid = false;
- convexity_ = Convexity::UNCHECKED;
- }
-
- static inline bool vsort(const Vertex& v1, const Vertex& v2)
- {
- TCompute<Vertex> x1 = getX(v1), x2 = getX(v2);
- TCompute<Vertex> y1 = getY(v1), y2 = getY(v2);
- return y1 == y2 ? x1 < x2 : y1 < y2;
- }
+ NestConfig() = default;
+ NestConfig(const typename Placer::Config &cfg) : placer_config{cfg} {}
+ NestConfig(const typename Selector::Config &cfg) : selector_config{cfg} {}
+ NestConfig(const typename Placer::Config & pcfg,
+ const typename Selector::Config &scfg)
+ : placer_config{pcfg}, selector_config{scfg} {}
};
-/**
- * \brief Subclass of _Item for regular rectangle items.
- */
-template<class RawShape>
-class _Rectangle: public _Item<RawShape> {
- using _Item<RawShape>::vertex;
- using TO = Orientation;
-public:
-
- using Unit = TCoord<TPoint<RawShape>>;
-
- template<TO o = OrientationType<RawShape>::Value>
- inline _Rectangle(Unit width, Unit height,
- // disable this ctor if o != CLOCKWISE
- enable_if_t< o == TO::CLOCKWISE, int> = 0 ):
- _Item<RawShape>( sl::create<RawShape>( {
- {0, 0},
- {0, height},
- {width, height},
- {width, 0},
- {0, 0}
- } ))
- {
- }
-
- template<TO o = OrientationType<RawShape>::Value>
- inline _Rectangle(Unit width, Unit height,
- // disable this ctor if o != COUNTER_CLOCKWISE
- enable_if_t< o == TO::COUNTER_CLOCKWISE, int> = 0 ):
- _Item<RawShape>( sl::create<RawShape>( {
- {0, 0},
- {width, 0},
- {width, height},
- {0, height},
- {0, 0}
- } ))
- {
- }
-
- inline Unit width() const BP2D_NOEXCEPT {
- return getX(vertex(2));
- }
-
- inline Unit height() const BP2D_NOEXCEPT {
- return getY(vertex(2));
- }
+struct NestControl {
+ ProgressFunction progressfn;
+ StopCondition stopcond = []{ return false; };
+
+ NestControl() = default;
+ NestControl(ProgressFunction pr) : progressfn{std::move(pr)} {}
+ NestControl(StopCondition sc) : stopcond{std::move(sc)} {}
+ NestControl(ProgressFunction pr, StopCondition sc)
+ : progressfn{std::move(pr)}, stopcond{std::move(sc)}
+ {}
};
-template<class RawShape>
-inline bool _Item<RawShape>::isInside(const _Box<TPoint<RawShape>>& box) const {
- return sl::isInside(boundingBox(), box);
+template<class Placer = NfpPlacer,
+ class Selector = FirstFitSelection,
+ class Iterator = std::vector<Item>::iterator>
+std::size_t nest(Iterator from, Iterator to,
+ const typename Placer::BinType & bin,
+ Coord dist = 0,
+ const NestConfig<Placer, Selector> &cfg = {},
+ NestControl ctl = {})
+{
+ _Nester<Placer, Selector> nester{bin, dist, cfg.placer_config, cfg.selector_config};
+ if(ctl.progressfn) nester.progressIndicator(ctl.progressfn);
+ if(ctl.stopcond) nester.stopCondition(ctl.stopcond);
+ return nester.execute(from, to);
}
-template<class RawShape> inline bool
-_Item<RawShape>::isInside(const _Circle<TPoint<RawShape>>& circ) const {
- return sl::isInside(transformedShape(), circ);
+#ifdef LIBNEST2D_STATIC
+
+extern template class _Nester<NfpPlacer, FirstFitSelection>;
+extern template class _Nester<BottomLeftPlacer, FirstFitSelection>;
+extern template std::size_t nest(std::vector<Item>::iterator from,
+ std::vector<Item>::iterator to,
+ const Box & bin,
+ Coord dist,
+ const NestConfig<NfpPlacer, FirstFitSelection> &cfg,
+ NestControl ctl);
+extern template std::size_t nest(std::vector<Item>::iterator from,
+ std::vector<Item>::iterator to,
+ const Box & bin,
+ Coord dist,
+ const NestConfig<BottomLeftPlacer, FirstFitSelection> &cfg,
+ NestControl ctl);
+
+#endif
+
+template<class Placer = NfpPlacer,
+ class Selector = FirstFitSelection,
+ class Container = std::vector<Item>>
+std::size_t nest(Container&& cont,
+ const typename Placer::BinType & bin,
+ Coord dist = 0,
+ const NestConfig<Placer, Selector> &cfg = {},
+ NestControl ctl = {})
+{
+ return nest<Placer, Selector>(cont.begin(), cont.end(), bin, dist, cfg, ctl);
}
-template<class RawShape> using _ItemRef = std::reference_wrapper<_Item<RawShape>>;
-template<class RawShape> using _ItemGroup = std::vector<_ItemRef<RawShape>>;
-
-/**
- * \brief A list of packed item vectors. Each vector represents a bin.
- */
-template<class RawShape>
-using _PackGroup = std::vector<std::vector<_ItemRef<RawShape>>>;
-
-template<class Iterator>
-struct ConstItemRange {
- Iterator from;
- Iterator to;
- bool valid = false;
-
- ConstItemRange() = default;
- ConstItemRange(Iterator f, Iterator t): from(f), to(t), valid(true) {}
-};
-
-template<class Container>
-inline ConstItemRange<typename Container::const_iterator>
-rem(typename Container::const_iterator it, const Container& cont) {
- return {std::next(it), cont.end()};
-}
-
-/**
- * \brief A wrapper interface (trait) class for any placement strategy provider.
- *
- * If a client wants to use its own placement algorithm, all it has to do is to
- * specialize this class template and define all the ten methods it has. It can
- * use the strategies::PlacerBoilerplace class for creating a new placement
- * strategy where only the constructor and the trypack method has to be provided
- * and it will work out of the box.
- */
-template<class PlacementStrategy>
-class PlacementStrategyLike {
- PlacementStrategy impl_;
-public:
-
- using RawShape = typename PlacementStrategy::ShapeType;
-
- /// The item type that the placer works with.
- using Item = _Item<RawShape>;
-
- /// The placer's config type. Should be a simple struct but can be anything.
- using Config = typename PlacementStrategy::Config;
-
- /**
- * \brief The type of the bin that the placer works with.
- *
- * Can be a box or an arbitrary shape or just a width or height without a
- * second dimension if an infinite bin is considered.
- */
- using BinType = typename PlacementStrategy::BinType;
-
- /**
- * \brief Pack result that can be used to accept or discard it. See trypack
- * method.
- */
- using PackResult = typename PlacementStrategy::PackResult;
-
- using ItemGroup = _ItemGroup<RawShape>;
- using DefaultIterator = typename ItemGroup::const_iterator;
-
- /**
- * @brief Constructor taking the bin and an optional configuration.
- * @param bin The bin object whose type is defined by the placement strategy.
- * @param config The configuration for the particular placer.
- */
- explicit PlacementStrategyLike(const BinType& bin,
- const Config& config = Config()):
- impl_(bin)
- {
- configure(config);
- }
-
- /**
- * @brief Provide a different configuration for the placer.
- *
- * Note that it depends on the particular placer implementation how it
- * reacts to config changes in the middle of a calculation.
- *
- * @param config The configuration object defined by the placement strategy.
- */
- inline void configure(const Config& config) { impl_.configure(config); }
-
- /**
- * Try to pack an item with a result object that contains the packing
- * information for later accepting it.
- *
- * \param item_store A container of items that are intended to be packed
- * later. Can be used by the placer to switch tactics. When it's knows that
- * many items will come a greedy strategy may not be the best.
- * \param from The iterator to the item from which the packing should start,
- * including the pointed item
- * \param count How many items should be packed. If the value is 1, than
- * just the item pointed to by "from" argument should be packed.
- */
- template<class Iter = DefaultIterator>
- inline PackResult trypack(
- Item& item,
- const ConstItemRange<Iter>& remaining = ConstItemRange<Iter>())
- {
- return impl_.trypack(item, remaining);
- }
-
- /**
- * @brief A method to accept a previously tried item (or items).
- *
- * If the pack result is a failure the method should ignore it.
- * @param r The result of a previous trypack call.
- */
- inline void accept(PackResult& r) { impl_.accept(r); }
-
- /**
- * @brief pack Try to pack and immediately accept it on success.
- *
- * A default implementation would be to call
- * { auto&& r = trypack(...); accept(r); return r; } but we should let the
- * implementor of the placement strategy to harvest any optimizations from
- * the absence of an intermediate step. The above version can still be used
- * in the implementation.
- *
- * @param item The item to pack.
- * @return Returns true if the item was packed or false if it could not be
- * packed.
- */
- template<class Range = ConstItemRange<DefaultIterator>>
- inline bool pack(
- Item& item,
- const Range& remaining = Range())
- {
- return impl_.pack(item, remaining);
- }
-
- /**
- * This method makes possible to "preload" some items into the placer. It
- * will not move these items but will consider them as already packed.
- */
- inline void preload(const ItemGroup& packeditems)
- {
- impl_.preload(packeditems);
- }
-
- /// Unpack the last element (remove it from the list of packed items).
- inline void unpackLast() { impl_.unpackLast(); }
-
- /// Get the bin object.
- inline const BinType& bin() const { return impl_.bin(); }
-
- /// Set a new bin object.
- inline void bin(const BinType& bin) { impl_.bin(bin); }
-
- /// Get the packed items.
- inline ItemGroup getItems() { return impl_.getItems(); }
-
- /// Clear the packed items so a new session can be started.
- inline void clearItems() { impl_.clearItems(); }
-
- inline double filledArea() const { return impl_.filledArea(); }
-
-};
-
-// The progress function will be called with the number of placed items
-using ProgressFunction = std::function<void(unsigned)>;
-using StopCondition = std::function<bool(void)>;
-
-/**
- * A wrapper interface (trait) class for any selections strategy provider.
- */
-template<class SelectionStrategy>
-class SelectionStrategyLike {
- SelectionStrategy impl_;
-public:
- using RawShape = typename SelectionStrategy::ShapeType;
- using Item = _Item<RawShape>;
- using PackGroup = _PackGroup<RawShape>;
- using Config = typename SelectionStrategy::Config;
-
-
- /**
- * @brief Provide a different configuration for the selection strategy.
- *
- * Note that it depends on the particular placer implementation how it
- * reacts to config changes in the middle of a calculation.
- *
- * @param config The configuration object defined by the selection strategy.
- */
- inline void configure(const Config& config) {
- impl_.configure(config);
- }
-
- /**
- * @brief A function callback which should be called whenever an item or
- * a group of items where successfully packed.
- * @param fn A function callback object taking one unsigned integer as the
- * number of the remaining items to pack.
- */
- void progressIndicator(ProgressFunction fn) { impl_.progressIndicator(fn); }
-
- void stopCondition(StopCondition cond) { impl_.stopCondition(cond); }
-
- /**
- * \brief A method to start the calculation on the input sequence.
- *
- * \tparam TPlacer The only mandatory template parameter is the type of
- * placer compatible with the PlacementStrategyLike interface.
- *
- * \param first, last The first and last iterator if the input sequence. It
- * can be only an iterator of a type convertible to Item.
- * \param bin. The shape of the bin. It has to be supported by the placement
- * strategy.
- * \param An optional config object for the placer.
- */
- template<class TPlacer, class TIterator,
- class TBin = typename PlacementStrategyLike<TPlacer>::BinType,
- class PConfig = typename PlacementStrategyLike<TPlacer>::Config>
- inline void packItems(
- TIterator first,
- TIterator last,
- TBin&& bin,
- PConfig&& config = PConfig() )
- {
- impl_.template packItems<TPlacer>(first, last,
- std::forward<TBin>(bin),
- std::forward<PConfig>(config));
- }
-
- /**
- * @brief Get the items for a particular bin.
- * @param binIndex The index of the requested bin.
- * @return Returns a list of all items packed into the requested bin.
- */
- inline const PackGroup& getResult() const {
- return impl_.getResult();
- }
-
- void clear() { impl_.clear(); }
-};
-
-/**
- * The _Nester is the front-end class for the libnest2d library. It takes the
- * input items and changes their transformations to be inside the provided bin.
- */
-template<class PlacementStrategy, class SelectionStrategy >
-class _Nester {
- using TSel = SelectionStrategyLike<SelectionStrategy>;
- TSel selector_;
-
-public:
- using Item = typename PlacementStrategy::Item;
- using ShapeType = typename Item::ShapeType;
- using ItemRef = std::reference_wrapper<Item>;
- using TPlacer = PlacementStrategyLike<PlacementStrategy>;
- using BinType = typename TPlacer::BinType;
- using PlacementConfig = typename TPlacer::Config;
- using SelectionConfig = typename TSel::Config;
- using Coord = TCoord<TPoint<typename Item::ShapeType>>;
- using PackGroup = _PackGroup<typename Item::ShapeType>;
- using ResultType = PackGroup;
-
-private:
- BinType bin_;
- PlacementConfig pconfig_;
- Coord min_obj_distance_;
-
- using SItem = typename SelectionStrategy::Item;
- using TPItem = remove_cvref_t<Item>;
- using TSItem = remove_cvref_t<SItem>;
-
- StopCondition stopfn_;
-
- template<class It> using TVal = remove_ref_t<typename It::value_type>;
-
- template<class It, class Out>
- using ItemIteratorOnly =
- enable_if_t<std::is_convertible<TVal<It>&, TPItem&>::value, Out>;
-
-public:
-
- /**
- * \brief Constructor taking the bin as the only mandatory parameter.
- *
- * \param bin The bin shape that will be used by the placers. The type
- * of the bin should be one that is supported by the placer type.
- */
- template<class TBinType = BinType,
- class PConf = PlacementConfig,
- class SConf = SelectionConfig>
- _Nester(TBinType&& bin, Coord min_obj_distance = 0,
- const PConf& pconfig = PConf(), const SConf& sconfig = SConf()):
- bin_(std::forward<TBinType>(bin)),
- pconfig_(pconfig),
- min_obj_distance_(min_obj_distance)
- {
- static_assert( std::is_same<TPItem, TSItem>::value,
- "Incompatible placement and selection strategy!");
-
- selector_.configure(sconfig);
- }
-
- void configure(const PlacementConfig& pconf) { pconfig_ = pconf; }
- void configure(const SelectionConfig& sconf) { selector_.configure(sconf); }
- void configure(const PlacementConfig& pconf, const SelectionConfig& sconf)
- {
- pconfig_ = pconf;
- selector_.configure(sconf);
- }
- void configure(const SelectionConfig& sconf, const PlacementConfig& pconf)
- {
- pconfig_ = pconf;
- selector_.configure(sconf);
- }
-
- /**
- * \brief Arrange an input sequence of _Item-s.
- *
- * To get the result, call the translation(), rotation() and binId()
- * methods of each item. If only the transformed polygon is needed, call
- * transformedShape() to get the properly transformed shapes.
- *
- * The number of groups in the pack group is the number of bins opened by
- * the selection algorithm.
- */
- template<class It>
- inline ItemIteratorOnly<It, size_t> execute(It from, It to)
- {
- auto infl = static_cast<Coord>(std::ceil(min_obj_distance_/2.0));
- if(infl > 0) std::for_each(from, to, [this, infl](Item& item) {
- item.inflate(infl);
- });
-
- selector_.template packItems<PlacementStrategy>(
- from, to, bin_, pconfig_);
-
- if(min_obj_distance_ > 0) std::for_each(from, to, [infl](Item& item) {
- item.inflate(-infl);
- });
-
- return selector_.getResult().size();
- }
-
- /// Set a progress indicator function object for the selector.
- inline _Nester& progressIndicator(ProgressFunction func)
- {
- selector_.progressIndicator(func); return *this;
- }
-
- /// Set a predicate to tell when to abort nesting.
- inline _Nester& stopCondition(StopCondition fn)
- {
- stopfn_ = fn; selector_.stopCondition(fn); return *this;
- }
-
- inline const PackGroup& lastResult() const
- {
- return selector_.getResult();
- }
-};
-
}
#endif // LIBNEST2D_HPP
diff --git a/src/libnest2d/include/libnest2d/nester.hpp b/src/libnest2d/include/libnest2d/nester.hpp
new file mode 100644
index 000000000..2f207d526
--- /dev/null
+++ b/src/libnest2d/include/libnest2d/nester.hpp
@@ -0,0 +1,869 @@
+#ifndef NESTER_HPP
+#define NESTER_HPP
+
+#include <memory>
+#include <vector>
+#include <map>
+#include <array>
+#include <algorithm>
+#include <functional>
+
+#include <libnest2d/geometry_traits.hpp>
+
+namespace libnest2d {
+
+static const constexpr int BIN_ID_UNSET = -1;
+
+/**
+ * \brief An item to be placed on a bin.
+ *
+ * It holds a copy of the original shape object but supports move construction
+ * from the shape objects if its an rvalue reference. This way we can construct
+ * the items without the cost of copying a potentially large amount of input.
+ *
+ * The results of some calculations are cached for maintaining fast run times.
+ * For this reason, memory demands are much higher but this should pay off.
+ */
+template<class RawShape>
+class _Item {
+ using Coord = TCoord<TPoint<RawShape>>;
+ using Vertex = TPoint<RawShape>;
+ using Box = _Box<Vertex>;
+
+ using VertexConstIterator = typename TContour<RawShape>::const_iterator;
+
+ // The original shape that gets encapsulated.
+ RawShape sh_;
+
+ // Transformation data
+ Vertex translation_{0, 0};
+ Radians rotation_{0.0};
+ Coord inflation_{0};
+
+ // Info about whether the transformations will have to take place
+ // This is needed because if floating point is used, it is hard to say
+ // that a zero angle is not a rotation because of testing for equality.
+ bool has_rotation_ = false, has_translation_ = false, has_inflation_ = false;
+
+ // For caching the calculations as they can get pretty expensive.
+ mutable RawShape tr_cache_;
+ mutable bool tr_cache_valid_ = false;
+ mutable double area_cache_ = 0;
+ mutable bool area_cache_valid_ = false;
+ mutable RawShape inflate_cache_;
+ mutable bool inflate_cache_valid_ = false;
+
+ enum class Convexity: char {
+ UNCHECKED,
+ C_TRUE,
+ C_FALSE
+ };
+
+ mutable Convexity convexity_ = Convexity::UNCHECKED;
+ mutable VertexConstIterator rmt_; // rightmost top vertex
+ mutable VertexConstIterator lmb_; // leftmost bottom vertex
+ mutable bool rmt_valid_ = false, lmb_valid_ = false;
+ mutable struct BBCache {
+ Box bb; bool valid;
+ BBCache(): valid(false) {}
+ } bb_cache_;
+
+ int binid_{BIN_ID_UNSET}, priority_{0};
+ bool fixed_{false};
+
+public:
+
+ /// The type of the shape which was handed over as the template argument.
+ using ShapeType = RawShape;
+
+ /**
+ * \brief Iterator type for the outer vertices.
+ *
+ * Only const iterators can be used. The _Item type is not intended to
+ * modify the carried shapes from the outside. The main purpose of this type
+ * is to cache the calculation results from the various operators it
+ * supports. Giving out a non const iterator would make it impossible to
+ * perform correct cache invalidation.
+ */
+ using Iterator = VertexConstIterator;
+
+ /**
+ * @brief Get the orientation of the polygon.
+ *
+ * The orientation have to be specified as a specialization of the
+ * OrientationType struct which has a Value constant.
+ *
+ * @return The orientation type identifier for the _Item type.
+ */
+ static BP2D_CONSTEXPR Orientation orientation() {
+ return OrientationType<RawShape>::Value;
+ }
+
+ /**
+ * @brief Constructing an _Item form an existing raw shape. The shape will
+ * be copied into the _Item object.
+ * @param sh The original shape object.
+ */
+ explicit inline _Item(const RawShape& sh): sh_(sh) {}
+
+ /**
+ * @brief Construction of an item by moving the content of the raw shape,
+ * assuming that it supports move semantics.
+ * @param sh The original shape object.
+ */
+ explicit inline _Item(RawShape&& sh): sh_(std::move(sh)) {}
+
+ /**
+ * @brief Create an item from an initializer list.
+ * @param il The initializer list of vertices.
+ */
+ inline _Item(const std::initializer_list< Vertex >& il):
+ sh_(sl::create<RawShape>(il)) {}
+
+ inline _Item(const TContour<RawShape>& contour,
+ const THolesContainer<RawShape>& holes = {}):
+ sh_(sl::create<RawShape>(contour, holes)) {}
+
+ inline _Item(TContour<RawShape>&& contour,
+ THolesContainer<RawShape>&& holes):
+ sh_(sl::create<RawShape>(std::move(contour), std::move(holes))) {}
+
+ inline bool isFixed() const noexcept { return fixed_; }
+ inline void markAsFixedInBin(int binid)
+ {
+ fixed_ = binid >= 0;
+ binid_ = binid;
+ }
+
+ inline void binId(int idx) { binid_ = idx; }
+ inline int binId() const noexcept { return binid_; }
+
+ inline void priority(int p) { priority_ = p; }
+ inline int priority() const noexcept { return priority_; }
+
+ /**
+ * @brief Convert the polygon to string representation. The format depends
+ * on the implementation of the polygon.
+ * @return
+ */
+ inline std::string toString() const
+ {
+ return sl::toString(sh_);
+ }
+
+ /// Iterator tho the first contour vertex in the polygon.
+ inline Iterator begin() const
+ {
+ return sl::cbegin(sh_);
+ }
+
+ /// Alias to begin()
+ inline Iterator cbegin() const
+ {
+ return sl::cbegin(sh_);
+ }
+
+ /// Iterator to the last contour vertex.
+ inline Iterator end() const
+ {
+ return sl::cend(sh_);
+ }
+
+ /// Alias to end()
+ inline Iterator cend() const
+ {
+ return sl::cend(sh_);
+ }
+
+ /**
+ * @brief Get a copy of an outer vertex within the carried shape.
+ *
+ * Note that the vertex considered here is taken from the original shape
+ * that this item is constructed from. This means that no transformation is
+ * applied to the shape in this call.
+ *
+ * @param idx The index of the requested vertex.
+ * @return A copy of the requested vertex.
+ */
+ inline Vertex vertex(unsigned long idx) const
+ {
+ return sl::vertex(sh_, idx);
+ }
+
+ /**
+ * @brief Modify a vertex.
+ *
+ * Note that this method will invalidate every cached calculation result
+ * including polygon offset and transformations.
+ *
+ * @param idx The index of the requested vertex.
+ * @param v The new vertex data.
+ */
+ inline void setVertex(unsigned long idx, const Vertex& v )
+ {
+ invalidateCache();
+ sl::vertex(sh_, idx) = v;
+ }
+
+ /**
+ * @brief Calculate the shape area.
+ *
+ * The method returns absolute value and does not reflect polygon
+ * orientation. The result is cached, subsequent calls will have very little
+ * cost.
+ * @return The shape area in floating point double precision.
+ */
+ inline double area() const {
+ double ret ;
+ if(area_cache_valid_) ret = area_cache_;
+ else {
+ ret = sl::area(infaltedShape());
+ area_cache_ = ret;
+ area_cache_valid_ = true;
+ }
+ return ret;
+ }
+
+ inline bool isContourConvex() const {
+ bool ret = false;
+
+ switch(convexity_) {
+ case Convexity::UNCHECKED:
+ ret = sl::isConvex(sl::contour(transformedShape()));
+ convexity_ = ret? Convexity::C_TRUE : Convexity::C_FALSE;
+ break;
+ case Convexity::C_TRUE: ret = true; break;
+ case Convexity::C_FALSE:;
+ }
+
+ return ret;
+ }
+
+ inline bool isHoleConvex(unsigned /*holeidx*/) const {
+ return false;
+ }
+
+ inline bool areHolesConvex() const {
+ return false;
+ }
+
+ /// The number of the outer ring vertices.
+ inline size_t vertexCount() const {
+ return sl::contourVertexCount(sh_);
+ }
+
+ inline size_t holeCount() const {
+ return sl::holeCount(sh_);
+ }
+
+ /**
+ * @brief isPointInside
+ * @param p
+ * @return
+ */
+ inline bool isInside(const Vertex& p) const
+ {
+ return sl::isInside(p, transformedShape());
+ }
+
+ inline bool isInside(const _Item& sh) const
+ {
+ return sl::isInside(transformedShape(), sh.transformedShape());
+ }
+
+ inline bool isInside(const RawShape& sh) const
+ {
+ return sl::isInside(transformedShape(), sh);
+ }
+
+ inline bool isInside(const _Box<TPoint<RawShape>>& box) const;
+ inline bool isInside(const _Circle<TPoint<RawShape>>& box) const;
+
+ inline void translate(const Vertex& d) BP2D_NOEXCEPT
+ {
+ translation(translation() + d);
+ }
+
+ inline void rotate(const Radians& rads) BP2D_NOEXCEPT
+ {
+ rotation(rotation() + rads);
+ }
+
+ inline void inflation(Coord distance) BP2D_NOEXCEPT
+ {
+ inflation_ = distance;
+ has_inflation_ = true;
+ invalidateCache();
+ }
+
+ inline Coord inflation() const BP2D_NOEXCEPT {
+ return inflation_;
+ }
+
+ inline void inflate(Coord distance) BP2D_NOEXCEPT
+ {
+ inflation(inflation() + distance);
+ }
+
+ inline Radians rotation() const BP2D_NOEXCEPT
+ {
+ return rotation_;
+ }
+
+ inline TPoint<RawShape> translation() const BP2D_NOEXCEPT
+ {
+ return translation_;
+ }
+
+ inline void rotation(Radians rot) BP2D_NOEXCEPT
+ {
+ if(rotation_ != rot) {
+ rotation_ = rot; has_rotation_ = true; tr_cache_valid_ = false;
+ rmt_valid_ = false; lmb_valid_ = false;
+ bb_cache_.valid = false;
+ }
+ }
+
+ inline void translation(const TPoint<RawShape>& tr) BP2D_NOEXCEPT
+ {
+ if(translation_ != tr) {
+ translation_ = tr; has_translation_ = true; tr_cache_valid_ = false;
+ //bb_cache_.valid = false;
+ }
+ }
+
+ inline const RawShape& transformedShape() const
+ {
+ if(tr_cache_valid_) return tr_cache_;
+
+ RawShape cpy = infaltedShape();
+ if(has_rotation_) sl::rotate(cpy, rotation_);
+ if(has_translation_) sl::translate(cpy, translation_);
+ tr_cache_ = cpy; tr_cache_valid_ = true;
+ rmt_valid_ = false; lmb_valid_ = false;
+
+ return tr_cache_;
+ }
+
+ inline operator RawShape() const
+ {
+ return transformedShape();
+ }
+
+ inline const RawShape& rawShape() const BP2D_NOEXCEPT
+ {
+ return sh_;
+ }
+
+ inline void resetTransformation() BP2D_NOEXCEPT
+ {
+ has_translation_ = false; has_rotation_ = false; has_inflation_ = false;
+ invalidateCache();
+ }
+
+ inline Box boundingBox() const {
+ if(!bb_cache_.valid) {
+ if(!has_rotation_)
+ bb_cache_.bb = sl::boundingBox(infaltedShape());
+ else {
+ // TODO make sure this works
+ auto rotsh = infaltedShape();
+ sl::rotate(rotsh, rotation_);
+ bb_cache_.bb = sl::boundingBox(rotsh);
+ }
+ bb_cache_.valid = true;
+ }
+
+ auto &bb = bb_cache_.bb; auto &tr = translation_;
+ return {bb.minCorner() + tr, bb.maxCorner() + tr };
+ }
+
+ inline Vertex referenceVertex() const {
+ return rightmostTopVertex();
+ }
+
+ inline Vertex rightmostTopVertex() const {
+ if(!rmt_valid_ || !tr_cache_valid_) { // find max x and max y vertex
+ auto& tsh = transformedShape();
+ rmt_ = std::max_element(sl::cbegin(tsh), sl::cend(tsh), vsort);
+ rmt_valid_ = true;
+ }
+ return *rmt_;
+ }
+
+ inline Vertex leftmostBottomVertex() const {
+ if(!lmb_valid_ || !tr_cache_valid_) { // find min x and min y vertex
+ auto& tsh = transformedShape();
+ lmb_ = std::min_element(sl::cbegin(tsh), sl::cend(tsh), vsort);
+ lmb_valid_ = true;
+ }
+ return *lmb_;
+ }
+
+ //Static methods:
+
+ inline static bool intersects(const _Item& sh1, const _Item& sh2)
+ {
+ return sl::intersects(sh1.transformedShape(),
+ sh2.transformedShape());
+ }
+
+ inline static bool touches(const _Item& sh1, const _Item& sh2)
+ {
+ return sl::touches(sh1.transformedShape(),
+ sh2.transformedShape());
+ }
+
+private:
+
+ inline const RawShape& infaltedShape() const {
+ if(has_inflation_ ) {
+ if(inflate_cache_valid_) return inflate_cache_;
+
+ inflate_cache_ = sh_;
+ sl::offset(inflate_cache_, inflation_);
+ inflate_cache_valid_ = true;
+ return inflate_cache_;
+ }
+ return sh_;
+ }
+
+ inline void invalidateCache() const BP2D_NOEXCEPT
+ {
+ tr_cache_valid_ = false;
+ lmb_valid_ = false; rmt_valid_ = false;
+ area_cache_valid_ = false;
+ inflate_cache_valid_ = false;
+ bb_cache_.valid = false;
+ convexity_ = Convexity::UNCHECKED;
+ }
+
+ static inline bool vsort(const Vertex& v1, const Vertex& v2)
+ {
+ TCompute<Vertex> x1 = getX(v1), x2 = getX(v2);
+ TCompute<Vertex> y1 = getY(v1), y2 = getY(v2);
+ return y1 == y2 ? x1 < x2 : y1 < y2;
+ }
+};
+
+/**
+ * \brief Subclass of _Item for regular rectangle items.
+ */
+template<class RawShape>
+class _Rectangle: public _Item<RawShape> {
+ using _Item<RawShape>::vertex;
+ using TO = Orientation;
+public:
+
+ using Unit = TCoord<TPoint<RawShape>>;
+
+ template<TO o = OrientationType<RawShape>::Value>
+ inline _Rectangle(Unit width, Unit height,
+ // disable this ctor if o != CLOCKWISE
+ enable_if_t< o == TO::CLOCKWISE, int> = 0 ):
+ _Item<RawShape>( sl::create<RawShape>( {
+ {0, 0},
+ {0, height},
+ {width, height},
+ {width, 0},
+ {0, 0}
+ } ))
+ {
+ }
+
+ template<TO o = OrientationType<RawShape>::Value>
+ inline _Rectangle(Unit width, Unit height,
+ // disable this ctor if o != COUNTER_CLOCKWISE
+ enable_if_t< o == TO::COUNTER_CLOCKWISE, int> = 0 ):
+ _Item<RawShape>( sl::create<RawShape>( {
+ {0, 0},
+ {width, 0},
+ {width, height},
+ {0, height},
+ {0, 0}
+ } ))
+ {
+ }
+
+ inline Unit width() const BP2D_NOEXCEPT {
+ return getX(vertex(2));
+ }
+
+ inline Unit height() const BP2D_NOEXCEPT {
+ return getY(vertex(2));
+ }
+};
+
+template<class RawShape>
+inline bool _Item<RawShape>::isInside(const _Box<TPoint<RawShape>>& box) const {
+ return sl::isInside(boundingBox(), box);
+}
+
+template<class RawShape> inline bool
+_Item<RawShape>::isInside(const _Circle<TPoint<RawShape>>& circ) const {
+ return sl::isInside(transformedShape(), circ);
+}
+
+template<class RawShape> using _ItemRef = std::reference_wrapper<_Item<RawShape>>;
+template<class RawShape> using _ItemGroup = std::vector<_ItemRef<RawShape>>;
+
+/**
+ * \brief A list of packed item vectors. Each vector represents a bin.
+ */
+template<class RawShape>
+using _PackGroup = std::vector<std::vector<_ItemRef<RawShape>>>;
+
+template<class Iterator>
+struct ConstItemRange {
+ Iterator from;
+ Iterator to;
+ bool valid = false;
+
+ ConstItemRange() = default;
+ ConstItemRange(Iterator f, Iterator t): from(f), to(t), valid(true) {}
+};
+
+template<class Container>
+inline ConstItemRange<typename Container::const_iterator>
+rem(typename Container::const_iterator it, const Container& cont) {
+ return {std::next(it), cont.end()};
+}
+
+/**
+ * \brief A wrapper interface (trait) class for any placement strategy provider.
+ *
+ * If a client wants to use its own placement algorithm, all it has to do is to
+ * specialize this class template and define all the ten methods it has. It can
+ * use the strategies::PlacerBoilerplace class for creating a new placement
+ * strategy where only the constructor and the trypack method has to be provided
+ * and it will work out of the box.
+ */
+template<class PlacementStrategy>
+class PlacementStrategyLike {
+ PlacementStrategy impl_;
+public:
+
+ using RawShape = typename PlacementStrategy::ShapeType;
+
+ /// The item type that the placer works with.
+ using Item = _Item<RawShape>;
+
+ /// The placer's config type. Should be a simple struct but can be anything.
+ using Config = typename PlacementStrategy::Config;
+
+ /**
+ * \brief The type of the bin that the placer works with.
+ *
+ * Can be a box or an arbitrary shape or just a width or height without a
+ * second dimension if an infinite bin is considered.
+ */
+ using BinType = typename PlacementStrategy::BinType;
+
+ /**
+ * \brief Pack result that can be used to accept or discard it. See trypack
+ * method.
+ */
+ using PackResult = typename PlacementStrategy::PackResult;
+
+ using ItemGroup = _ItemGroup<RawShape>;
+ using DefaultIterator = typename ItemGroup::const_iterator;
+
+ /**
+ * @brief Constructor taking the bin and an optional configuration.
+ * @param bin The bin object whose type is defined by the placement strategy.
+ * @param config The configuration for the particular placer.
+ */
+ explicit PlacementStrategyLike(const BinType& bin,
+ const Config& config = Config()):
+ impl_(bin)
+ {
+ configure(config);
+ }
+
+ /**
+ * @brief Provide a different configuration for the placer.
+ *
+ * Note that it depends on the particular placer implementation how it
+ * reacts to config changes in the middle of a calculation.
+ *
+ * @param config The configuration object defined by the placement strategy.
+ */
+ inline void configure(const Config& config) { impl_.configure(config); }
+
+ /**
+ * Try to pack an item with a result object that contains the packing
+ * information for later accepting it.
+ *
+ * \param item_store A container of items that are intended to be packed
+ * later. Can be used by the placer to switch tactics. When it's knows that
+ * many items will come a greedy strategy may not be the best.
+ * \param from The iterator to the item from which the packing should start,
+ * including the pointed item
+ * \param count How many items should be packed. If the value is 1, than
+ * just the item pointed to by "from" argument should be packed.
+ */
+ template<class Iter = DefaultIterator>
+ inline PackResult trypack(
+ Item& item,
+ const ConstItemRange<Iter>& remaining = ConstItemRange<Iter>())
+ {
+ return impl_.trypack(item, remaining);
+ }
+
+ /**
+ * @brief A method to accept a previously tried item (or items).
+ *
+ * If the pack result is a failure the method should ignore it.
+ * @param r The result of a previous trypack call.
+ */
+ inline void accept(PackResult& r) { impl_.accept(r); }
+
+ /**
+ * @brief pack Try to pack and immediately accept it on success.
+ *
+ * A default implementation would be to call
+ * { auto&& r = trypack(...); accept(r); return r; } but we should let the
+ * implementor of the placement strategy to harvest any optimizations from
+ * the absence of an intermediate step. The above version can still be used
+ * in the implementation.
+ *
+ * @param item The item to pack.
+ * @return Returns true if the item was packed or false if it could not be
+ * packed.
+ */
+ template<class Range = ConstItemRange<DefaultIterator>>
+ inline bool pack(
+ Item& item,
+ const Range& remaining = Range())
+ {
+ return impl_.pack(item, remaining);
+ }
+
+ /**
+ * This method makes possible to "preload" some items into the placer. It
+ * will not move these items but will consider them as already packed.
+ */
+ inline void preload(const ItemGroup& packeditems)
+ {
+ impl_.preload(packeditems);
+ }
+
+ /// Unpack the last element (remove it from the list of packed items).
+ inline void unpackLast() { impl_.unpackLast(); }
+
+ /// Get the bin object.
+ inline const BinType& bin() const { return impl_.bin(); }
+
+ /// Set a new bin object.
+ inline void bin(const BinType& bin) { impl_.bin(bin); }
+
+ /// Get the packed items.
+ inline ItemGroup getItems() { return impl_.getItems(); }
+
+ /// Clear the packed items so a new session can be started.
+ inline void clearItems() { impl_.clearItems(); }
+
+ inline double filledArea() const { return impl_.filledArea(); }
+
+};
+
+// The progress function will be called with the number of placed items
+using ProgressFunction = std::function<void(unsigned)>;
+using StopCondition = std::function<bool(void)>;
+
+/**
+ * A wrapper interface (trait) class for any selections strategy provider.
+ */
+template<class SelectionStrategy>
+class SelectionStrategyLike {
+ SelectionStrategy impl_;
+public:
+ using RawShape = typename SelectionStrategy::ShapeType;
+ using Item = _Item<RawShape>;
+ using PackGroup = _PackGroup<RawShape>;
+ using Config = typename SelectionStrategy::Config;
+
+
+ /**
+ * @brief Provide a different configuration for the selection strategy.
+ *
+ * Note that it depends on the particular placer implementation how it
+ * reacts to config changes in the middle of a calculation.
+ *
+ * @param config The configuration object defined by the selection strategy.
+ */
+ inline void configure(const Config& config) {
+ impl_.configure(config);
+ }
+
+ /**
+ * @brief A function callback which should be called whenever an item or
+ * a group of items where successfully packed.
+ * @param fn A function callback object taking one unsigned integer as the
+ * number of the remaining items to pack.
+ */
+ void progressIndicator(ProgressFunction fn) { impl_.progressIndicator(fn); }
+
+ void stopCondition(StopCondition cond) { impl_.stopCondition(cond); }
+
+ /**
+ * \brief A method to start the calculation on the input sequence.
+ *
+ * \tparam TPlacer The only mandatory template parameter is the type of
+ * placer compatible with the PlacementStrategyLike interface.
+ *
+ * \param first, last The first and last iterator if the input sequence. It
+ * can be only an iterator of a type convertible to Item.
+ * \param bin. The shape of the bin. It has to be supported by the placement
+ * strategy.
+ * \param An optional config object for the placer.
+ */
+ template<class TPlacer, class TIterator,
+ class TBin = typename PlacementStrategyLike<TPlacer>::BinType,
+ class PConfig = typename PlacementStrategyLike<TPlacer>::Config>
+ inline void packItems(
+ TIterator first,
+ TIterator last,
+ TBin&& bin,
+ PConfig&& config = PConfig() )
+ {
+ impl_.template packItems<TPlacer>(first, last,
+ std::forward<TBin>(bin),
+ std::forward<PConfig>(config));
+ }
+
+ /**
+ * @brief Get the items for a particular bin.
+ * @param binIndex The index of the requested bin.
+ * @return Returns a list of all items packed into the requested bin.
+ */
+ inline const PackGroup& getResult() const {
+ return impl_.getResult();
+ }
+
+ void clear() { impl_.clear(); }
+};
+
+/**
+ * The _Nester is the front-end class for the libnest2d library. It takes the
+ * input items and changes their transformations to be inside the provided bin.
+ */
+template<class PlacementStrategy, class SelectionStrategy >
+class _Nester {
+ using TSel = SelectionStrategyLike<SelectionStrategy>;
+ TSel selector_;
+
+public:
+ using Item = typename PlacementStrategy::Item;
+ using ShapeType = typename Item::ShapeType;
+ using ItemRef = std::reference_wrapper<Item>;
+ using TPlacer = PlacementStrategyLike<PlacementStrategy>;
+ using BinType = typename TPlacer::BinType;
+ using PlacementConfig = typename TPlacer::Config;
+ using SelectionConfig = typename TSel::Config;
+ using Coord = TCoord<TPoint<typename Item::ShapeType>>;
+ using PackGroup = _PackGroup<typename Item::ShapeType>;
+ using ResultType = PackGroup;
+
+private:
+ BinType bin_;
+ PlacementConfig pconfig_;
+ Coord min_obj_distance_;
+
+ using SItem = typename SelectionStrategy::Item;
+ using TPItem = remove_cvref_t<Item>;
+ using TSItem = remove_cvref_t<SItem>;
+
+ StopCondition stopfn_;
+
+ template<class It> using TVal = remove_ref_t<typename It::value_type>;
+
+ template<class It, class Out>
+ using ItemIteratorOnly =
+ enable_if_t<std::is_convertible<TVal<It>&, TPItem&>::value, Out>;
+
+public:
+
+ /**
+ * \brief Constructor taking the bin as the only mandatory parameter.
+ *
+ * \param bin The bin shape that will be used by the placers. The type
+ * of the bin should be one that is supported by the placer type.
+ */
+ template<class TBinType = BinType,
+ class PConf = PlacementConfig,
+ class SConf = SelectionConfig>
+ _Nester(TBinType&& bin, Coord min_obj_distance = 0,
+ const PConf& pconfig = PConf(), const SConf& sconfig = SConf()):
+ bin_(std::forward<TBinType>(bin)),
+ pconfig_(pconfig),
+ min_obj_distance_(min_obj_distance)
+ {
+ static_assert( std::is_same<TPItem, TSItem>::value,
+ "Incompatible placement and selection strategy!");
+
+ selector_.configure(sconfig);
+ }
+
+ void configure(const PlacementConfig& pconf) { pconfig_ = pconf; }
+ void configure(const SelectionConfig& sconf) { selector_.configure(sconf); }
+ void configure(const PlacementConfig& pconf, const SelectionConfig& sconf)
+ {
+ pconfig_ = pconf;
+ selector_.configure(sconf);
+ }
+ void configure(const SelectionConfig& sconf, const PlacementConfig& pconf)
+ {
+ pconfig_ = pconf;
+ selector_.configure(sconf);
+ }
+
+ /**
+ * \brief Arrange an input sequence of _Item-s.
+ *
+ * To get the result, call the translation(), rotation() and binId()
+ * methods of each item. If only the transformed polygon is needed, call
+ * transformedShape() to get the properly transformed shapes.
+ *
+ * The number of groups in the pack group is the number of bins opened by
+ * the selection algorithm.
+ */
+ template<class It>
+ inline ItemIteratorOnly<It, size_t> execute(It from, It to)
+ {
+ auto infl = static_cast<Coord>(std::ceil(min_obj_distance_/2.0));
+ if(infl > 0) std::for_each(from, to, [this, infl](Item& item) {
+ item.inflate(infl);
+ });
+
+ selector_.template packItems<PlacementStrategy>(
+ from, to, bin_, pconfig_);
+
+ if(min_obj_distance_ > 0) std::for_each(from, to, [infl](Item& item) {
+ item.inflate(-infl);
+ });
+
+ return selector_.getResult().size();
+ }
+
+ /// Set a progress indicator function object for the selector.
+ inline _Nester& progressIndicator(ProgressFunction func)
+ {
+ selector_.progressIndicator(func); return *this;
+ }
+
+ /// Set a predicate to tell when to abort nesting.
+ inline _Nester& stopCondition(StopCondition fn)
+ {
+ stopfn_ = fn; selector_.stopCondition(fn); return *this;
+ }
+
+ inline const PackGroup& lastResult() const
+ {
+ return selector_.getResult();
+ }
+};
+
+}
+
+#endif // NESTER_HPP
diff --git a/src/libnest2d/include/libnest2d/optimizers/nlopt/CMakeLists.txt b/src/libnest2d/include/libnest2d/optimizers/nlopt/CMakeLists.txt
deleted file mode 100644
index 6f51718d8..000000000
--- a/src/libnest2d/include/libnest2d/optimizers/nlopt/CMakeLists.txt
+++ /dev/null
@@ -1,61 +0,0 @@
-find_package(NLopt 1.4)
-
-if(NOT NLopt_FOUND)
- message(STATUS "NLopt not found so downloading "
- "and automatic build is performed...")
-
- include(DownloadProject)
-
- if (CMAKE_VERSION VERSION_LESS 3.2)
- set(UPDATE_DISCONNECTED_IF_AVAILABLE "")
- else()
- set(UPDATE_DISCONNECTED_IF_AVAILABLE "UPDATE_DISCONNECTED 1")
- endif()
-
- set(URL_NLOPT "https://github.com/stevengj/nlopt.git"
- CACHE STRING "Location of the nlopt git repository")
-
- # set(NLopt_DIR ${CMAKE_BINARY_DIR}/nlopt)
- include(DownloadProject)
- download_project( PROJ nlopt
- GIT_REPOSITORY ${URL_NLOPT}
- GIT_TAG v2.5.0
- # CMAKE_CACHE_ARGS -DBUILD_SHARED_LIBS:BOOL=OFF -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX=${NLopt_DIR}
- ${UPDATE_DISCONNECTED_IF_AVAILABLE}
- )
-
- set(SHARED_LIBS_STATE BUILD_SHARED_LIBS)
- set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
- set(NLOPT_PYTHON OFF CACHE BOOL "" FORCE)
- set(NLOPT_OCTAVE OFF CACHE BOOL "" FORCE)
- set(NLOPT_MATLAB OFF CACHE BOOL "" FORCE)
- set(NLOPT_GUILE OFF CACHE BOOL "" FORCE)
- set(NLOPT_SWIG OFF CACHE BOOL "" FORCE)
- set(NLOPT_LINK_PYTHON OFF CACHE BOOL "" FORCE)
-
- add_subdirectory(${nlopt_SOURCE_DIR} ${nlopt_BINARY_DIR})
-
- set(NLopt_LIBS nlopt)
- set(NLopt_INCLUDE_DIR ${nlopt_BINARY_DIR} ${nlopt_BINARY_DIR}/src/api)
- set(SHARED_LIBS_STATE ${SHARED_STATE})
-
- add_library(nloptOptimizer INTERFACE)
- target_link_libraries(nloptOptimizer INTERFACE nlopt)
- target_include_directories(nloptOptimizer INTERFACE ${NLopt_INCLUDE_DIR})
-
-else()
- add_library(nloptOptimizer INTERFACE)
- target_link_libraries(nloptOptimizer INTERFACE Nlopt::Nlopt)
-endif()
-
-#target_sources( nloptOptimizer INTERFACE
-#${CMAKE_CURRENT_SOURCE_DIR}/simplex.hpp
-#${CMAKE_CURRENT_SOURCE_DIR}/subplex.hpp
-#${CMAKE_CURRENT_SOURCE_DIR}/genetic.hpp
-#${CMAKE_CURRENT_SOURCE_DIR}/nlopt_boilerplate.hpp
-#)
-
-target_compile_definitions(nloptOptimizer INTERFACE LIBNEST2D_OPTIMIZER_NLOPT)
-
-# And finally plug the nloptOptimizer into libnest2d
-#target_link_libraries(libnest2d INTERFACE nloptOptimizer)
diff --git a/src/libnest2d/include/libnest2d/placers/placer_boilerplate.hpp b/src/libnest2d/include/libnest2d/placers/placer_boilerplate.hpp
index 26681aeec..dc1bbd4f1 100644
--- a/src/libnest2d/include/libnest2d/placers/placer_boilerplate.hpp
+++ b/src/libnest2d/include/libnest2d/placers/placer_boilerplate.hpp
@@ -1,7 +1,7 @@
#ifndef PLACER_BOILERPLATE_HPP
#define PLACER_BOILERPLATE_HPP
-#include <libnest2d/libnest2d.hpp>
+#include <libnest2d/nester.hpp>
namespace libnest2d { namespace placers {
diff --git a/src/libnest2d/include/libnest2d/selections/selection_boilerplate.hpp b/src/libnest2d/include/libnest2d/selections/selection_boilerplate.hpp
index 36fec7164..8e65bafe9 100644
--- a/src/libnest2d/include/libnest2d/selections/selection_boilerplate.hpp
+++ b/src/libnest2d/include/libnest2d/selections/selection_boilerplate.hpp
@@ -2,7 +2,7 @@
#define SELECTION_BOILERPLATE_HPP
#include <atomic>
-#include <libnest2d/libnest2d.hpp>
+#include <libnest2d/nester.hpp>
namespace libnest2d { namespace selections {
@@ -25,7 +25,7 @@ public:
inline void clear() { packed_bins_.clear(); }
protected:
-
+
template<class Placer, class Container, class Bin, class PCfg>
void remove_unpackable_items(Container &c, const Bin &bin, const PCfg& pcfg)
{
@@ -33,14 +33,14 @@ protected:
// then it should be removed from the list
auto it = c.begin();
while (it != c.end() && !stopcond_()) {
-
+
// WARNING: The copy of itm needs to be created before Placer.
// Placer is working with references and its destructor still
// manipulates the item this is why the order of stack creation
- // matters here.
+ // matters here.
const Item& itm = *it;
Item cpy{itm};
-
+
Placer p{bin};
p.configure(pcfg);
if (itm.area() <= 0 || !p.pack(cpy)) it = c.erase(it);
diff --git a/src/libnest2d/src/libnest2d.cpp b/src/libnest2d/src/libnest2d.cpp
index 740f6e18d..5a881541e 100644
--- a/src/libnest2d/src/libnest2d.cpp
+++ b/src/libnest2d/src/libnest2d.cpp
@@ -1,19 +1,24 @@
-#include <libnest2d.h>
+#include <libnest2d/libnest2d.hpp>
namespace libnest2d {
-template class Nester<NfpPlacer, FirstFitSelection>;
-template class Nester<BottomLeftPlacer, FirstFitSelection>;
+template class _Nester<NfpPlacer, FirstFitSelection>;
+template class _Nester<BottomLeftPlacer, FirstFitSelection>;
+
+template std::size_t _Nester<NfpPlacer, FirstFitSelection>::execute(
+ std::vector<Item>::iterator, std::vector<Item>::iterator);
+template std::size_t _Nester<BottomLeftPlacer, FirstFitSelection>::execute(
+ std::vector<Item>::iterator, std::vector<Item>::iterator);
template std::size_t nest(std::vector<Item>::iterator from,
- std::vector<Item>::iterator from to,
+ std::vector<Item>::iterator to,
const Box & bin,
Coord dist,
const NestConfig<NfpPlacer, FirstFitSelection> &cfg,
NestControl ctl);
template std::size_t nest(std::vector<Item>::iterator from,
- std::vector<Item>::iterator from to,
+ std::vector<Item>::iterator to,
const Box & bin,
Coord dist,
const NestConfig<BottomLeftPlacer, FirstFitSelection> &cfg,
diff --git a/src/libnest2d/tests/CMakeLists.txt b/src/libnest2d/tests/CMakeLists.txt
deleted file mode 100644
index 1b7d8e3ae..000000000
--- a/src/libnest2d/tests/CMakeLists.txt
+++ /dev/null
@@ -1,60 +0,0 @@
-
-# Try to find existing GTest installation
-find_package(GTest 1.7)
-
-if(NOT GTEST_FOUND)
- set(URL_GTEST "https://github.com/google/googletest.git"
- CACHE STRING "Google test source code repository location.")
-
- message(STATUS "GTest not found so downloading from ${URL_GTEST}")
- # Go and download google test framework, integrate it with the build
- set(GTEST_LIBS_TO_LINK gtest gtest_main)
-
- if (CMAKE_VERSION VERSION_LESS 3.2)
- set(UPDATE_DISCONNECTED_IF_AVAILABLE "")
- else()
- set(UPDATE_DISCONNECTED_IF_AVAILABLE "UPDATE_DISCONNECTED 1")
- endif()
-
- include(DownloadProject)
- download_project(PROJ googletest
- GIT_REPOSITORY ${URL_GTEST}
- GIT_TAG release-1.7.0
- ${UPDATE_DISCONNECTED_IF_AVAILABLE}
- )
-
- # Prevent GoogleTest from overriding our compiler/linker options
- # when building with Visual Studio
- set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
-
- add_subdirectory(${googletest_SOURCE_DIR}
- ${googletest_BINARY_DIR}
- )
-
- set(GTEST_INCLUDE_DIRS ${googletest_SOURCE_DIR}/include)
-
-else()
- find_package(Threads REQUIRED)
- set(GTEST_LIBS_TO_LINK ${GTEST_BOTH_LIBRARIES} Threads::Threads)
-endif()
-
-add_executable(tests_clipper_nlopt
- test.cpp
- ../tools/svgtools.hpp
-# ../tools/libnfpglue.hpp
-# ../tools/libnfpglue.cpp
- printer_parts.h
- printer_parts.cpp
-)
-
-target_link_libraries(tests_clipper_nlopt libnest2d ${GTEST_LIBS_TO_LINK} )
-
-target_include_directories(tests_clipper_nlopt PRIVATE BEFORE ${GTEST_INCLUDE_DIRS})
-
-if(NOT LIBNEST2D_HEADER_ONLY)
- target_link_libraries(tests_clipper_nlopt ${LIBNAME})
-else()
- target_link_libraries(tests_clipper_nlopt libnest2d)
-endif()
-
-add_test(libnest2d_tests tests_clipper_nlopt)
diff --git a/src/libnest2d/tests/printer_parts.cpp b/src/libnest2d/tests/printer_parts.cpp
deleted file mode 100644
index bdc2a3d43..000000000
--- a/src/libnest2d/tests/printer_parts.cpp
+++ /dev/null
@@ -1,3175 +0,0 @@
-#include "printer_parts.h"
-
-const TestData PRINTER_PART_POLYGONS =
-{
- {
- {-5000000, 8954050},
- {5000000, 8954050},
- {5000000, -45949},
- {4972609, -568550},
- {3500000, -8954050},
- {-3500000, -8954050},
- {-4972609, -568550},
- {-5000000, -45949},
- {-5000000, 8954050},
- },
- {
- {-63750000, -8000000},
- {-54750000, 46000000},
- {50750000, 46000000},
- {63750000, 33000000},
- {63750000, -46000000},
- {-54750000, -46000000},
- {-63750000, -28000000},
- {-63750000, -8000000},
- },
- {
- {-52750000, 41512348},
- {-31250000, 45987651},
- {52750000, 45987651},
- {52750000, -45987651},
- {-52750000, -45987651},
- {-52750000, 41512348},
- },
- {
- {-3900000, 14000000},
- {-2167950, 14000000},
- {1721454, 7263400},
- {3828529, 3613790},
- {3838809, 3582149},
- {3871560, 3270569},
- {3900000, 3000000},
- {3500000, -3000000},
- {3471560, -3270565},
- {3447549, -3498986},
- {3292510, -3976167},
- {3099999, -4512949},
- {2530129, -5500000},
- {807565, -8483570},
- {-2377349, -14000000},
- {-3900000, -14000000},
- {-3900000, 14000000},
- },
- {
- {-31750000, -1000000},
- {-25250000, 40500000},
- {-18250000, 47500000},
- {10750000, 47500000},
- {16750000, 41500000},
- {31750000, -37000000},
- {31750000, -43857898},
- {28107900, -47500000},
- {18392099, -47500000},
- {-20750000, -46500000},
- {-31750000, -4000000},
- {-31750000, -1000000},
- },
- {
- {-34625000, -14265399},
- {-10924999, 24875000},
- {33325000, 24875000},
- {37575000, 20625000},
- {37575000, 17625000},
- {26575000, -24875000},
- {-8924999, -24875000},
- {-34625000, -24484600},
- {-37575000, -19375000},
- {-34625000, -14265399},
- },
- {
- {-14000000, 9000000},
- {-11000000, 17000000},
- {14000000, 17000000},
- {14000000, -17000000},
- {-11000000, -17000000},
- {-14000000, -8000000},
- {-14000000, 9000000},
- },
- {
- {-5300000, 2227401},
- {-237800, 5150001},
- {5299999, 5150001},
- {5299999, 650001},
- {4699999, -5149997},
- {-5300000, -5149997},
- {-5300000, 2227401},
- },
- {
- {-12000000, 18000000},
- {12000000, 18000000},
- {12000000, -18000000},
- {-12000000, -18000000},
- {-12000000, 18000000},
- },
- {
- {-18000000, -1000000},
- {-15000000, 22000000},
- {-11000000, 26000000},
- {11000000, 26000000},
- {15000000, 22000000},
- {18000000, -1000000},
- {18000000, -26000000},
- {-18000000, -26000000},
- {-18000000, -1000000},
- },
- {
- {-77500000, 30000000},
- {-72500000, 35000000},
- {72500000, 35000000},
- {77500000, 30000000},
- {77500000, -32928901},
- {75428901, -35000000},
- {-75428901, -35000000},
- {-77500000, -32928901},
- {-77500000, 30000000},
- },
- {
- {-9945219, -3065619},
- {-9781479, -2031780},
- {-9510560, -1020730},
- {-9135450, -43529},
- {-2099999, 14110899},
- {2099999, 14110899},
- {9135450, -43529},
- {9510560, -1020730},
- {9781479, -2031780},
- {9945219, -3065619},
- {10000000, -4110899},
- {9945219, -5156179},
- {9781479, -6190019},
- {9510560, -7201069},
- {9135450, -8178270},
- {8660249, -9110899},
- {8090169, -9988750},
- {7431449, -10802209},
- {6691309, -11542349},
- {5877850, -12201069},
- {5000000, -12771149},
- {4067369, -13246350},
- {3090169, -13621459},
- {2079119, -13892379},
- {1045279, -14056119},
- {0, -14110899},
- {-1045279, -14056119},
- {-2079119, -13892379},
- {-3090169, -13621459},
- {-4067369, -13246350},
- {-5000000, -12771149},
- {-5877850, -12201069},
- {-6691309, -11542349},
- {-7431449, -10802209},
- {-8090169, -9988750},
- {-8660249, -9110899},
- {-9135450, -8178270},
- {-9510560, -7201069},
- {-9781479, -6190019},
- {-9945219, -5156179},
- {-10000000, -4110899},
- {-9945219, -3065619},
- },
- {
- {-34192394, -5192389},
- {-31499996, 39000000},
- {-8183795, 47668998},
- {-6769596, 47668998},
- {-4648197, 45547698},
- {34192394, 6707109},
- {34192394, 5192389},
- {31500003, -39000000},
- {8183803, -47668998},
- {6769603, -47668998},
- {4648202, -45547698},
- {-32474895, -8424619},
- {-34192394, -6707109},
- {-34192394, -5192389},
- },
- {
- {-23475500, -11910099},
- {-18000000, 8217699},
- {-11139699, 20100000},
- {-10271400, 20899999},
- {9532010, 20899999},
- {11199999, 20100000},
- {18500000, 8600000},
- {23475500, -11910099},
- {23799999, -14899999},
- {23706600, -15788900},
- {23668899, -16147499},
- {23281299, -17340400},
- {22654100, -18426700},
- {21814800, -19358900},
- {20799999, -20096199},
- {19654100, -20606300},
- {18427200, -20867099},
- {17799999, -20899999},
- {-17799999, -20899999},
- {-18427200, -20867099},
- {-19654100, -20606300},
- {-20799999, -20096199},
- {-21814800, -19358900},
- {-22654100, -18426700},
- {-23281299, -17340400},
- {-23668899, -16147499},
- {-23799999, -14899999},
- {-23475500, -11910099},
- },
- {
- {-32000000, 10000000},
- {-31934440, 10623733},
- {-31740640, 11220210},
- {-31427049, 11763360},
- {-31007389, 12229430},
- {-30500000, 12598079},
- {-29927051, 12853170},
- {-29313585, 12983570},
- {16000000, 16000000},
- {26000000, 16000000},
- {31007400, 12229430},
- {31427101, 11763360},
- {31740600, 11220210},
- {31934398, 10623733},
- {32000000, 10000000},
- {32000000, -13000000},
- {31934398, -13623699},
- {31740600, -14220199},
- {31427101, -14763399},
- {31007400, -15229400},
- {30500000, -15598100},
- {29927101, -15853200},
- {29313598, -15983600},
- {29000000, -16000000},
- {-28000000, -16000000},
- {-29313585, -15983600},
- {-29927051, -15853200},
- {-30500000, -15598100},
- {-31007389, -15229400},
- {-31427049, -14763399},
- {-31740640, -14220199},
- {-31934440, -13623699},
- {-32000000, -13000000},
- {-32000000, 10000000},
- },
- {
- {-36133789, -46431022},
- {-36040100, -46171817},
- {-35852722, -45653411},
- {2200073, 59616485},
- {12112792, 87039184},
- {14274505, 93019332},
- {14382049, 93291641},
- {14508483, 93563430},
- {14573425, 93688369},
- {14654052, 93832443},
- {14818634, 94096328},
- {14982757, 94327621},
- {15001708, 94352630},
- {15202392, 94598999},
- {15419342, 94833160},
- {15497497, 94910552},
- {15650848, 95053039},
- {15894866, 95256866},
- {16104309, 95412185},
- {16149047, 95443206},
- {16410888, 95611038},
- {16677795, 95759750},
- {16782348, 95812332},
- {16947143, 95889144},
- {17216400, 95999465},
- {17483123, 96091293},
- {17505554, 96098251},
- {17745178, 96165542},
- {18000671, 96223373},
- {18245880, 96265884},
- {18484039, 96295257},
- {18976715, 96319580},
- {31135131, 96319580},
- {31697082, 96287902},
- {31746368, 96282104},
- {32263000, 96190719},
- {32338623, 96172576},
- {32821411, 96026641},
- {32906188, 95995391},
- {33360565, 95797012},
- {33443420, 95754882},
- {33869171, 95505874},
- {33900756, 95485122},
- {34136413, 95318618},
- {34337127, 95159790},
- {34377288, 95125930},
- {34619628, 94905410},
- {34756286, 94767364},
- {34859008, 94656143},
- {35090606, 94378067},
- {35120849, 94338546},
- {35309295, 94072113},
- {35434875, 93871475},
- {35510070, 93740310},
- {35688232, 93385772},
- {35699096, 93361679},
- {35839782, 93012557},
- {35905487, 92817459},
- {35961578, 92625488},
- {36048004, 92249023},
- {36051574, 92229934},
- {36108856, 91831405},
- {36122985, 91667816},
- {36133789, 91435317},
- {36129669, 91085830},
- {36127685, 91046661},
- {36092742, 90669830},
- {36069946, 90514739},
- {36031829, 90308425},
- {35948211, 89965225},
- {34482635, 84756820},
- {27911407, 61403976},
- {-5872558, -58657440},
- {-14243621, -88406509},
- {-14576812, -89590599},
- {-15421997, -92594200},
- {-15657684, -93431732},
- {-16038940, -93720520},
- {-16420196, -94009307},
- {-17182708, -94586875},
- {-18834838, -95838272},
- {-19470275, -96319580},
- {-21368133, -96319580},
- {-22763854, -96319534},
- {-29742462, -96319274},
- {-32533935, -96319168},
- {-36133789, -54619018},
- {-36133789, -46431022},
- },
- {
- {-26000000, 25500000},
- {-6500000, 45000000},
- {17499998, 45000000},
- {23966310, 38533699},
- {26000000, 36500000},
- {26000000, -19000000},
- {25950000, -24500000},
- {17000000, -42214698},
- {14300000, -45000000},
- {-14299999, -45000000},
- {-17500000, -41714698},
- {-23400001, -24500000},
- {-26000000, -10464000},
- {-26000000, 25500000},
- },
- {
- {-26000000, 16636100},
- {-25072200, 18777799},
- {-16500000, 35299999},
- {-15050000, 36750000},
- {13550000, 36750000},
- {15000000, 35299999},
- {26000000, 16045200},
- {26000000, -2750000},
- {16500000, -34507900},
- {14840600, -36167301},
- {14257900, -36750000},
- {-14257900, -36750000},
- {-16500000, -34507900},
- {-26000000, -2750000},
- {-26000000, 16636100},
- },
- {
- {-18062349, 18950099},
- {4644938, 20049900},
- {6230361, 20049900},
- {7803279, 19851200},
- {9338899, 19456899},
- {10812990, 18873300},
- {12202310, 18109500},
- {13484951, 17177600},
- {14640670, 16092300},
- {15651250, 14870700},
- {16500749, 13532100},
- {17175849, 12097599},
- {17665750, 10589700},
- {17962850, 9032400},
- {18062349, 7450099},
- {17962850, 5867799},
- {15810750, -11007740},
- {15683750, -11727769},
- {15506849, -12437200},
- {15280929, -13132559},
- {15007040, -13810470},
- {14686531, -14467609},
- {14320949, -15100799},
- {13912099, -15706950},
- {13461959, -16283100},
- {12972730, -16826450},
- {12446790, -17334339},
- {11886699, -17804309},
- {11295190, -18234069},
- {10675149, -18621520},
- {10029590, -18964771},
- {9361650, -19262149},
- {8674600, -19512220},
- {7971780, -19713699},
- {7256609, -19865798},
- {6532589, -19967498},
- {5803222, -20018501},
- {5437650, -20024900},
- {-1062349, -20049900},
- {-16562349, -20049900},
- {-18062349, -18549900},
- {-18062349, 18950099},
- },
- {
- {-18062349, 41299900},
- {-1062349, 41299900},
- {15280929, -8117440},
- {15506849, -8812799},
- {15683750, -9522230},
- {15810750, -10242259},
- {17962850, -27117799},
- {18062349, -28700099},
- {17962850, -30282400},
- {17665750, -31839700},
- {17175849, -33347599},
- {16500749, -34782100},
- {15651250, -36120700},
- {14640670, -37342300},
- {13484951, -38427600},
- {12202310, -39359500},
- {10812990, -40123298},
- {9338899, -40706901},
- {7803279, -41101200},
- {6230361, -41299900},
- {4644938, -41299900},
- {-18062349, -40200099},
- {-18062349, 41299900},
- },
- {
- {-11750000, 13057900},
- {-9807860, 15000000},
- {4392139, 24000000},
- {11750000, 24000000},
- {11750000, -24000000},
- {4392139, -24000000},
- {-9807860, -15000000},
- {-11750000, -13057900},
- {-11750000, 13057900},
- },
- {
- {-12500000, 17500000},
- {12500000, 17500000},
- {12500000, -17500000},
- {-12500000, -17500000},
- {-12500000, 17500000},
- },
- {
- {-23500000, 11500000},
- {-13857859, 21000000},
- {-11000000, 21000000},
- {18500000, 500000},
- {23500000, -4500000},
- {23500000, -19500000},
- {22000000, -21000000},
- {-23500000, -21000000},
- {-23500000, 11500000},
- },
- {
- {-13000000, 5250000},
- {-4000000, 6750000},
- {4000000, 6750000},
- {13000000, 5250000},
- {13000000, 838459},
- {11376299, -1973939},
- {10350899, -3750000},
- {8618800, -6750000},
- {-8498290, -6750000},
- {-13000000, 1047180},
- {-13000000, 5250000},
- },
- {
- {-25000000, 50500000},
- {-21500000, 54000000},
- {18286800, 54000000},
- {25000000, 47286800},
- {25000000, -47286800},
- {18286800, -54000000},
- {-21500000, -54000000},
- {-25000000, -50500000},
- {-25000000, 50500000},
- },
- {
- {-19000000, 46000000},
- {-16799999, 46000000},
- {14000000, 34000000},
- {19000000, 29000000},
- {19000000, -29000000},
- {14000000, -34000000},
- {-16799999, -46000000},
- {-19000000, -46000000},
- {-19000000, 46000000},
- },
- {
- {-7956170, 836226},
- {-7825180, 1663290},
- {-7767529, 1914530},
- {-7608449, 2472140},
- {-7308360, 3253890},
- {-7083650, 3717780},
- {-6928199, 4000000},
- {-6472139, 4702280},
- {-5988090, 5304979},
- {-5945159, 5353040},
- {-5353040, 5945159},
- {-4702280, 6472139},
- {-4544519, 6583869},
- {-4000000, 6928199},
- {-3253890, 7308360},
- {-2836839, 7480130},
- {-2472140, 7608449},
- {-1663290, 7825180},
- {-964293, 7941669},
- {-836226, 7956170},
- {0, 8000000},
- {836226, 7956170},
- {964293, 7941669},
- {1663290, 7825180},
- {2472140, 7608449},
- {2836839, 7480130},
- {3253890, 7308360},
- {4000000, 6928199},
- {4544519, 6583869},
- {4702280, 6472139},
- {5353040, 5945159},
- {5945159, 5353040},
- {5988090, 5304979},
- {6472139, 4702280},
- {6928199, 4000000},
- {7083650, 3717780},
- {7308360, 3253890},
- {7608449, 2472140},
- {7767529, 1914530},
- {7825180, 1663290},
- {7956170, 836226},
- {8000000, 0},
- {7956170, -836226},
- {7825180, -1663290},
- {7767529, -1914530},
- {7608449, -2472140},
- {7308360, -3253890},
- {7083650, -3717780},
- {6928199, -4000000},
- {6472139, -4702280},
- {5988090, -5304979},
- {5945159, -5353040},
- {5353040, -5945159},
- {4702280, -6472139},
- {4544519, -6583869},
- {4000000, -6928199},
- {3253890, -7308360},
- {2836839, -7480130},
- {2472140, -7608449},
- {1663290, -7825180},
- {964293, -7941669},
- {836226, -7956170},
- {0, -8000000},
- {-836226, -7956170},
- {-964293, -7941669},
- {-1663290, -7825180},
- {-2472140, -7608449},
- {-2836839, -7480130},
- {-3253890, -7308360},
- {-4000000, -6928199},
- {-4544519, -6583869},
- {-4702280, -6472139},
- {-5353040, -5945159},
- {-5945159, -5353040},
- {-5988090, -5304979},
- {-6472139, -4702280},
- {-6928199, -4000000},
- {-7083650, -3717780},
- {-7308360, -3253890},
- {-7608449, -2472140},
- {-7767529, -1914530},
- {-7825180, -1663290},
- {-7956170, -836226},
- {-8000000, 0},
- {-7956170, 836226},
- },
-};
-
-const TestData STEGOSAUR_POLYGONS =
-{
- {
- {113210205, 107034095},
- {113561798, 109153793},
- {113750099, 109914001},
- {114396499, 111040199},
- {114599197, 111321998},
- {115570404, 112657096},
- {116920097, 114166595},
- {117630599, 114609390},
- {119703704, 115583900},
- {120559494, 115811996},
- {121045410, 115754493},
- {122698097, 115526496},
- {123373001, 115370193},
- {123482406, 115315689},
- {125664199, 114129798},
- {125920303, 113968193},
- {128551208, 111866195},
- {129075592, 111443199},
- {135044692, 106572608},
- {135254898, 106347694},
- {135415100, 106102897},
- {136121704, 103779891},
- {136325103, 103086303},
- {136690093, 101284896},
- {136798309, 97568496},
- {136798309, 97470397},
- {136787399, 97375297},
- {136753295, 97272102},
- {136687988, 97158699},
- {136539794, 96946899},
- {135526702, 95550994},
- {135388488, 95382293},
- {135272491, 95279098},
- {135214904, 95250595},
- {135122894, 95218002},
- {134966705, 95165191},
- {131753997, 94380798},
- {131226806, 94331001},
- {129603393, 94193893},
- {129224197, 94188003},
- {127874107, 94215103},
- {126812797, 94690200},
- {126558197, 94813896},
- {118361801, 99824195},
- {116550796, 101078796},
- {116189704, 101380493},
- {114634002, 103027999},
- {114118103, 103820297},
- {113399200, 105568000},
- {113201705, 106093597},
- {113210205, 107034095},
- },
- {
- {77917999, 130563003},
- {77926300, 131300903},
- {77990196, 132392700},
- {78144195, 133328002},
- {78170593, 133427093},
- {78235900, 133657592},
- {78799598, 135466705},
- {78933296, 135832397},
- {79112899, 136247604},
- {79336303, 136670898},
- {79585197, 137080596},
- {79726303, 137309005},
- {79820297, 137431900},
- {79942199, 137549407},
- {90329193, 145990203},
- {90460197, 146094390},
- {90606399, 146184509},
- {90715194, 146230010},
- {90919601, 146267211},
- {142335296, 153077697},
- {143460296, 153153594},
- {143976593, 153182189},
- {145403991, 153148605},
- {145562301, 153131195},
- {145705993, 153102905},
- {145938796, 153053192},
- {146134094, 153010101},
- {146483184, 152920196},
- {146904693, 152806396},
- {147180099, 152670196},
- {147357788, 152581695},
- {147615295, 152423095},
- {147782287, 152294708},
- {149281799, 150908386},
- {149405303, 150784912},
- {166569305, 126952499},
- {166784301, 126638099},
- {166938491, 126393699},
- {167030899, 126245101},
- {167173004, 126015899},
- {167415298, 125607200},
- {167468292, 125504699},
- {167553100, 125320899},
- {167584594, 125250694},
- {167684997, 125004394},
- {167807098, 124672401},
- {167938995, 124255203},
- {168052307, 123694000},
- {170094100, 112846900},
- {170118408, 112684204},
- {172079101, 88437797},
- {172082000, 88294403},
- {171916290, 82827606},
- {171911590, 82705703},
- {171874893, 82641906},
- {169867004, 79529907},
- {155996795, 58147998},
- {155904998, 58066299},
- {155864791, 58054199},
- {134315704, 56830902},
- {134086486, 56817901},
- {98200096, 56817798},
- {97838195, 56818599},
- {79401695, 56865097},
- {79291297, 56865501},
- {79180694, 56869499},
- {79058799, 56885097},
- {78937301, 56965301},
- {78324691, 57374599},
- {77932998, 57638401},
- {77917999, 57764297},
- {77917999, 130563003},
- },
- {
- {75566848, 109289947},
- {75592651, 109421951},
- {75644248, 109534446},
- {95210548, 141223846},
- {95262649, 141307449},
- {95487854, 141401443},
- {95910850, 141511642},
- {96105651, 141550338},
- {106015045, 142803451},
- {106142852, 142815155},
- {166897460, 139500244},
- {167019348, 139484741},
- {168008239, 138823043},
- {168137542, 138735153},
- {168156250, 138616851},
- {173160751, 98882049},
- {174381546, 87916046},
- {174412246, 87579048},
- {174429443, 86988746},
- {174436141, 86297348},
- {174438949, 84912048},
- {174262939, 80999145},
- {174172546, 80477546},
- {173847549, 79140846},
- {173623840, 78294349},
- {173120239, 76485046},
- {173067138, 76300544},
- {173017852, 76137542},
- {172941543, 75903045},
- {172892547, 75753143},
- {172813537, 75533348},
- {172758453, 75387046},
- {172307556, 74196746},
- {171926544, 73192848},
- {171891448, 73100448},
- {171672546, 72524147},
- {171502441, 72085144},
- {171414459, 71859146},
- {171294250, 71552352},
- {171080139, 71019744},
- {171039245, 70928146},
- {170970550, 70813346},
- {170904235, 70704040},
- {170786254, 70524353},
- {168063247, 67259048},
- {167989547, 67184844},
- {83427947, 67184844},
- {78360847, 67201248},
- {78238845, 67220550},
- {78151550, 67350547},
- {77574554, 68220550},
- {77494949, 68342651},
- {77479949, 68464546},
- {75648345, 106513351},
- {75561050, 109165740},
- {75566848, 109289947},
- },
- {
- {75619415, 108041595},
- {83609863, 134885772},
- {83806945, 135450820},
- {83943908, 135727371},
- {84799934, 137289794},
- {86547897, 140033782},
- {86674118, 140192962},
- {86810661, 140364715},
- {87045211, 140619918},
- {88187042, 141853240},
- {93924575, 147393783},
- {94058013, 147454803},
- {111640083, 153754562},
- {111762550, 153787933},
- {111975250, 153835311},
- {112127426, 153842803},
- {116797996, 154005157},
- {116969688, 154010681},
- {117141731, 154005935},
- {117333145, 153988037},
- {118007507, 153919952},
- {118159675, 153902130},
- {118931480, 153771942},
- {120878150, 153379089},
- {121172164, 153319259},
- {122074508, 153034362},
- {122260681, 152970367},
- {122313438, 152949584},
- {130755096, 149423736},
- {130996063, 149316818},
- {138893524, 144469665},
- {138896423, 144466918},
- {169883666, 97686134},
- {170115036, 96518981},
- {170144317, 96365257},
- {174395645, 67672065},
- {174396560, 67664222},
- {174288452, 66839241},
- {174170364, 66096923},
- {174112731, 65952033},
- {174021377, 65823486},
- {173948608, 65743225},
- {173863830, 65654769},
- {170408340, 63627494},
- {170004867, 63394714},
- {169585632, 63194389},
- {169441162, 63137046},
- {168944274, 62952133},
- {160605072, 60214218},
- {160331573, 60126396},
- {159674743, 59916877},
- {150337249, 56943778},
- {150267730, 56922073},
- {150080139, 56864868},
- {149435333, 56676422},
- {149310241, 56640579},
- {148055419, 56285041},
- {147828796, 56230949},
- {147598205, 56181800},
- {147149963, 56093917},
- {146834457, 56044700},
- {146727966, 56028717},
- {146519729, 56004882},
- {146328521, 55989326},
- {146170684, 55990036},
- {146151321, 55990745},
- {145800170, 56003616},
- {145639526, 56017753},
- {145599426, 56022491},
- {145481338, 56039184},
- {145389556, 56052757},
- {145325134, 56062591},
- {145176574, 56086135},
- {145017272, 56113922},
- {107163085, 63504539},
- {101013870, 65454101},
- {100921798, 65535285},
- {95362182, 74174079},
- {75652366, 107803443},
- {75635391, 107834983},
- {75628814, 107853294},
- {75603431, 107933692},
- {75619415, 108041595},
- },
- {
- {83617141, 120264900},
- {84617370, 126416427},
- {84648635, 126601341},
- {84693695, 126816085},
- {84762496, 127082641},
- {84772140, 127117034},
- {84860748, 127391693},
- {84927398, 127550239},
- {85072967, 127789642},
- {85155151, 127908851},
- {86745422, 130042907},
- {86982666, 130317489},
- {89975143, 133230743},
- {90091384, 133338500},
- {96260833, 138719818},
- {96713928, 139103668},
- {98139297, 140307388},
- {102104766, 143511505},
- {102142089, 143536468},
- {102457626, 143735107},
- {103386764, 144312988},
- {103845001, 144579177},
- {104139175, 144737136},
- {104551254, 144932250},
- {104690155, 144985778},
- {104844238, 145010009},
- {105020034, 145010375},
- {128999633, 144082305},
- {129096542, 144076141},
- {133932327, 143370178},
- {134130615, 143326751},
- {134281250, 143289520},
- {135247116, 142993438},
- {150774948, 137828704},
- {150893478, 137786178},
- {151350921, 137608901},
- {159797760, 134318115},
- {159979827, 134244384},
- {159988128, 134240997},
- {160035186, 134221633},
- {160054962, 134211486},
- {160168762, 134132736},
- {160181228, 134121047},
- {160336425, 133961502},
- {160689147, 133564331},
- {161446258, 132710739},
- {163306427, 130611648},
- {164845474, 128873855},
- {165270233, 128393600},
- {165281478, 128380706},
- {165300598, 128358673},
- {165303497, 128355194},
- {166411590, 122772674},
- {166423767, 122708648},
- {164745605, 66237312},
- {164740341, 66193061},
- {164721755, 66082092},
- {164721160, 66078750},
- {164688476, 65914146},
- {164668426, 65859436},
- {164563110, 65765937},
- {164431152, 65715034},
- {163997619, 65550788},
- {163946426, 65531440},
- {162998107, 65173629},
- {162664978, 65049140},
- {162482696, 64991668},
- {162464660, 64989639},
- {148029083, 66896141},
- {147862396, 66932853},
- {130087829, 73341102},
- {129791564, 73469726},
- {100590927, 90307685},
- {100483535, 90373847},
- {100364990, 90458930},
- {96447448, 93276664},
- {95179656, 94189010},
- {93692718, 95260208},
- {87904327, 99430885},
- {87663711, 99606147},
- {87576202, 99683990},
- {87498199, 99801719},
- {85740264, 104173728},
- {85538925, 104710494},
- {84786132, 107265830},
- {84635955, 107801383},
- {84619506, 107868064},
- {84518463, 108287200},
- {84456848, 108613471},
- {84419158, 108826194},
- {84375244, 109093818},
- {84329818, 109435180},
- {84249862, 110179664},
- {84218429, 110572166},
- {83630020, 117995208},
- {83595535, 118787673},
- {83576217, 119290679},
- {83617141, 120264900},
- },
- {
- {91735549, 117640846},
- {91748252, 117958145},
- {91823547, 118515449},
- {92088752, 119477249},
- {97995346, 140538452},
- {98031051, 140660446},
- {98154449, 141060241},
- {98179855, 141133758},
- {98217056, 141232849},
- {98217147, 141233047},
- {98269256, 141337051},
- {98298950, 141387954},
- {98337753, 141445755},
- {99455047, 142984451},
- {99656250, 143247344},
- {102567855, 146783752},
- {102685150, 146906845},
- {102828948, 147031250},
- {102972457, 147120452},
- {103676147, 147539642},
- {103758956, 147586151},
- {103956756, 147682144},
- {104479949, 147931457},
- {104744453, 148044143},
- {104994750, 148123443},
- {105375648, 148158645},
- {109266250, 148178253},
- {109447753, 148169052},
- {109693649, 148129150},
- {113729949, 147337448},
- {113884552, 147303054},
- {115155349, 146956146},
- {117637145, 146174346},
- {154694046, 134048049},
- {156979949, 133128555},
- {157076843, 133059356},
- {157125045, 133001449},
- {157561340, 132300750},
- {157865753, 131795959},
- {157923156, 131667358},
- {158007049, 131297653},
- {158112747, 130777053},
- {158116653, 130640853},
- {158268951, 119981643},
- {158260040, 119824752},
- {158229949, 119563751},
- {149914047, 73458648},
- {149877548, 73331748},
- {144460754, 66413558},
- {144230545, 66153152},
- {144128051, 66075057},
- {143974853, 65973152},
- {142812744, 65353149},
- {141810943, 64837249},
- {141683349, 64805152},
- {141505157, 64784652},
- {108214355, 61896251},
- {107826354, 61866352},
- {107072151, 61821750},
- {106938850, 61873550},
- {106584251, 62055152},
- {106419952, 62147548},
- {100459152, 65546951},
- {100343849, 65615150},
- {100198852, 65716949},
- {99825149, 65979751},
- {94619247, 70330352},
- {94492355, 70480850},
- {94445846, 70547355},
- {94425354, 70588752},
- {94379753, 70687652},
- {94110252, 71443450},
- {94095252, 71569053},
- {91737251, 117308746},
- {91731048, 117430946},
- {91735549, 117640846},
- },
- {
- {108231399, 111763748},
- {108335403, 111927955},
- {108865203, 112754745},
- {109206703, 113283851},
- {127117500, 125545951},
- {127212097, 125560951},
- {127358497, 125563652},
- {131348007, 125551147},
- {131412002, 125550849},
- {131509506, 125535446},
- {131579391, 125431343},
- {132041000, 124735656},
- {132104690, 124637847},
- {144108505, 100950546},
- {144120605, 100853042},
- {144123291, 100764648},
- {144122695, 100475143},
- {144086898, 85637748},
- {144083602, 85549346},
- {144071105, 85451843},
- {144007003, 85354545},
- {143679595, 84864547},
- {143468597, 84551048},
- {143367889, 84539146},
- {109847702, 84436347},
- {109684700, 84458953},
- {105946502, 89406143},
- {105915901, 91160446},
- {105880905, 93187744},
- {105876701, 93441345},
- {108231399, 111763748},
- },
- {
- {102614700, 117684249},
- {102675102, 118074157},
- {102888999, 118743148},
- {103199707, 119517555},
- {103446800, 120099655},
- {103488204, 120193450},
- {104063903, 121373947},
- {104535499, 122192245},
- {104595802, 122295249},
- {104663002, 122402854},
- {104945701, 122854858},
- {105740501, 124038848},
- {106809700, 125479354},
- {107564399, 126380050},
- {108116203, 126975646},
- {123724700, 142516540},
- {124938400, 143705444},
- {127919601, 146599243},
- {128150894, 146821456},
- {128251602, 146917251},
- {128383605, 147041839},
- {128527709, 147176147},
- {128685699, 147321456},
- {128861007, 147481246},
- {132825103, 151046661},
- {133005493, 151205657},
- {133389007, 151488143},
- {133896499, 151858062},
- {134172302, 151991546},
- {134375000, 152063140},
- {135316101, 152300949},
- {136056304, 152220947},
- {136242706, 152186843},
- {136622207, 152016448},
- {136805404, 151908355},
- {147099594, 145766845},
- {147246704, 144900756},
- {147387603, 144048461},
- {144353698, 99345855},
- {144333801, 99232254},
- {144244598, 98812850},
- {144228698, 98757858},
- {144174606, 98616455},
- {133010101, 72396743},
- {132018905, 70280853},
- {130667404, 67536949},
- {129167297, 64854446},
- {128569198, 64098350},
- {124458503, 59135948},
- {124260597, 58946949},
- {123908706, 58658851},
- {123460098, 58327850},
- {122674499, 57840648},
- {122041801, 57712150},
- {121613403, 57699047},
- {121359901, 57749351},
- {121123199, 57826450},
- {120953498, 57882247},
- {120431701, 58198547},
- {120099205, 58599349},
- {119892303, 58903049},
- {102835296, 115179351},
- {102686599, 115817245},
- {102612396, 116540557},
- {102614700, 117684249},
- },
- {
- {98163757, 71203430},
- {98212463, 73314544},
- {98326538, 74432693},
- {98402908, 75169799},
- {98524154, 76328353},
- {99088806, 79911361},
- {99304885, 80947769},
- {100106689, 84244186},
- {100358123, 85080337},
- {101715545, 89252807},
- {101969528, 89987213},
- {107989440, 106391418},
- {126299575, 140277343},
- {127061813, 141486663},
- {127405746, 141872253},
- {127846908, 142318450},
- {130818496, 145301574},
- {134366424, 148100921},
- {135308380, 148798828},
- {135745666, 149117523},
- {136033020, 149251800},
- {136500579, 149387725},
- {136662719, 149418395},
- {136973922, 149474822},
- {137184890, 149484375},
- {137623748, 149434356},
- {137830810, 149355072},
- {138681732, 148971343},
- {139374465, 148463409},
- {139589187, 148264312},
- {139809707, 148010711},
- {139985610, 147685028},
- {140196029, 147284973},
- {140355834, 146978668},
- {142079666, 142575622},
- {146702194, 129469726},
- {151285888, 113275238},
- {151543731, 112046264},
- {151701629, 110884704},
- {151837020, 108986206},
- {151837097, 107724029},
- {151760101, 106529205},
- {151581970, 105441925},
- {151577301, 105413757},
- {151495269, 105014709},
- {151393142, 104551513},
- {151058502, 103296112},
- {150705520, 102477264},
- {150137725, 101686370},
- {149427032, 100938537},
- {102979965, 60772064},
- {101930953, 60515609},
- {101276748, 60634414},
- {100717803, 60918136},
- {100125732, 61584625},
- {99618148, 62413436},
- {99457214, 62709442},
- {99368347, 62914794},
- {99166992, 63728332},
- {98313827, 69634780},
- {98176910, 70615707},
- {98162902, 70798233},
- {98163757, 71203430},
- },
- {
- {79090698, 116426399},
- {80959800, 137087692},
- {81030303, 137762298},
- {81190704, 138903503},
- {81253700, 139084197},
- {81479301, 139544998},
- {81952003, 140118896},
- {82319900, 140523895},
- {82967803, 140993896},
- {83022903, 141032104},
- {83777900, 141493606},
- {84722099, 141849899},
- {84944396, 141887207},
- {86144699, 141915893},
- {87643997, 141938095},
- {88277503, 141887695},
- {88582099, 141840606},
- {89395401, 141712203},
- {90531204, 141528396},
- {91014801, 141438400},
- {92097595, 141190093},
- {123348297, 132876998},
- {123399505, 132860000},
- {123452804, 132841506},
- {123515502, 132818908},
- {123543800, 132806198},
- {124299598, 132437393},
- {124975502, 132042098},
- {125047500, 131992202},
- {125119506, 131930603},
- {166848800, 86317703},
- {168976409, 83524902},
- {169359603, 82932701},
- {169852600, 81917800},
- {170686904, 79771202},
- {170829406, 79245597},
- {170885498, 78796295},
- {170909301, 78531898},
- {170899703, 78238700},
- {170842803, 77553199},
- {170701293, 76723495},
- {170302307, 75753898},
- {169924301, 75067398},
- {169359802, 74578796},
- {168148605, 73757499},
- {163261596, 71124702},
- {162986007, 70977798},
- {162248703, 70599098},
- {158193405, 68923995},
- {157514297, 68667495},
- {156892700, 68495201},
- {156607299, 68432998},
- {154301895, 68061904},
- {93440299, 68061904},
- {88732002, 68255996},
- {88627304, 68298500},
- {88111396, 68541900},
- {86393898, 69555404},
- {86138298, 69706695},
- {85871704, 69913200},
- {85387199, 70393402},
- {79854499, 76783203},
- {79209701, 77649398},
- {79108505, 78072502},
- {79090698, 78472198},
- {79090698, 116426399},
- },
- {
- {90956314, 84639938},
- {91073814, 85141891},
- {91185752, 85505371},
- {109815368, 137196487},
- {110342590, 138349899},
- {110388549, 138447540},
- {110652862, 138971343},
- {110918045, 139341140},
- {114380859, 143159042},
- {114446723, 143220352},
- {114652198, 143392166},
- {114712196, 143437301},
- {114782165, 143476028},
- {114873054, 143514923},
- {115217086, 143660934},
- {115306060, 143695526},
- {115344009, 143707580},
- {115444541, 143737747},
- {115589378, 143779937},
- {115751358, 143823989},
- {115802780, 143825820},
- {116872810, 143753616},
- {116927055, 143744644},
- {154690734, 133504180},
- {155009704, 133371856},
- {155029907, 133360061},
- {155089141, 133323181},
- {155342315, 133163360},
- {155602294, 132941406},
- {155669158, 132880294},
- {155821624, 132737884},
- {155898986, 132656890},
- {155934936, 132608932},
- {155968627, 132562713},
- {156062896, 132431808},
- {156111694, 132363174},
- {156148147, 132297180},
- {158738342, 127281066},
- {159026672, 126378631},
- {159073699, 125806335},
- {159048522, 125299743},
- {159040313, 125192901},
- {158898300, 123934677},
- {149829376, 70241508},
- {149763031, 69910629},
- {149684692, 69628723},
- {149557800, 69206214},
- {149366485, 68864326},
- {149137390, 68578514},
- {148637466, 68048767},
- {147027725, 66632934},
- {146228607, 66257507},
- {146061309, 66184646},
- {146017929, 66174186},
- {145236465, 66269500},
- {144802490, 66345039},
- {144673995, 66376220},
- {93732284, 79649864},
- {93345336, 79785865},
- {93208084, 79840286},
- {92814521, 79997779},
- {92591087, 80098968},
- {92567016, 80110511},
- {92032684, 80860725},
- {91988853, 80930152},
- {91471725, 82210029},
- {91142349, 83076683},
- {90969284, 83653182},
- {90929664, 84043212},
- {90926315, 84325256},
- {90956314, 84639938},
- },
- {
- {114758499, 88719909},
- {114771591, 88860549},
- {115515533, 94195907},
- {115559539, 94383651},
- {119882980, 109502059},
- {120660522, 111909683},
- {126147735, 124949630},
- {127127212, 127107215},
- {129976379, 132117279},
- {130754470, 133257080},
- {130820968, 133340835},
- {130889312, 133423858},
- {131094787, 133652832},
- {131257629, 133828247},
- {131678619, 134164276},
- {131791107, 134248901},
- {131969482, 134335189},
- {132054107, 134373718},
- {132927368, 134701141},
- {133077072, 134749313},
- {133196075, 134785705},
- {133345230, 134804351},
- {133498809, 134809051},
- {133611541, 134797607},
- {134621170, 134565322},
- {134741165, 134527511},
- {134892089, 134465240},
- {135071212, 134353820},
- {135252029, 134185821},
- {135384979, 134003631},
- {135615585, 133576675},
- {135793029, 132859008},
- {135890228, 131382904},
- {135880828, 131261657},
- {135837570, 130787963},
- {135380661, 127428909},
- {132830596, 109495368},
- {132815826, 109411666},
- {132765869, 109199302},
- {132724380, 109068161},
- {127490066, 93353515},
- {125330810, 87852828},
- {125248336, 87647026},
- {125002182, 87088424},
- {124894592, 86872482},
- {121007278, 80019584},
- {120962829, 79941261},
- {120886489, 79833923},
- {120154983, 78949615},
- {119366561, 78111709},
- {119014755, 77776794},
- {116728790, 75636238},
- {116660522, 75593933},
- {116428192, 75458541},
- {116355255, 75416870},
- {116264663, 75372528},
- {115952728, 75233367},
- {115865554, 75205482},
- {115756835, 75190956},
- {115564163, 75197830},
- {115481170, 75202087},
- {115417144, 75230400},
- {115226959, 75337806},
- {115203842, 75351448},
- {114722015, 75746932},
- {114672103, 75795661},
- {114594619, 75891891},
- {114565811, 75973831},
- {114478256, 76240814},
- {114178039, 77252197},
- {114137664, 77769668},
- {114109771, 78154464},
- {114758499, 88719909},
- },
- {
- {108135070, 109828002},
- {108200347, 110091529},
- {108319419, 110298500},
- {108439025, 110488388},
- {108663574, 110766731},
- {108812957, 110935768},
- {109321914, 111398925},
- {109368087, 111430320},
- {109421295, 111466331},
- {110058998, 111849746},
- {127160308, 120588981},
- {127350692, 120683456},
- {128052749, 120997207},
- {128326919, 121113449},
- {131669586, 122213058},
- {131754745, 122240592},
- {131854583, 122264770},
- {132662048, 122449813},
- {132782669, 122449897},
- {132909118, 122443687},
- {133013442, 122436058},
- {140561035, 121609939},
- {140786346, 121583320},
- {140876144, 121570228},
- {140962356, 121547996},
- {141052612, 121517837},
- {141231292, 121442184},
- {141309371, 121390007},
- {141370132, 121327003},
- {141456008, 121219932},
- {141591598, 121045005},
- {141905761, 120634796},
- {141894607, 120305725},
- {141881881, 120110855},
- {141840881, 119885009},
- {141685043, 119238922},
- {141617416, 118962882},
- {141570434, 118858856},
- {131617462, 100598548},
- {131542846, 100487213},
- {131229385, 100089019},
- {131091476, 99928108},
- {119824127, 90297180},
- {119636337, 90142387},
- {119507492, 90037765},
- {119436744, 89983657},
- {119423942, 89974159},
- {119207366, 89822471},
- {119117149, 89767097},
- {119039489, 89726867},
- {116322929, 88522857},
- {114817031, 87882110},
- {114683975, 87826751},
- {114306411, 87728507},
- {113876434, 87646003},
- {113792106, 87629974},
- {113658988, 87615974},
- {113574333, 87609275},
- {112813575, 87550102},
- {112578567, 87560157},
- {112439880, 87571647},
- {112306922, 87599395},
- {112225082, 87622535},
- {112132568, 87667175},
- {112103477, 87682830},
- {110795242, 88511634},
- {110373565, 88847793},
- {110286537, 88934989},
- {109730873, 89531501},
- {109648735, 89628883},
- {109552581, 89768859},
- {109514228, 89838470},
- {109501640, 89877586},
- {109480964, 89941864},
- {109461761, 90032417},
- {109457778, 90055458},
- {108105194, 109452575},
- {108094238, 109620979},
- {108135070, 109828002},
- },
- {
- {108764694, 108910400},
- {108965499, 112306495},
- {109598602, 120388298},
- {110573898, 128289596},
- {110597801, 128427795},
- {113786201, 137983795},
- {113840301, 138134704},
- {113937202, 138326904},
- {114046005, 138520401},
- {114150802, 138696792},
- {114164703, 138717895},
- {114381896, 139021194},
- {114701004, 139425292},
- {114997398, 139747497},
- {115065597, 139805191},
- {115134498, 139850891},
- {115167098, 139871704},
- {115473396, 139992797},
- {115537498, 139995101},
- {116762596, 139832000},
- {116897499, 139808593},
- {118401802, 139225585},
- {118437500, 139209594},
- {118488204, 139182189},
- {118740097, 139033996},
- {118815795, 138967285},
- {134401000, 116395492},
- {134451507, 116309997},
- {135488098, 113593597},
- {137738006, 106775695},
- {140936492, 97033889},
- {140960006, 96948997},
- {141026504, 96660995},
- {141067291, 96467094},
- {141124893, 95771896},
- {141511795, 90171600},
- {141499801, 90026000},
- {141479598, 89907798},
- {141276794, 88844596},
- {141243804, 88707397},
- {140778305, 87031593},
- {140733306, 86871696},
- {140697204, 86789993},
- {140619796, 86708190},
- {140398391, 86487396},
- {125798797, 72806198},
- {125415802, 72454498},
- {123150398, 70566093},
- {123038803, 70503997},
- {122681198, 70305397},
- {121919204, 70104797},
- {121533699, 70008094},
- {121273696, 70004898},
- {121130599, 70020797},
- {121045097, 70033294},
- {120847099, 70082298},
- {120481895, 70278999},
- {120367004, 70379692},
- {120272796, 70475097},
- {119862098, 71004791},
- {119745101, 71167297},
- {119447799, 71726997},
- {119396499, 71825798},
- {119348701, 71944496},
- {109508796, 98298797},
- {109368598, 98700897},
- {109298400, 98926391},
- {108506301, 102750991},
- {108488197, 102879898},
- {108764694, 108910400},
- },
- {
- {106666252, 87231246},
- {106673248, 87358055},
- {107734146, 101975646},
- {107762649, 102357955},
- {108702445, 111208351},
- {108749450, 111345153},
- {108848350, 111542648},
- {110270645, 114264358},
- {110389648, 114445144},
- {138794845, 143461151},
- {139048355, 143648956},
- {139376144, 143885345},
- {139594451, 144022644},
- {139754043, 144110046},
- {139923950, 144185852},
- {140058242, 144234451},
- {140185653, 144259552},
- {140427551, 144292648},
- {141130950, 144281448},
- {141157653, 144278152},
- {141214355, 144266555},
- {141347457, 144223449},
- {141625350, 144098953},
- {141755142, 144040145},
- {141878143, 143971557},
- {142011444, 143858154},
- {142076843, 143796356},
- {142160644, 143691055},
- {142224456, 143560852},
- {142925842, 142090850},
- {142935653, 142065353},
- {142995956, 141899154},
- {143042556, 141719757},
- {143102951, 141436157},
- {143129257, 141230453},
- {143316055, 139447250},
- {143342544, 133704650},
- {143307556, 130890960},
- {142461257, 124025558},
- {141916046, 120671051},
- {141890457, 120526153},
- {140002349, 113455749},
- {139909149, 113144149},
- {139853454, 112974456},
- {137303756, 105228057},
- {134700546, 98161254},
- {134617950, 97961547},
- {133823547, 96118057},
- {133688751, 95837356},
- {133481353, 95448059},
- {133205444, 94948150},
- {131178955, 91529853},
- {131144744, 91482055},
- {113942047, 67481246},
- {113837051, 67360549},
- {113048950, 66601745},
- {112305549, 66002746},
- {112030853, 65790351},
- {111970649, 65767547},
- {111912445, 65755249},
- {111854248, 65743453},
- {111657447, 65716354},
- {111576950, 65707351},
- {111509750, 65708549},
- {111443550, 65718551},
- {111397247, 65737449},
- {111338546, 65764648},
- {111129547, 65863349},
- {111112449, 65871551},
- {110995254, 65927856},
- {110968849, 65946151},
- {110941444, 65966751},
- {110836448, 66057853},
- {110490447, 66445449},
- {110404144, 66576751},
- {106802055, 73202148},
- {106741950, 73384948},
- {106715454, 73469650},
- {106678054, 73627151},
- {106657455, 75433448},
- {106666252, 87231246},
- },
- {
- {101852752, 106261352},
- {101868949, 106406051},
- {102347549, 108974250},
- {112286750, 152027954},
- {112305648, 152106536},
- {112325752, 152175857},
- {112391448, 152290863},
- {113558250, 154187454},
- {113592048, 154226745},
- {113694351, 154313156},
- {113736549, 154335647},
- {113818145, 154367462},
- {114284454, 154490951},
- {114415847, 154504547},
- {114520751, 154489151},
- {114571350, 154478057},
- {114594551, 154472854},
- {114630546, 154463958},
- {114715148, 154429443},
- {146873657, 136143051},
- {146941741, 136074249},
- {147190155, 135763549},
- {147262649, 135654937},
- {147309951, 135557159},
- {147702255, 133903945},
- {147934143, 131616348},
- {147967041, 131273864},
- {148185852, 127892250},
- {148195648, 127669754},
- {148179656, 126409851},
- {148119552, 126182151},
- {147874053, 125334152},
- {147818954, 125150352},
- {146958557, 122656646},
- {139070251, 101025955},
- {139002655, 100879051},
- {119028450, 63067649},
- {118846649, 62740753},
- {115676048, 57814651},
- {115550453, 57629852},
- {115330352, 57319751},
- {115094749, 56998352},
- {114978347, 56847454},
- {114853050, 56740550},
- {114695053, 56609550},
- {114582252, 56528148},
- {114210449, 56375953},
- {113636245, 56214950},
- {113470352, 56171649},
- {109580749, 55503551},
- {109491645, 55495452},
- {109238754, 55511550},
- {109080352, 55534049},
- {108027748, 55687351},
- {107839950, 55732349},
- {107614456, 55834953},
- {107488143, 55925952},
- {107302551, 56062553},
- {107218353, 56145751},
- {107199447, 56167251},
- {107052749, 56354850},
- {106978652, 56476348},
- {106869644, 56710754},
- {104541351, 62448753},
- {104454551, 62672554},
- {104441253, 62707351},
- {104231750, 63366348},
- {104222648, 63419952},
- {104155746, 63922649},
- {104127349, 64147552},
- {104110847, 64299957},
- {102235450, 92366752},
- {101804351, 102877655},
- {101852752, 106261352},
- },
- {
- {106808700, 120885696},
- {106818695, 120923103},
- {106873901, 121057098},
- {115123603, 133614700},
- {115128799, 133619598},
- {115182197, 133661804},
- {115330101, 133740707},
- {115455398, 133799407},
- {115595001, 133836807},
- {115651000, 133851806},
- {116413604, 134055206},
- {116654495, 134097900},
- {116887603, 134075210},
- {117071098, 134040405},
- {117458801, 133904891},
- {118057998, 133572601},
- {118546997, 133261001},
- {118578498, 133239395},
- {118818603, 133011596},
- {121109695, 130501495},
- {122661598, 128760101},
- {142458190, 102765197},
- {142789001, 102099601},
- {143105010, 101386505},
- {143154800, 101239700},
- {143193908, 100825500},
- {143160507, 100282501},
- {143133499, 100083602},
- {143092697, 99880500},
- {143050689, 99766700},
- {142657501, 98974502},
- {142580307, 98855201},
- {122267196, 76269897},
- {122036399, 76105003},
- {121832000, 76028305},
- {121688796, 75983108},
- {121591598, 75955001},
- {121119697, 75902099},
- {120789596, 75953498},
- {120487495, 76041900},
- {120042701, 76365798},
- {119886695, 76507301},
- {119774200, 76635299},
- {119739097, 76686904},
- {119685195, 76798202},
- {119456199, 77320098},
- {106877601, 119561401},
- {106854797, 119645103},
- {106849098, 119668807},
- {106847099, 119699005},
- {106840400, 119801406},
- {106807800, 120719299},
- {106806098, 120862808},
- {106808700, 120885696},
- },
- {
- {99663352, 105328948},
- {99690048, 105797050},
- {99714050, 105921447},
- {99867248, 106439949},
- {100111557, 107256546},
- {104924850, 120873649},
- {105106155, 121284049},
- {105519149, 122184753},
- {105586051, 122292655},
- {105665054, 122400154},
- {106064147, 122838455},
- {106755355, 123453453},
- {106929054, 123577651},
- {107230346, 123771949},
- {107760650, 123930648},
- {108875854, 124205154},
- {108978752, 124228050},
- {131962051, 123738754},
- {135636047, 123513954},
- {135837249, 123500747},
- {136357345, 123442749},
- {136577346, 123394454},
- {136686645, 123367752},
- {137399353, 123185050},
- {137733947, 123063156},
- {137895355, 122997154},
- {138275650, 122829154},
- {138394256, 122767753},
- {138516845, 122670150},
- {139987045, 121111251},
- {149171646, 108517349},
- {149274353, 108372848},
- {149314758, 108314247},
- {149428848, 108140846},
- {149648651, 107650550},
- {149779541, 107290252},
- {149833343, 107115249},
- {149891357, 106920051},
- {150246353, 105630249},
- {150285842, 105423454},
- {150320953, 105233749},
- {150336639, 104981552},
- {150298049, 104374053},
- {150287948, 104271850},
- {150026153, 103481147},
- {149945449, 103301651},
- {149888946, 103213455},
- {149800949, 103103851},
- {149781143, 103079650},
- {149714141, 103005447},
- {149589950, 102914146},
- {149206054, 102698951},
- {128843856, 91378150},
- {128641754, 91283050},
- {119699851, 87248046},
- {117503555, 86311950},
- {117145851, 86178054},
- {116323654, 85925048},
- {115982551, 85834045},
- {115853050, 85819252},
- {115222549, 85771949},
- {107169357, 85771949},
- {107122650, 85776451},
- {106637145, 85831550},
- {105095046, 86423950},
- {104507850, 86703750},
- {104384155, 86763153},
- {104332351, 86790145},
- {104198257, 86882644},
- {103913757, 87109451},
- {103592346, 87388450},
- {103272651, 87666748},
- {103198051, 87779052},
- {101698654, 90600952},
- {101523551, 90958450},
- {101360054, 91347450},
- {101295349, 91542144},
- {99774551, 98278152},
- {99746749, 98417755},
- {99704055, 98675453},
- {99663352, 99022949},
- {99663352, 105328948},
- },
- {
- {95036499, 101778106},
- {95479103, 102521301},
- {95587295, 102700103},
- {98306503, 106984901},
- {98573303, 107377700},
- {100622406, 110221702},
- {101252304, 111089599},
- {104669502, 115750198},
- {121838500, 131804107},
- {122000503, 131943695},
- {122176803, 132023406},
- {122474105, 132025390},
- {122703804, 132023101},
- {123278808, 131878112},
- {124072998, 131509109},
- {124466506, 131102508},
- {152779296, 101350906},
- {153016510, 101090606},
- {153269699, 100809097},
- {153731994, 100214096},
- {153927902, 99939796},
- {154641098, 98858100},
- {154864303, 98517601},
- {155056594, 97816604},
- {155083511, 97645599},
- {155084899, 97462097},
- {154682601, 94386100},
- {154376007, 92992599},
- {154198593, 92432403},
- {153830505, 91861701},
- {153686904, 91678695},
- {151907104, 90314605},
- {151368896, 89957603},
- {146983306, 87632202},
- {139082397, 84273605},
- {128947692, 80411399},
- {121179000, 78631301},
- {120264701, 78458198},
- {119279510, 78304603},
- {116913101, 77994102},
- {116151504, 77974601},
- {115435104, 78171401},
- {113544105, 78709106},
- {113231002, 78879898},
- {112726303, 79163604},
- {112310501, 79411102},
- {96169998, 97040802},
- {95196304, 98364402},
- {95167800, 98409599},
- {95083503, 98570701},
- {94986999, 99022201},
- {94915100, 100413299},
- {95036499, 101778106},
- },
- {
- {82601348, 96004745},
- {83443847, 128861953},
- {84173248, 136147354},
- {104268249, 141388839},
- {104373649, 141395355},
- {105686950, 141389541},
- {149002243, 140435653},
- {159095748, 133388244},
- {159488143, 133112655},
- {159661849, 132894653},
- {163034149, 128290847},
- {164801849, 124684249},
- {167405746, 72553245},
- {167330444, 71960746},
- {167255050, 71791847},
- {167147155, 71572044},
- {166999557, 71341545},
- {166723937, 70961448},
- {166238250, 70611541},
- {165782348, 70359649},
- {165649444, 70286849},
- {165332946, 70122344},
- {165164154, 70062248},
- {164879150, 69967544},
- {164744949, 69928947},
- {164691452, 69915245},
- {164669448, 69910247},
- {159249938, 68738952},
- {158528259, 68704742},
- {147564254, 68604644},
- {116196655, 68982742},
- {115364944, 69005050},
- {115193145, 69013549},
- {101701248, 70984146},
- {93918449, 72233047},
- {93789749, 72285247},
- {93777046, 72292648},
- {93586044, 72444046},
- {93366348, 72662345},
- {93301147, 72745452},
- {93260345, 72816345},
- {83523948, 92593849},
- {83430145, 92810241},
- {82815048, 94665542},
- {82755554, 94858551},
- {82722953, 95014350},
- {82594253, 95682350},
- {82601348, 96004745},
- },
- {
- {110371345, 125796493},
- {110411544, 126159599},
- {110445251, 126362899},
- {111201950, 127863800},
- {112030052, 129270492},
- {112367050, 129799301},
- {113088348, 130525604},
- {113418144, 130853698},
- {117363449, 134705505},
- {118131149, 135444793},
- {118307449, 135607299},
- {119102546, 136297195},
- {119385047, 136531906},
- {120080848, 137094390},
- {120794845, 137645401},
- {121150344, 137896392},
- {121528945, 138162506},
- {121644546, 138242095},
- {122142349, 138506408},
- {127540847, 141363006},
- {127933448, 141516204},
- {128728256, 141766799},
- {129877151, 141989898},
- {130626052, 142113891},
- {130912246, 142135192},
- {131246841, 142109100},
- {131496047, 142027404},
- {131596252, 141957794},
- {131696350, 141873504},
- {131741043, 141803405},
- {138788452, 128037704},
- {139628646, 125946197},
- {138319351, 112395401},
- {130035354, 78066703},
- {124174049, 69908798},
- {123970649, 69676895},
- {123874252, 69571899},
- {123246643, 68961303},
- {123193954, 68924400},
- {121952049, 68110000},
- {121787345, 68021896},
- {121661544, 67970306},
- {121313446, 67877502},
- {121010650, 67864799},
- {120995346, 67869705},
- {120583747, 68122207},
- {120509750, 68170600},
- {120485847, 68189102},
- {112160148, 77252403},
- {111128646, 78690704},
- {110969650, 78939407},
- {110512550, 79663406},
- {110397247, 79958206},
- {110371345, 80038299},
- {110371345, 125796493},
- },
- {
- {112163948, 137752700},
- {112171150, 137837997},
- {112203048, 137955993},
- {112240150, 138008209},
- {112343246, 138111099},
- {112556243, 138223205},
- {112937149, 138307998},
- {113318748, 138331909},
- {126076446, 138428298},
- {126165245, 138428695},
- {126312446, 138417907},
- {134075546, 136054504},
- {134322753, 135949401},
- {134649948, 135791198},
- {135234954, 135493408},
- {135290145, 135464691},
- {135326248, 135443695},
- {135920043, 135032592},
- {135993850, 134975799},
- {136244247, 134761199},
- {136649444, 134378692},
- {137067153, 133964294},
- {137188156, 133839096},
- {137298049, 133704498},
- {137318954, 133677795},
- {137413543, 133522201},
- {137687347, 133043792},
- {137816055, 132660705},
- {137836044, 131747695},
- {137807144, 131318603},
- {136279342, 119078704},
- {136249053, 118945800},
- {127306152, 81348602},
- {127114852, 81065505},
- {127034248, 80951400},
- {126971649, 80893707},
- {125093551, 79178001},
- {124935745, 79036003},
- {115573745, 71767601},
- {115411148, 71701805},
- {115191947, 71621002},
- {115017051, 71571304},
- {114870147, 71572898},
- {113869552, 71653900},
- {112863349, 72976104},
- {112756347, 73223899},
- {112498947, 73832206},
- {112429351, 73998504},
- {112366050, 74168098},
- {112273246, 74487098},
- {112239250, 74605400},
- {112195549, 74899902},
- {112163948, 75280700},
- {112163948, 137752700},
- },
- {
- {78562347, 141451843},
- {79335624, 142828186},
- {79610343, 143188140},
- {79845077, 143445724},
- {81379173, 145126678},
- {81826751, 145577178},
- {82519126, 146209472},
- {83964973, 147280502},
- {85471343, 148377868},
- {86115539, 148760803},
- {88839988, 150281188},
- {89021247, 150382217},
- {90775917, 151320526},
- {91711380, 151767288},
- {92757591, 152134277},
- {93241058, 152201766},
- {113402145, 153091995},
- {122065994, 146802825},
- {164111053, 91685104},
- {164812759, 90470565},
- {165640182, 89037384},
- {171027435, 66211853},
- {171450805, 64406951},
- {171463150, 64349624},
- {171469787, 64317184},
- {171475585, 64282028},
- {171479812, 64253036},
- {171483596, 64210433},
- {171484405, 64153488},
- {171483001, 64140785},
- {171481719, 64132751},
- {171478668, 64115478},
- {171472702, 64092437},
- {171462768, 64075408},
- {171448089, 64061347},
- {171060333, 63854789},
- {169640502, 63197738},
- {169342147, 63086711},
- {166413101, 62215766},
- {151881774, 58826736},
- {146010574, 57613151},
- {141776962, 56908004},
- {140982940, 57030628},
- {139246154, 57540817},
- {139209609, 57566974},
- {127545310, 66015594},
- {127476654, 66104812},
- {105799087, 98784980},
- {85531921, 129338897},
- {79319717, 138704513},
- {78548156, 140188079},
- {78530448, 140530456},
- {78515594, 141299987},
- {78562347, 141451843},
- },
- {
- {77755004, 128712387},
- {78073547, 130552612},
- {78433593, 132017822},
- {79752693, 136839645},
- {80479461, 138929260},
- {80903221, 140119674},
- {81789848, 141978454},
- {82447387, 143105575},
- {83288436, 144264328},
- {84593582, 145846542},
- {84971939, 146242813},
- {86905578, 147321304},
- {87874191, 147594131},
- {89249092, 147245132},
- {89541542, 147169052},
- {98759140, 144071609},
- {98894233, 144024261},
- {113607818, 137992843},
- {128324356, 131649307},
- {139610076, 126210189},
- {146999572, 122112884},
- {147119415, 122036041},
- {148717330, 120934616},
- {149114776, 120652725},
- {171640289, 92086624},
- {171677917, 92036224},
- {171721191, 91973869},
- {171851608, 91721557},
- {171927795, 91507644},
- {172398696, 89846351},
- {172436752, 89559959},
- {169361663, 64753852},
- {169349029, 64687164},
- {169115127, 63616458},
- {168965728, 63218254},
- {168911788, 63121219},
- {168901611, 63106807},
- {168896896, 63100486},
- {168890686, 63092460},
- {168876586, 63081058},
- {168855529, 63067909},
- {168808746, 63046024},
- {167251068, 62405864},
- {164291717, 63716899},
- {152661651, 69910156},
- {142312393, 75421356},
- {78778053, 111143295},
- {77887222, 113905914},
- {77591979, 124378433},
- {77563247, 126586669},
- {77755004, 128712387},
- },
- {
- {105954101, 131182754},
- {105959197, 131275848},
- {105972801, 131473556},
- {105981498, 131571044},
- {106077903, 132298553},
- {106134094, 132715255},
- {106155700, 132832351},
- {106180099, 132942657},
- {106326797, 133590347},
- {106375099, 133719345},
- {106417602, 133829345},
- {106471000, 133930343},
- {106707901, 134308654},
- {106728401, 134340545},
- {106778198, 134417556},
- {106832397, 134491851},
- {106891296, 134562957},
- {106981300, 134667358},
- {107044204, 134736557},
- {107111000, 134802658},
- {107180999, 134865661},
- {107291099, 134961349},
- {107362998, 135020355},
- {107485397, 135112854},
- {107558998, 135166946},
- {107690399, 135256256},
- {107765098, 135305252},
- {107903594, 135390548},
- {108183898, 135561843},
- {108459503, 135727951},
- {108532501, 135771850},
- {108796096, 135920059},
- {108944099, 135972549},
- {109102401, 136010757},
- {109660598, 136071044},
- {109971595, 136100250},
- {110209594, 136116851},
- {110752799, 136122344},
- {111059906, 136105758},
- {111152900, 136100357},
- {111237197, 136091354},
- {111316101, 136075057},
- {111402000, 136050949},
- {111475296, 136026657},
- {143546600, 123535949},
- {143899002, 122454353},
- {143917404, 122394348},
- {143929199, 122354652},
- {143944793, 122295753},
- {143956207, 122250953},
- {143969497, 122192253},
- {143980102, 122143249},
- {143991302, 122083053},
- {144000396, 122031753},
- {144009796, 121970954},
- {144017303, 121917655},
- {144025405, 121850250},
- {144030609, 121801452},
- {144036804, 121727455},
- {144040008, 121683456},
- {144043502, 121600952},
- {144044708, 121565048},
- {144045700, 121470352},
- {144045898, 121446952},
- {144041503, 121108657},
- {144037506, 121023452},
- {143733795, 118731750},
- {140461395, 95238647},
- {140461105, 95236755},
- {140433807, 95115249},
- {140392608, 95011650},
- {134840805, 84668952},
- {134824996, 84642456},
- {134781494, 84572952},
- {134716796, 84480850},
- {127473899, 74425453},
- {127467002, 74417152},
- {127431701, 74381652},
- {127402603, 74357147},
- {127375503, 74334457},
- {127294906, 74276649},
- {127181900, 74207649},
- {127177597, 74205451},
- {127123901, 74178451},
- {127078903, 74155853},
- {127028999, 74133148},
- {126870803, 74070953},
- {126442901, 73917648},
- {126432403, 73914955},
- {126326004, 73889846},
- {126262405, 73880645},
- {126128097, 73878456},
- {125998199, 73877655},
- {108701095, 74516647},
- {108644599, 74519348},
- {108495201, 74528953},
- {108311302, 74556457},
- {108252799, 74569458},
- {108079002, 74612152},
- {107981399, 74638954},
- {107921295, 74657951},
- {107862197, 74685951},
- {107601303, 74828948},
- {107546997, 74863449},
- {107192794, 75098846},
- {107131202, 75151153},
- {106260002, 76066146},
- {106195098, 76221145},
- {106168502, 76328453},
- {106144699, 76437454},
- {106124496, 76538452},
- {106103698, 76649650},
- {106084197, 76761650},
- {106066299, 76874450},
- {106049903, 76987457},
- {106034797, 77101150},
- {106020904, 77214950},
- {106008201, 77328948},
- {105996902, 77443145},
- {105986099, 77565849},
- {105977005, 77679649},
- {105969299, 77793151},
- {105963096, 77906349},
- {105958297, 78019149},
- {105955299, 78131454},
- {105954101, 78242950},
- {105954101, 131182754},
- },
- {
- {91355499, 77889205},
- {114834197, 120804504},
- {114840301, 120815200},
- {124701507, 132324798},
- {124798805, 132436706},
- {124901504, 132548309},
- {125126602, 132788909},
- {125235000, 132901901},
- {125337707, 133005401},
- {125546302, 133184707},
- {125751602, 133358703},
- {126133300, 133673004},
- {126263900, 133775604},
- {126367401, 133855499},
- {126471908, 133935104},
- {126596008, 134027496},
- {127119308, 134397094},
- {127135101, 134408203},
- {127433609, 134614303},
- {127554107, 134695709},
- {128155395, 135070907},
- {128274505, 135141799},
- {129132003, 135573211},
- {129438003, 135713195},
- {129556106, 135767196},
- {131512695, 136648498},
- {132294509, 136966598},
- {132798400, 137158798},
- {133203796, 137294494},
- {133377410, 137350799},
- {133522399, 137396606},
- {133804397, 137480697},
- {134017807, 137542205},
- {134288696, 137618408},
- {134564208, 137680099},
- {134844696, 137740097},
- {135202606, 137807098},
- {135489105, 137849807},
- {135626800, 137864898},
- {135766906, 137878692},
- {135972808, 137895797},
- {136110107, 137905502},
- {136235000, 137913101},
- {136485809, 137907196},
- {139194305, 136979202},
- {140318298, 136536209},
- {140380004, 136505004},
- {140668197, 136340499},
- {140724304, 136298904},
- {140808197, 136228210},
- {140861801, 136180603},
- {140917404, 136129104},
- {140979202, 136045104},
- {141022903, 135984207},
- {147591094, 126486999},
- {147661315, 126356101},
- {147706100, 126261901},
- {147749099, 126166000},
- {147817108, 126007507},
- {147859100, 125908599},
- {153693206, 111901100},
- {153731109, 111807800},
- {153760894, 111698806},
- {158641998, 92419303},
- {158644500, 92263702},
- {158539703, 92013504},
- {158499603, 91918899},
- {158335510, 91626800},
- {158264007, 91516304},
- {158216308, 91449203},
- {158178314, 91397506},
- {158094299, 91283203},
- {157396408, 90368202},
- {157285491, 90224700},
- {157169906, 90079200},
- {157050003, 89931304},
- {156290603, 89006805},
- {156221099, 88922897},
- {156087707, 88771003},
- {155947906, 88620498},
- {155348602, 88004203},
- {155113204, 87772796},
- {154947296, 87609703},
- {154776306, 87448204},
- {154588806, 87284301},
- {153886306, 86716400},
- {153682403, 86560501},
- {152966705, 86032402},
- {152687805, 85828704},
- {152484313, 85683204},
- {152278808, 85539001},
- {150878204, 84561401},
- {150683013, 84426498},
- {150599395, 84372703},
- {150395599, 84243202},
- {149988906, 83989395},
- {149782897, 83864501},
- {149568908, 83739799},
- {148872100, 83365303},
- {148625396, 83242202},
- {128079010, 73079605},
- {127980506, 73031005},
- {126701103, 72407104},
- {126501701, 72312202},
- {126431503, 72280601},
- {126311706, 72230606},
- {126260101, 72210899},
- {126191902, 72187599},
- {126140106, 72170303},
- {126088203, 72155303},
- {126036102, 72142700},
- {125965904, 72126899},
- {125913009, 72116600},
- {125859603, 72108505},
- {125788101, 72100296},
- {125733505, 72094398},
- {125678100, 72090400},
- {125621398, 72088302},
- {125548805, 72087303},
- {125490707, 72086898},
- {125430908, 72088203},
- {125369804, 72091094},
- {125306900, 72095306},
- {125233505, 72100997},
- {125168609, 72106506},
- {125102203, 72113601},
- {125034103, 72122207},
- {124964309, 72132095},
- {124890701, 72143707},
- {124819305, 72155105},
- {91355499, 77889099},
- {91355499, 77889205},
- },
- {
- {84531845, 127391708},
- {84916946, 130417510},
- {86133247, 131166900},
- {86338447, 131292892},
- {86748847, 131544799},
- {102193946, 136599502},
- {103090942, 136796798},
- {103247146, 136822509},
- {104083549, 136911499},
- {106119346, 137109802},
- {106265853, 137122207},
- {106480247, 137139205},
- {110257850, 137133605},
- {116917747, 136131408},
- {117054946, 136106704},
- {119043945, 135244293},
- {119249046, 135154708},
- {136220947, 126833007},
- {165896347, 91517105},
- {166032546, 91314697},
- {166055435, 91204902},
- {166056152, 91176803},
- {166047256, 91100006},
- {166039733, 91063705},
- {165814849, 90080802},
- {165736450, 89837707},
- {165677246, 89732101},
- {165676956, 89731803},
- {165560241, 89629302},
- {154419952, 82608505},
- {153822143, 82239700},
- {137942749, 74046104},
- {137095245, 73845504},
- {135751342, 73537704},
- {134225952, 73208602},
- {132484344, 72860801},
- {124730346, 73902000},
- {120736549, 74464401},
- {100401245, 78685401},
- {90574645, 90625701},
- {90475944, 90748809},
- {90430747, 90808700},
- {90321548, 90958305},
- {90254852, 91077903},
- {90165641, 91244003},
- {90134941, 91302398},
- {84474647, 103745697},
- {84328048, 104137901},
- {84288543, 104327606},
- {84038047, 106164604},
- {84013351, 106368698},
- {83943847, 110643203},
- {84531845, 127391708},
- },
-};
-
-const TestDataEx PRINTER_PART_POLYGONS_EX =
-{
- {
- {
- {533726562, 142141690},
- {532359712, 143386134},
- {530141290, 142155145},
- {528649729, 160091460},
- {533659500, 157607547},
- {538669739, 160091454},
- {537178168, 142155145},
- {534959534, 143386102},
- {533726562, 142141690},
- },
- {
- },
- },
- {
- {
- {118305840, 11603332},
- {118311095, 26616786},
- {113311095, 26611146},
- {109311095, 29604752},
- {109300760, 44608489},
- {109311095, 49631801},
- {113300790, 52636806},
- {118311095, 52636806},
- {118308782, 103636810},
- {223830940, 103636981},
- {236845321, 90642174},
- {236832882, 11630488},
- {232825251, 11616786},
- {210149075, 11616786},
- {211308596, 13625149},
- {209315325, 17080886},
- {205326885, 17080886},
- {203334352, 13629720},
- {204493136, 11616786},
- {118305840, 11603332},
- },
- {
- },
- },
- {
- {
- {365619370, 111280336},
- {365609100, 198818091},
- {387109100, 198804367},
- {387109100, 203279701},
- {471129120, 203279688},
- {471128689, 111283937},
- {365619370, 111280336},
- },
- {
- },
- },
- {
- {
- {479997525, 19177632},
- {477473010, 21975778},
- {475272613, 21969219},
- {475267479, 32995796},
- {477026388, 32995796},
- {483041428, 22582411},
- {482560272, 20318630},
- {479997525, 19177632},
- },
- {
- },
- },
- {
- {
- {476809080, 4972372},
- {475267479, 4975778},
- {475272613, 16002357},
- {481018177, 18281994},
- {482638044, 15466085},
- {476809080, 4972372},
- },
- {
- },
- },
- {
- {
- {424866064, 10276075},
- {415113411, 10277960},
- {411723180, 13685293},
- {410473354, 18784347},
- {382490868, 18784008},
- {380996185, 17286945},
- {380996185, 11278161},
- {375976165, 11284347},
- {375976165, 56389754},
- {375169018, 57784347},
- {371996185, 57784347},
- {371996185, 53779177},
- {364976165, 53784347},
- {364969637, 56791976},
- {369214608, 61054367},
- {371474507, 61054367},
- {371473155, 98298160},
- {378476349, 105317193},
- {407491306, 105307497},
- {413509785, 99284903},
- {413496185, 48304367},
- {419496173, 48315719},
- {422501887, 45292801},
- {422500504, 39363184},
- {420425079, 37284347},
- {419476165, 43284347},
- {413496185, 43284347},
- {413497261, 30797428},
- {418986175, 25308513},
- {424005230, 25315076},
- {428496185, 20815924},
- {428512720, 13948847},
- {424866064, 10276075},
- },
- {
- },
- },
- {
- {
- {723893066, 37354349},
- {717673034, 37370791},
- {717673034, 44872138},
- {715673034, 44867768},
- {715673034, 46055353},
- {699219526, 40066777},
- {697880758, 37748547},
- {691985477, 37748293},
- {689014018, 42869257},
- {691985477, 48016003},
- {697575093, 48003007},
- {715671494, 54589493},
- {715656800, 87142158},
- {759954611, 87142158},
- {764193054, 82897328},
- {764193054, 79872138},
- {757173034, 79866968},
- {757173034, 83872138},
- {754419422, 83869509},
- {753193054, 81739327},
- {753193054, 37360571},
- {723893066, 37354349},
- },
- {
- },
- },
- {
- {
- {85607478, 4227596},
- {61739211, 4230337},
- {61739211, 13231393},
- {58725066, 13231405},
- {58721589, 27731406},
- {58738375, 30262521},
- {61739211, 30251413},
- {61736212, 38251411},
- {70759231, 38254724},
- {70905600, 33317391},
- {73749222, 31251468},
- {76592843, 33317393},
- {76739211, 38254516},
- {86765007, 38251411},
- {86759599, 4231393},
- {85607478, 4227596},
- },
- {
- },
- },
- {
- {
- {534839721, 53437770},
- {534839721, 60849059},
- {539898273, 63773857},
- {545461140, 63757881},
- {544859741, 53447836},
- {541839721, 53437862},
- {541710836, 56353878},
- {540193984, 57229659},
- {538859741, 53437862},
- {534839721, 53437770},
- },
- {
- },
- },
- {
- {
- {756086230, 136598477},
- {732054387, 136605752},
- {732052489, 172629505},
- {756091994, 172627853},
- {756086230, 136598477},
- },
- {
- },
- },
- {
- {
- {100337034, 79731391},
- {70296833, 79731391},
- {70311095, 92263567},
- {74329808, 96264260},
- {96344976, 96257215},
- {100344419, 92232243},
- {100337034, 79731391},
- },
- {
- },
- },
- {
- {
- {102331115, 44216643},
- {67311095, 44217252},
- {67311095, 69250964},
- {74329808, 76264260},
- {96334594, 76251411},
- {103335261, 69241401},
- {103345839, 44231404},
- {102331115, 44216643},
- },
- {
- },
- },
- {
- {
- {93849749, 109613798},
- {91771666, 111698636},
- {91772404, 174626800},
- {96782902, 179645338},
- {241790509, 179645349},
- {246800716, 174626800},
- {246802574, 111699755},
- {243934250, 109616385},
- {93849749, 109613798},
- },
- {
- },
- },
- {
- {
- {15856630, 87966835},
- {8414359, 91273170},
- {5891847, 99010553},
- {8403012, 104668172},
- {13739106, 107763252},
- {13739106, 116209175},
- {17959116, 116219127},
- {17959127, 107763252},
- {23952579, 103855773},
- {25806388, 96944174},
- {22553953, 90543787},
- {15856630, 87966835},
- },
- {
- },
- },
- {
- {
- {503922805, 110421794},
- {491110107, 123244292},
- {479598157, 123244304},
- {479601067, 149264312},
- {494260327, 149265241},
- {502929782, 157948320},
- {506490250, 155806171},
- {502950518, 155094962},
- {507193172, 150852294},
- {504364680, 148023895},
- {535816833, 116571757},
- {538656617, 119411542},
- {542887886, 115157558},
- {543594970, 118693080},
- {545330008, 116966050},
- {540309189, 110425901},
- {503922805, 110421794},
- },
- {
- },
- },
- {
- {
- {519310433, 62560296},
- {515749982, 64702434},
- {519289696, 65413661},
- {515047062, 69656303},
- {517875553, 72484703},
- {486423431, 103936848},
- {483595031, 101108448},
- {479352325, 105351055},
- {478645233, 101815525},
- {476917724, 103520870},
- {481923478, 110077233},
- {518337308, 110084297},
- {531130127, 97264312},
- {542630127, 97281049},
- {542639167, 71244292},
- {527979906, 71243363},
- {519310433, 62560296},
- },
- {
- },
- },
- {
- {
- {528658425, 14775300},
- {525975568, 24475413},
- {522556814, 29181341},
- {517517474, 32090757},
- {511736147, 32698600},
- {506200465, 30901018},
- {501879743, 27011092},
- {497782491, 14775300},
- {492372374, 15588397},
- {489384268, 20795320},
- {491253082, 28537271},
- {495185363, 34469052},
- {495178475, 43927542},
- {502032399, 55796416},
- {524402581, 55807400},
- {531706434, 44295318},
- {531205383, 34469052},
- {536679415, 23789946},
- {535868173, 17264403},
- {532873348, 15073849},
- {528658425, 14775300},
- },
- {
- },
- },
- {
- {
- {481122222, 166062916},
- {478115710, 166824472},
- {477103577, 169063247},
- {477106058, 192070670},
- {478623652, 194687013},
- {525109130, 195083267},
- {525117792, 198086965},
- {535129140, 198091624},
- {535129150, 195083267},
- {539038502, 194940807},
- {540865280, 193308821},
- {541132038, 169100183},
- {539614599, 166459484},
- {481122222, 166062916},
- },
- {
- },
- },
- {
- {
- {23771404, 13005453},
- {24774973, 19182457},
- {31971050, 18727127},
- {32556286, 58337520},
- {25390683, 58337566},
- {25063762, 54707065},
- {20168811, 54707252},
- {20171550, 62917175},
- {70810377, 202895528},
- {74314421, 205588631},
- {88674817, 205515176},
- {91837376, 203083756},
- {92280287, 199307207},
- {40674807, 15904975},
- {36849630, 13006690},
- {23771404, 13005453},
- },
- {
- },
- },
- {
- {
- {336421201, 2986256},
- {331176570, 6498191},
- {327552287, 5825511},
- {324913825, 2988891},
- {316226154, 2989990},
- {313040282, 6275291},
- {313040282, 23489990},
- {307126391, 23490002},
- {307140289, 25510010},
- {313040282, 25510010},
- {313040282, 28989990},
- {307126391, 28990002},
- {307140289, 31015515},
- {313040282, 31010010},
- {313040282, 35989990},
- {304534809, 37529785},
- {304524991, 73488855},
- {308554680, 77518546},
- {324040282, 77510010},
- {324040295, 93025333},
- {334574441, 93010010},
- {334574441, 90989990},
- {332560302, 90989990},
- {332560302, 85010010},
- {334560302, 85010010},
- {334561237, 82010010},
- {338540282, 82010010},
- {339540282, 83760010},
- {338540293, 93020012},
- {348060655, 93014679},
- {356564448, 84500000},
- {356560555, 28989990},
- {347334198, 29039989},
- {347334198, 25510010},
- {356510304, 25521084},
- {356510315, 23478922},
- {347560302, 23489990},
- {347560302, 5775291},
- {344874443, 2989990},
- {336421201, 2986256},
- },
- {
- },
- },
- {
- {
- {465152221, 31684687},
- {457606880, 31688302},
- {452659362, 35508617},
- {449044605, 34734089},
- {446478972, 31692751},
- {437784814, 31692957},
- {435521210, 33956565},
- {435532195, 65697616},
- {426028494, 65691361},
- {426025938, 85049712},
- {435532195, 95717636},
- {435524445, 103754026},
- {436995898, 105225463},
- {447552204, 105226323},
- {447552215, 103197497},
- {444552215, 103197616},
- {444552215, 99217636},
- {452032195, 99217636},
- {452032195, 105221758},
- {465588513, 105225463},
- {467059965, 103754026},
- {467052215, 95717636},
- {478053039, 84511285},
- {478056214, 65697616},
- {468552215, 65697616},
- {468563959, 33957323},
- {465152221, 31684687},
- },
- {
- },
- },
- {
- {
- {764927063, 92658416},
- {762115426, 94171595},
- {762122741, 131696443},
- {786415417, 132779578},
- {793690904, 129904572},
- {797383202, 124822853},
- {798269157, 120142660},
- {796710161, 114090278},
- {793387498, 110215980},
- {796094093, 103892242},
- {794107594, 96994001},
- {787445494, 92840355},
- {764927063, 92658416},
- },
- {
- },
- },
- {
- {
- {27496331, 123147467},
- {3202195, 124246400},
- {3203433, 205768600},
- {20223453, 205775606},
- {20223644, 163243606},
- {31297341, 162189074},
- {36789517, 155659691},
- {36967183, 150566416},
- {34468182, 145711036},
- {38465496, 140400171},
- {38952460, 132613091},
- {34771593, 126022444},
- {27496331, 123147467},
- },
- {
- },
- },
- {
- {
- {797556553, 39197820},
- {791313598, 39199767},
- {789506233, 39864015},
- {789522521, 48199767},
- {775974570, 48195721},
- {774022521, 50129235},
- {774008720, 76258022},
- {775974570, 78223833},
- {789522521, 78219787},
- {789522521, 86576919},
- {797556547, 87221747},
- {797556553, 39197820},
- },
- {
- },
- },
- {
- {
- {676593113, 129820144},
- {676565322, 164844636},
- {701599609, 164858650},
- {701599609, 129823260},
- {676593113, 129820144},
- },
- {
- },
- },
- {
- {
- {727646871, 93121321},
- {709122741, 93122138},
- {709122741, 125656310},
- {718769809, 135145243},
- {721622937, 135156111},
- {724152429, 132626619},
- {723734126, 112688301},
- {725837154, 107378546},
- {728976138, 104430846},
- {735847924, 102664848},
- {741289364, 104430846},
- {745202882, 108599767},
- {746590596, 114642158},
- {751137173, 114644887},
- {756151199, 109641674},
- {756149037, 94634278},
- {754642761, 93122138},
- {727646871, 93121321},
- },
- {
- },
- },
- {
- {
- {135915724, 185598906},
- {131396265, 193419009},
- {131399444, 197643260},
- {140399444, 197636810},
- {140399444, 199138818},
- {157419464, 197643916},
- {157422805, 193210743},
- {153046747, 185604789},
- {149044579, 185614655},
- {147324399, 189850396},
- {144168954, 191108901},
- {141187892, 189479768},
- {139917659, 185615382},
- {135915724, 185598906},
- },
- {
- },
- },
- {
- {
- {312619110, 154485844},
- {309601817, 157488332},
- {309599764, 203494810},
- {313109244, 207010010},
- {352900849, 207019221},
- {359629120, 200302405},
- {359638705, 159501827},
- {354621096, 154487830},
- {312619110, 154485844},
- },
- {
- },
- },
- {
- {
- {313120315, 98984639},
- {309609100, 102486971},
- {309596977, 148492024},
- {312591195, 151510010},
- {354608772, 151524494},
- {359629120, 146515788},
- {359638123, 105715491},
- {352907860, 98987790},
- {313120315, 98984639},
- },
- {
- },
- },
- {
- {
- {657746643, 86246732},
- {651722477, 92270881},
- {651720052, 131280884},
- {653947196, 131280884},
- {659746643, 125487816},
- {659746643, 119273826},
- {663742413, 112352691},
- {671726623, 112352691},
- {675733721, 119283349},
- {684745297, 119298573},
- {689758503, 114263168},
- {689752066, 91272158},
- {684746643, 86260871},
- {657746643, 86246732},
- },
- {
- },
- },
- {
- {
- {653940791, 39260871},
- {651720052, 39260871},
- {651726623, 78280611},
- {657746631, 84295035},
- {684746643, 84280891},
- {689752066, 79269604},
- {689746643, 56247942},
- {684745283, 51243184},
- {675733721, 51258413},
- {671726623, 58189071},
- {663742413, 58189071},
- {659746643, 51267936},
- {659746643, 45053950},
- {653940791, 39260871},
- },
- {
- },
- },
- {
- {
- {442365208, 3053303},
- {436408500, 5694021},
- {434342552, 11072741},
- {436986326, 17009033},
- {442365367, 19073360},
- {448299202, 16431441},
- {450365150, 11052721},
- {448299202, 5694021},
- {442365208, 3053303},
- },
- {
- },
- },
-};
diff --git a/src/libnest2d/tests/printer_parts.h b/src/libnest2d/tests/printer_parts.h
deleted file mode 100644
index 1e65826bd..000000000
--- a/src/libnest2d/tests/printer_parts.h
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifndef PRINTER_PARTS_H
-#define PRINTER_PARTS_H
-
-#include <vector>
-#include <libnest2d/backends/clipper/clipper_polygon.hpp>
-
-using TestData = std::vector<ClipperLib::Path>;
-using TestDataEx = std::vector<ClipperLib::Polygon>;
-
-extern const TestData PRINTER_PART_POLYGONS;
-extern const TestData STEGOSAUR_POLYGONS;
-extern const TestDataEx PRINTER_PART_POLYGONS_EX;
-
-#endif // PRINTER_PARTS_H
diff --git a/src/libnest2d/tests/test.cpp b/src/libnest2d/tests/test.cpp
deleted file mode 100644
index 5ba28228a..000000000
--- a/src/libnest2d/tests/test.cpp
+++ /dev/null
@@ -1,1062 +0,0 @@
-#include <gtest/gtest.h>
-#include <fstream>
-
-#include <libnest2d.h>
-#include "printer_parts.h"
-//#include <libnest2d/geometry_traits_nfp.hpp>
-#include "../tools/svgtools.hpp"
-#include <libnest2d/utils/rotcalipers.hpp>
-
-#if defined(_MSC_VER) && defined(__clang__)
-#define BOOST_NO_CXX17_HDR_STRING_VIEW
-#endif
-
-#include "boost/multiprecision/integer.hpp"
-#include "boost/rational.hpp"
-
-//#include "../tools/libnfpglue.hpp"
-//#include "../tools/nfp_svgnest_glue.hpp"
-
-namespace libnest2d {
-#if !defined(_MSC_VER) && defined(__SIZEOF_INT128__) && !defined(__APPLE__)
-using LargeInt = __int128;
-#else
-using LargeInt = boost::multiprecision::int128_t;
-template<> struct _NumTag<LargeInt> { using Type = ScalarTag; };
-#endif
-template<class T> struct _NumTag<boost::rational<T>> { using Type = RationalTag; };
-
-namespace nfp {
-
-template<class S>
-struct NfpImpl<S, NfpLevel::CONVEX_ONLY>
-{
- NfpResult<S> operator()(const S &sh, const S &other)
- {
- return nfpConvexOnly<S, boost::rational<LargeInt>>(sh, other);
- }
-};
-
-}
-}
-
-static std::vector<libnest2d::Item>& prusaParts() {
- static std::vector<libnest2d::Item> ret;
-
- if(ret.empty()) {
- ret.reserve(PRINTER_PART_POLYGONS.size());
- for(auto& inp : PRINTER_PART_POLYGONS) ret.emplace_back(inp);
- }
-
- return ret;
-}
-
-TEST(GeometryAlgorithms, Angles)
-{
-
- using namespace libnest2d;
-
- Degrees deg(180);
- Radians rad(deg);
- Degrees deg2(rad);
-
- ASSERT_DOUBLE_EQ(rad, Pi);
- ASSERT_DOUBLE_EQ(deg, 180);
- ASSERT_DOUBLE_EQ(deg2, 180);
- ASSERT_DOUBLE_EQ(rad, Radians(deg));
- ASSERT_DOUBLE_EQ( Degrees(rad), deg);
-
- ASSERT_TRUE(rad == deg);
-
- Segment seg = {{0, 0}, {12, -10}};
-
- ASSERT_TRUE(Degrees(seg.angleToXaxis()) > 270 &&
- Degrees(seg.angleToXaxis()) < 360);
-
- seg = {{0, 0}, {12, 10}};
-
- ASSERT_TRUE(Degrees(seg.angleToXaxis()) > 0 &&
- Degrees(seg.angleToXaxis()) < 90);
-
- seg = {{0, 0}, {-12, 10}};
-
- ASSERT_TRUE(Degrees(seg.angleToXaxis()) > 90 &&
- Degrees(seg.angleToXaxis()) < 180);
-
- seg = {{0, 0}, {-12, -10}};
-
- ASSERT_TRUE(Degrees(seg.angleToXaxis()) > 180 &&
- Degrees(seg.angleToXaxis()) < 270);
-
- seg = {{0, 0}, {1, 0}};
-
- ASSERT_DOUBLE_EQ(Degrees(seg.angleToXaxis()), 0);
-
- seg = {{0, 0}, {0, 1}};
-
- ASSERT_DOUBLE_EQ(Degrees(seg.angleToXaxis()), 90);
-
-
- seg = {{0, 0}, {-1, 0}};
-
- ASSERT_DOUBLE_EQ(Degrees(seg.angleToXaxis()), 180);
-
-
- seg = {{0, 0}, {0, -1}};
-
- ASSERT_DOUBLE_EQ(Degrees(seg.angleToXaxis()), 270);
-
-}
-
-// Simple test, does not use gmock
-TEST(Nesting, ItemCreationAndDestruction)
-{
- using namespace libnest2d;
-
- Item sh = { {0, 0}, {1, 0}, {1, 1}, {0, 1} };
-
- ASSERT_EQ(sh.vertexCount(), 4u);
-
- Item sh2 ({ {0, 0}, {1, 0}, {1, 1}, {0, 1} });
-
- ASSERT_EQ(sh2.vertexCount(), 4u);
-
- // copy
- Item sh3 = sh2;
-
- ASSERT_EQ(sh3.vertexCount(), 4u);
-
- sh2 = {};
-
- ASSERT_EQ(sh2.vertexCount(), 0u);
- ASSERT_EQ(sh3.vertexCount(), 4u);
-
-}
-
-TEST(GeometryAlgorithms, boundingCircle) {
- using namespace libnest2d;
- using placers::boundingCircle;
-
- PolygonImpl p = {{{0, 10}, {10, 0}, {0, -10}, {0, 10}}, {}};
- Circle c = boundingCircle(p);
-
- ASSERT_EQ(c.center().X, 0);
- ASSERT_EQ(c.center().Y, 0);
- ASSERT_DOUBLE_EQ(c.radius(), 10);
-
- shapelike::translate(p, PointImpl{10, 10});
- c = boundingCircle(p);
-
- ASSERT_EQ(c.center().X, 10);
- ASSERT_EQ(c.center().Y, 10);
- ASSERT_DOUBLE_EQ(c.radius(), 10);
-
- auto parts = prusaParts();
-
- int i = 0;
- for(auto& part : parts) {
- c = boundingCircle(part.transformedShape());
- if(std::isnan(c.radius())) std::cout << "fail: radius is nan" << std::endl;
-
- else for(auto v : shapelike::contour(part.transformedShape()) ) {
- auto d = pointlike::distance(v, c.center());
- if(d > c.radius() ) {
- auto e = std::abs( 1.0 - d/c.radius());
- ASSERT_LE(e, 1e-3);
- }
- }
- i++;
- }
-
-}
-
-TEST(GeometryAlgorithms, Distance) {
- using namespace libnest2d;
-
- Point p1 = {0, 0};
-
- Point p2 = {10, 0};
- Point p3 = {10, 10};
-
- ASSERT_DOUBLE_EQ(pointlike::distance(p1, p2), 10);
- ASSERT_DOUBLE_EQ(pointlike::distance(p1, p3), sqrt(200));
-
- Segment seg(p1, p3);
-
- // ASSERT_DOUBLE_EQ(pointlike::distance(p2, seg), 7.0710678118654755);
-
- auto result = pointlike::horizontalDistance(p2, seg);
-
- auto check = [](TCompute<Coord> val, TCompute<Coord> expected) {
- if(std::is_floating_point<TCompute<Coord>>::value)
- ASSERT_DOUBLE_EQ(static_cast<double>(val),
- static_cast<double>(expected));
- else
- ASSERT_EQ(val, expected);
- };
-
- ASSERT_TRUE(result.second);
- check(result.first, 10);
-
- result = pointlike::verticalDistance(p2, seg);
- ASSERT_TRUE(result.second);
- check(result.first, -10);
-
- result = pointlike::verticalDistance(Point{10, 20}, seg);
- ASSERT_TRUE(result.second);
- check(result.first, 10);
-
-
- Point p4 = {80, 0};
- Segment seg2 = { {0, 0}, {0, 40} };
-
- result = pointlike::horizontalDistance(p4, seg2);
-
- ASSERT_TRUE(result.second);
- check(result.first, 80);
-
- result = pointlike::verticalDistance(p4, seg2);
- // Point should not be related to the segment
- ASSERT_FALSE(result.second);
-
-}
-
-TEST(GeometryAlgorithms, Area) {
- using namespace libnest2d;
-
- Rectangle rect(10, 10);
-
- ASSERT_EQ(rect.area(), 100);
-
- Rectangle rect2 = {100, 100};
-
- ASSERT_EQ(rect2.area(), 10000);
-
- Item item = {
- {61, 97},
- {70, 151},
- {176, 151},
- {189, 138},
- {189, 59},
- {70, 59},
- {61, 77},
- {61, 97}
- };
-
- ASSERT_TRUE(shapelike::area(item.transformedShape()) > 0 );
-}
-
-TEST(GeometryAlgorithms, IsPointInsidePolygon) {
- using namespace libnest2d;
-
- Rectangle rect(10, 10);
-
- Point p = {1, 1};
-
- ASSERT_TRUE(rect.isInside(p));
-
- p = {11, 11};
-
- ASSERT_FALSE(rect.isInside(p));
-
-
- p = {11, 12};
-
- ASSERT_FALSE(rect.isInside(p));
-
-
- p = {3, 3};
-
- ASSERT_TRUE(rect.isInside(p));
-
-}
-
-//TEST(GeometryAlgorithms, Intersections) {
-// using namespace binpack2d;
-
-// Rectangle rect(70, 30);
-
-// rect.translate({80, 60});
-
-// Rectangle rect2(80, 60);
-// rect2.translate({80, 0});
-
-//// ASSERT_FALSE(Item::intersects(rect, rect2));
-
-// Segment s1({0, 0}, {10, 10});
-// Segment s2({1, 1}, {11, 11});
-// ASSERT_FALSE(ShapeLike::intersects(s1, s1));
-// ASSERT_FALSE(ShapeLike::intersects(s1, s2));
-//}
-
-// Simple test, does not use gmock
-TEST(GeometryAlgorithms, LeftAndDownPolygon)
-{
- using namespace libnest2d;
- using namespace libnest2d;
-
- Box bin(100, 100);
- BottomLeftPlacer placer(bin);
-
- Item item = {{70, 75}, {88, 60}, {65, 50}, {60, 30}, {80, 20}, {42, 20},
- {35, 35}, {35, 55}, {40, 75}, {70, 75}};
-
- Item leftControl = { {40, 75},
- {35, 55},
- {35, 35},
- {42, 20},
- {0, 20},
- {0, 75},
- {40, 75}};
-
- Item downControl = {{88, 60},
- {88, 0},
- {35, 0},
- {35, 35},
- {42, 20},
- {80, 20},
- {60, 30},
- {65, 50},
- {88, 60}};
-
- Item leftp(placer.leftPoly(item));
-
- ASSERT_TRUE(shapelike::isValid(leftp.rawShape()).first);
- ASSERT_EQ(leftp.vertexCount(), leftControl.vertexCount());
-
- for(unsigned long i = 0; i < leftControl.vertexCount(); i++) {
- ASSERT_EQ(getX(leftp.vertex(i)), getX(leftControl.vertex(i)));
- ASSERT_EQ(getY(leftp.vertex(i)), getY(leftControl.vertex(i)));
- }
-
- Item downp(placer.downPoly(item));
-
- ASSERT_TRUE(shapelike::isValid(downp.rawShape()).first);
- ASSERT_EQ(downp.vertexCount(), downControl.vertexCount());
-
- for(unsigned long i = 0; i < downControl.vertexCount(); i++) {
- ASSERT_EQ(getX(downp.vertex(i)), getX(downControl.vertex(i)));
- ASSERT_EQ(getY(downp.vertex(i)), getY(downControl.vertex(i)));
- }
-}
-
-// Simple test, does not use gmock
-TEST(GeometryAlgorithms, ArrangeRectanglesTight)
-{
- using namespace libnest2d;
-
- std::vector<Rectangle> rects = {
- {80, 80},
- {60, 90},
- {70, 30},
- {80, 60},
- {60, 60},
- {60, 40},
- {40, 40},
- {10, 10},
- {10, 10},
- {10, 10},
- {10, 10},
- {10, 10},
- {5, 5},
- {5, 5},
- {5, 5},
- {5, 5},
- {5, 5},
- {5, 5},
- {5, 5},
- {20, 20} };
-
- Box bin(210, 250, {105, 125});
-
- ASSERT_EQ(bin.width(), 210);
- ASSERT_EQ(bin.height(), 250);
- ASSERT_EQ(getX(bin.center()), 105);
- ASSERT_EQ(getY(bin.center()), 125);
-
- _Nester<BottomLeftPlacer, DJDHeuristic> arrange(bin);
-
- arrange.execute(rects.begin(), rects.end());
-
- auto max_group = std::max_element(rects.begin(), rects.end(),
- [](const Item &i1, const Item &i2) {
- return i1.binId() < i2.binId();
- });
-
- int groups = max_group == rects.end() ? 0 : max_group->binId() + 1;
-
- ASSERT_EQ(groups, 1u);
- ASSERT_TRUE(
- std::all_of(rects.begin(), rects.end(), [](const Rectangle &itm) {
- return itm.binId() != BIN_ID_UNSET;
- }));
-
- // check for no intersections, no containment:
-
- bool valid = true;
- for(Item& r1 : rects) {
- for(Item& r2 : rects) {
- if(&r1 != &r2 ) {
- valid = !Item::intersects(r1, r2) || Item::touches(r1, r2);
- ASSERT_TRUE(valid);
- valid = (valid && !r1.isInside(r2) && !r2.isInside(r1));
- ASSERT_TRUE(valid);
- }
- }
- }
-}
-
-TEST(GeometryAlgorithms, ArrangeRectanglesLoose)
-{
- using namespace libnest2d;
-
- // std::vector<Rectangle> rects = { {40, 40}, {10, 10}, {20, 20} };
- std::vector<Rectangle> rects = {
- {80, 80},
- {60, 90},
- {70, 30},
- {80, 60},
- {60, 60},
- {60, 40},
- {40, 40},
- {10, 10},
- {10, 10},
- {10, 10},
- {10, 10},
- {10, 10},
- {5, 5},
- {5, 5},
- {5, 5},
- {5, 5},
- {5, 5},
- {5, 5},
- {5, 5},
- {20, 20} };
-
- Box bin(210, 250, {105, 125});
-
- ASSERT_EQ(bin.width(), 210);
- ASSERT_EQ(bin.height(), 250);
- ASSERT_EQ(getX(bin.center()), 105);
- ASSERT_EQ(getY(bin.center()), 125);
-
- Coord min_obj_distance = 5;
-
- _Nester<BottomLeftPlacer, DJDHeuristic> arrange(bin, min_obj_distance);
-
- arrange.execute(rects.begin(), rects.end());
-
- auto max_group = std::max_element(rects.begin(), rects.end(),
- [](const Item &i1, const Item &i2) {
- return i1.binId() < i2.binId();
- });
-
- size_t groups = max_group == rects.end() ? 0 : max_group->binId() + 1;
-
- ASSERT_EQ(groups, 1u);
- ASSERT_TRUE(
- std::all_of(rects.begin(), rects.end(), [](const Rectangle &itm) {
- return itm.binId() != BIN_ID_UNSET;
- }));
-
- // check for no intersections, no containment:
- bool valid = true;
- for(Item& r1 : rects) {
- for(Item& r2 : rects) {
- if(&r1 != &r2 ) {
- valid = !Item::intersects(r1, r2);
- valid = (valid && !r1.isInside(r2) && !r2.isInside(r1));
- ASSERT_TRUE(valid);
- }
- }
- }
-
-}
-
-namespace {
-using namespace libnest2d;
-
-template<long long SCALE = 1, class Bin>
-void exportSVG(std::vector<std::reference_wrapper<Item>>& result, const Bin& bin, int idx = 0) {
-
-
- std::string loc = "out";
-
- static std::string svg_header =
- R"raw(<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
-<svg height="500" width="500" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
-)raw";
-
- int i = idx;
- auto r = result;
- // for(auto r : result) {
- std::fstream out(loc + std::to_string(i) + ".svg", std::fstream::out);
- if(out.is_open()) {
- out << svg_header;
- Item rbin( Rectangle(bin.width(), bin.height()) );
- for(unsigned i = 0; i < rbin.vertexCount(); i++) {
- auto v = rbin.vertex(i);
- setY(v, -getY(v)/SCALE + 500 );
- setX(v, getX(v)/SCALE);
- rbin.setVertex(i, v);
- }
- out << shapelike::serialize<Formats::SVG>(rbin.rawShape()) << std::endl;
- for(Item& sh : r) {
- Item tsh(sh.transformedShape());
- for(unsigned i = 0; i < tsh.vertexCount(); i++) {
- auto v = tsh.vertex(i);
- setY(v, -getY(v)/SCALE + 500);
- setX(v, getX(v)/SCALE);
- tsh.setVertex(i, v);
- }
- out << shapelike::serialize<Formats::SVG>(tsh.rawShape()) << std::endl;
- }
- out << "\n</svg>" << std::endl;
- }
- out.close();
-
- // i++;
- // }
-}
-}
-
-TEST(GeometryAlgorithms, BottomLeftStressTest) {
- using namespace libnest2d;
-
- const Coord SCALE = 1000000;
- auto& input = prusaParts();
-
- Box bin(210*SCALE, 250*SCALE);
- BottomLeftPlacer placer(bin);
-
- auto it = input.begin();
- auto next = it;
- int i = 0;
- while(it != input.end() && ++next != input.end()) {
- placer.pack(*it);
- placer.pack(*next);
-
- auto result = placer.getItems();
- bool valid = true;
-
- if(result.size() == 2) {
- Item& r1 = result[0];
- Item& r2 = result[1];
- valid = !Item::intersects(r1, r2) || Item::touches(r1, r2);
- valid = (valid && !r1.isInside(r2) && !r2.isInside(r1));
- if(!valid) {
- std::cout << "error index: " << i << std::endl;
- exportSVG(result, bin, i);
- }
- ASSERT_TRUE(valid);
- } else {
- std::cout << "something went terribly wrong!" << std::endl;
- FAIL();
- }
-
- placer.clearItems();
- it++;
- i++;
- }
-}
-
-TEST(GeometryAlgorithms, convexHull) {
- using namespace libnest2d;
-
- ClipperLib::Path poly = PRINTER_PART_POLYGONS[0];
-
- auto chull = sl::convexHull(poly);
-
- ASSERT_EQ(chull.size(), poly.size());
-}
-
-
-TEST(Nesting, NestPrusaPartsShouldFitIntoTwoBins) {
-
- // Get the input items and define the bin.
- std::vector<Item> input = prusaParts();
- auto bin = Box(250000000, 210000000);
-
- // Do the nesting. Check in each step if the remaining items are less than
- // in the previous step. (Some algorithms can place more items in one step)
- size_t pcount = input.size();
- libnest2d::nest(input, bin, [&pcount](unsigned cnt) {
- ASSERT_TRUE(cnt < pcount);
- pcount = cnt;
- });
-
- // Get the number of logical bins: search for the max binId...
- auto max_binid_it = std::max_element(input.begin(), input.end(),
- [](const Item &i1, const Item &i2) {
- return i1.binId() < i2.binId();
- });
-
- auto bins = size_t(max_binid_it == input.end() ? 0 :
- max_binid_it->binId() + 1);
-
- // For prusa parts, 2 bins should be enough...
- ASSERT_LE(bins, 2u);
-
- // All parts should be processed by the algorithm
- ASSERT_TRUE(
- std::all_of(input.begin(), input.end(), [](const Item &itm) {
- return itm.binId() != BIN_ID_UNSET;
- }));
-
- // Gather the items into piles of arranged polygons...
- using Pile = TMultiShape<ClipperLib::Polygon>;
- std::vector<Pile> piles(bins);
-
- for (auto &itm : input)
- piles[size_t(itm.binId())].emplace_back(itm.transformedShape());
-
- // Now check all the piles, the bounding box of each pile should be inside
- // the defined bin.
- for (auto &pile : piles) {
- auto bb = sl::boundingBox(pile);
- ASSERT_TRUE(sl::isInside(bb, bin));
- }
-}
-
-TEST(Nesting, NestEmptyItemShouldBeUntouched) {
- auto bin = Box(250000000, 210000000); // dummy bin
-
- std::vector<Item> items;
- items.emplace_back(Item{}); // Emplace empty item
- items.emplace_back(Item{0, 200, 0}); // Emplace zero area item
-
- libnest2d::nest(items, bin);
-
- for (auto &itm : items) ASSERT_EQ(itm.binId(), BIN_ID_UNSET);
-}
-
-TEST(Nesting, NestLargeItemShouldBeUntouched) {
- auto bin = Box(250000000, 210000000); // dummy bin
-
- std::vector<Item> items;
- items.emplace_back(Rectangle{250000001, 210000001}); // Emplace large item
-
- libnest2d::nest(items, bin);
-
- ASSERT_EQ(items.front().binId(), BIN_ID_UNSET);
-}
-
-namespace {
-
-struct ItemPair {
- Item orbiter;
- Item stationary;
-};
-
-std::vector<ItemPair> nfp_testdata = {
- {
- {
- {80, 50},
- {100, 70},
- {120, 50},
- {80, 50}
- },
- {
- {10, 10},
- {10, 40},
- {40, 40},
- {40, 10},
- {10, 10}
- }
- },
- {
- {
- {80, 50},
- {60, 70},
- {80, 90},
- {120, 90},
- {140, 70},
- {120, 50},
- {80, 50}
- },
- {
- {10, 10},
- {10, 40},
- {40, 40},
- {40, 10},
- {10, 10}
- }
- },
- {
- {
- {40, 10},
- {30, 10},
- {20, 20},
- {20, 30},
- {30, 40},
- {40, 40},
- {50, 30},
- {50, 20},
- {40, 10}
- },
- {
- {80, 0},
- {80, 30},
- {110, 30},
- {110, 0},
- {80, 0}
- }
- },
- {
- {
- {117, 107},
- {118, 109},
- {120, 112},
- {122, 113},
- {128, 113},
- {130, 112},
- {132, 109},
- {133, 107},
- {133, 103},
- {132, 101},
- {130, 98},
- {128, 97},
- {122, 97},
- {120, 98},
- {118, 101},
- {117, 103},
- {117, 107}
- },
- {
- {102, 116},
- {111, 126},
- {114, 126},
- {144, 106},
- {148, 100},
- {148, 85},
- {147, 84},
- {102, 84},
- {102, 116},
- }
- },
- {
- {
- {99, 122},
- {108, 140},
- {110, 142},
- {139, 142},
- {151, 122},
- {151, 102},
- {142, 70},
- {139, 68},
- {111, 68},
- {108, 70},
- {99, 102},
- {99, 122},
- },
- {
- {107, 124},
- {128, 125},
- {133, 125},
- {136, 124},
- {140, 121},
- {142, 119},
- {143, 116},
- {143, 109},
- {141, 93},
- {139, 89},
- {136, 86},
- {134, 85},
- {108, 85},
- {107, 86},
- {107, 124},
- }
- },
- {
- {
- {91, 100},
- {94, 144},
- {117, 153},
- {118, 153},
- {159, 112},
- {159, 110},
- {156, 66},
- {133, 57},
- {132, 57},
- {91, 98},
- {91, 100},
- },
- {
- {101, 90},
- {103, 98},
- {107, 113},
- {114, 125},
- {115, 126},
- {135, 126},
- {136, 125},
- {144, 114},
- {149, 90},
- {149, 89},
- {148, 87},
- {145, 84},
- {105, 84},
- {102, 87},
- {101, 89},
- {101, 90},
- }
- }
-};
-
- std::vector<ItemPair> nfp_concave_testdata = {
- { // ItemPair
- {
- {
- {533726, 142141},
- {532359, 143386},
- {530141, 142155},
- {528649, 160091},
- {533659, 157607},
- {538669, 160091},
- {537178, 142155},
- {534959, 143386},
- {533726, 142141},
- }
- },
- {
- {
- {118305, 11603},
- {118311, 26616},
- {113311, 26611},
- {109311, 29604},
- {109300, 44608},
- {109311, 49631},
- {113300, 52636},
- {118311, 52636},
- {118308, 103636},
- {223830, 103636},
- {236845, 90642},
- {236832, 11630},
- {232825, 11616},
- {210149, 11616},
- {211308, 13625},
- {209315, 17080},
- {205326, 17080},
- {203334, 13629},
- {204493, 11616},
- {118305, 11603},
- }
- },
- }
-};
-
-template<nfp::NfpLevel lvl, Coord SCALE>
-void testNfp(const std::vector<ItemPair>& testdata) {
- using namespace libnest2d;
-
- Box bin(210*SCALE, 250*SCALE);
-
- int testcase = 0;
-
- auto& exportfun = exportSVG<SCALE, Box>;
-
- auto onetest = [&](Item& orbiter, Item& stationary, unsigned /*testidx*/){
- testcase++;
-
- orbiter.translate({210*SCALE, 0});
-
- auto&& nfp = nfp::noFitPolygon<lvl>(stationary.rawShape(),
- orbiter.transformedShape());
-
- placers::correctNfpPosition(nfp, stationary, orbiter);
-
- auto valid = shapelike::isValid(nfp.first);
-
- /*Item infp(nfp.first);
- if(!valid.first) {
- std::cout << "test instance: " << testidx << " "
- << valid.second << std::endl;
- std::vector<std::reference_wrapper<Item>> inp = {std::ref(infp)};
- exportfun(inp, bin, testidx);
- }*/
-
- ASSERT_TRUE(valid.first);
-
- Item infp(nfp.first);
-
- int i = 0;
- auto rorbiter = orbiter.transformedShape();
- auto vo = nfp::referenceVertex(rorbiter);
-
- ASSERT_TRUE(stationary.isInside(infp));
-
- for(auto v : infp) {
- auto dx = getX(v) - getX(vo);
- auto dy = getY(v) - getY(vo);
-
- Item tmp = orbiter;
-
- tmp.translate({dx, dy});
-
- bool touching = Item::touches(tmp, stationary);
-
- if(!touching || !valid.first) {
- std::vector<std::reference_wrapper<Item>> inp = {
- std::ref(stationary), std::ref(tmp), std::ref(infp)
- };
-
- exportfun(inp, bin, testcase*i++);
- }
-
- ASSERT_TRUE(touching);
- }
- };
-
- unsigned tidx = 0;
- for(auto& td : testdata) {
- auto orbiter = td.orbiter;
- auto stationary = td.stationary;
- onetest(orbiter, stationary, tidx++);
- }
-
- tidx = 0;
- for(auto& td : testdata) {
- auto orbiter = td.stationary;
- auto stationary = td.orbiter;
- onetest(orbiter, stationary, tidx++);
- }
-}
-}
-
-TEST(GeometryAlgorithms, nfpConvexConvex) {
- testNfp<nfp::NfpLevel::CONVEX_ONLY, 1>(nfp_testdata);
-}
-
-//TEST(GeometryAlgorithms, nfpConcaveConcave) {
-// testNfp<NfpLevel::BOTH_CONCAVE, 1000>(nfp_concave_testdata);
-//}
-
-TEST(GeometryAlgorithms, pointOnPolygonContour) {
- using namespace libnest2d;
-
- Rectangle input(10, 10);
-
- placers::EdgeCache<PolygonImpl> ecache(input);
-
- auto first = *input.begin();
- ASSERT_TRUE(getX(first) == getX(ecache.coords(0)));
- ASSERT_TRUE(getY(first) == getY(ecache.coords(0)));
-
- auto last = *std::prev(input.end());
- ASSERT_TRUE(getX(last) == getX(ecache.coords(1.0)));
- ASSERT_TRUE(getY(last) == getY(ecache.coords(1.0)));
-
- for(int i = 0; i <= 100; i++) {
- auto v = ecache.coords(i*(0.01));
- ASSERT_TRUE(shapelike::touches(v, input.transformedShape()));
- }
-}
-
-TEST(GeometryAlgorithms, mergePileWithPolygon) {
- using namespace libnest2d;
-
- Rectangle rect1(10, 15);
- Rectangle rect2(15, 15);
- Rectangle rect3(20, 15);
-
- rect2.translate({10, 0});
- rect3.translate({25, 0});
-
- TMultiShape<PolygonImpl> pile;
- pile.push_back(rect1.transformedShape());
- pile.push_back(rect2.transformedShape());
-
- auto result = nfp::merge(pile, rect3.transformedShape());
-
- ASSERT_EQ(result.size(), 1);
-
- Rectangle ref(45, 15);
-
- ASSERT_EQ(shapelike::area(result.front()), ref.area());
-}
-
-namespace {
-
-long double refMinAreaBox(const PolygonImpl& p) {
-
- auto it = sl::cbegin(p), itx = std::next(it);
-
- long double min_area = std::numeric_limits<long double>::max();
-
-
- auto update_min = [&min_area, &it, &itx, &p]() {
- Segment s(*it, *itx);
-
- PolygonImpl rotated = p;
- sl::rotate(rotated, -s.angleToXaxis());
- auto bb = sl::boundingBox(rotated);
- auto area = cast<long double>(sl::area(bb));
- if(min_area > area) min_area = area;
- };
-
- while(itx != sl::cend(p)) {
- update_min();
- ++it; ++itx;
- }
-
- it = std::prev(sl::cend(p)); itx = sl::cbegin(p);
- update_min();
-
- return min_area;
-}
-
-template<class T> struct BoostGCD {
- T operator()(const T &a, const T &b) { return boost::gcd(a, b); }
-};
-
-using Unit = int64_t;
-using Ratio = boost::rational<boost::multiprecision::int128_t>;
-
-}
-
-//TEST(GeometryAlgorithms, MinAreaBBCClk) {
-// auto u = [](ClipperLib::cInt n) { return n*1000000; };
-// PolygonImpl poly({ {u(0), u(0)}, {u(4), u(1)}, {u(2), u(4)}});
-
-// long double arearef = refMinAreaBox(poly);
-// long double area = minAreaBoundingBox<PolygonImpl, Unit, Ratio>(poly).area();
-
-// ASSERT_LE(std::abs(area - arearef), 500e6 );
-//}
-
-TEST(GeometryAlgorithms, MinAreaBBWithRotatingCalipers) {
- long double err_epsilon = 500e6l;
-
- for(ClipperLib::Path rinput : PRINTER_PART_POLYGONS) {
- PolygonImpl poly(rinput);
-
- long double arearef = refMinAreaBox(poly);
- auto bb = minAreaBoundingBox<PathImpl, Unit, Ratio>(rinput);
- long double area = cast<long double>(bb.area());
-
- bool succ = std::abs(arearef - area) < err_epsilon;
-
- ASSERT_TRUE(succ);
- }
-
- for(ClipperLib::Path rinput : STEGOSAUR_POLYGONS) {
- rinput.pop_back();
- std::reverse(rinput.begin(), rinput.end());
-
- PolygonImpl poly(removeCollinearPoints<PathImpl, PointImpl, Unit>(rinput, 1000000));
-
- long double arearef = refMinAreaBox(poly);
- auto bb = minAreaBoundingBox<PolygonImpl, Unit, Ratio>(poly);
- long double area = cast<long double>(bb.area());
-
-
- bool succ = std::abs(arearef - area) < err_epsilon;
-
- ASSERT_TRUE(succ);
- }
-}
-
-int main(int argc, char **argv) {
- ::testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
-}
diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt
index 633549b42..cbaa24e9c 100644
--- a/src/libslic3r/CMakeLists.txt
+++ b/src/libslic3r/CMakeLists.txt
@@ -204,7 +204,7 @@ if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY)
add_precompiled_header(libslic3r pchheader.hpp FORCEINCLUDE)
endif ()
-target_compile_definitions(libslic3r PUBLIC -DUSE_TBB)
+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
libnest2d
@@ -221,7 +221,7 @@ target_link_libraries(libslic3r
poly2tri
qhull
semver
- tbb
+ TBB::tbb
${CMAKE_DL_LIBS}
)
diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp
index 63bd0c801..6e31beb9d 100644
--- a/src/libslic3r/Config.cpp
+++ b/src/libslic3r/Config.cpp
@@ -668,6 +668,12 @@ void ConfigBase::null_nullables()
}
}
+DynamicConfig::DynamicConfig(const ConfigBase& rhs, const t_config_option_keys& keys)
+{
+ for (const t_config_option_key& opt_key : keys)
+ this->options[opt_key] = std::unique_ptr<ConfigOption>(rhs.option(opt_key)->clone());
+}
+
bool DynamicConfig::operator==(const DynamicConfig &rhs) const
{
auto it1 = this->options.begin();
diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp
index 463f6ef2d..9c3aab4dd 100644
--- a/src/libslic3r/Config.hpp
+++ b/src/libslic3r/Config.hpp
@@ -1580,9 +1580,11 @@ class DynamicConfig : public virtual ConfigBase
{
public:
DynamicConfig() {}
- DynamicConfig(const DynamicConfig& other) { *this = other; }
- DynamicConfig(DynamicConfig&& other) : options(std::move(other.options)) { other.options.clear(); }
- virtual ~DynamicConfig() override { clear(); }
+ DynamicConfig(const DynamicConfig &rhs) { *this = rhs; }
+ DynamicConfig(DynamicConfig &&rhs) : options(std::move(rhs.options)) { rhs.options.clear(); }
+ explicit DynamicConfig(const ConfigBase &rhs, const t_config_option_keys &keys);
+ explicit DynamicConfig(const ConfigBase& rhs) : DynamicConfig(rhs, rhs.keys()) {}
+ virtual ~DynamicConfig() override { clear(); }
// Copy a content of one DynamicConfig to another DynamicConfig.
// If rhs.def() is not null, then it has to be equal to this->def().
diff --git a/src/libslic3r/ExPolygon.hpp b/src/libslic3r/ExPolygon.hpp
index a7ef2784c..c510b848f 100644
--- a/src/libslic3r/ExPolygon.hpp
+++ b/src/libslic3r/ExPolygon.hpp
@@ -18,15 +18,20 @@ class ExPolygon
{
public:
ExPolygon() {}
- ExPolygon(const ExPolygon &other) : contour(other.contour), holes(other.holes) {}
+ ExPolygon(const ExPolygon &other) : contour(other.contour), holes(other.holes) {}
ExPolygon(ExPolygon &&other) : contour(std::move(other.contour)), holes(std::move(other.holes)) {}
+ explicit ExPolygon(const Polygon &contour) : contour(contour) {}
+ explicit ExPolygon(Polygon &&contour) : contour(std::move(contour)) {}
+ explicit ExPolygon(const Points &contour) : contour(contour) {}
+ explicit ExPolygon(Points &&contour) : contour(std::move(contour)) {}
+ explicit ExPolygon(const Polygon &contour, const Polygon &hole) : contour(contour) { holes.emplace_back(hole); }
+ explicit ExPolygon(Polygon &&contour, Polygon &&hole) : contour(std::move(contour)) { holes.emplace_back(std::move(hole)); }
+ explicit ExPolygon(const Points &contour, const Points &hole) : contour(contour) { holes.emplace_back(hole); }
+ explicit ExPolygon(Points &&contour, Polygon &&hole) : contour(std::move(contour)) { holes.emplace_back(std::move(hole)); }
ExPolygon& operator=(const ExPolygon &other) { contour = other.contour; holes = other.holes; return *this; }
ExPolygon& operator=(ExPolygon &&other) { contour = std::move(other.contour); holes = std::move(other.holes); return *this; }
- inline explicit ExPolygon(const Polygon &p): contour(p) {}
- inline explicit ExPolygon(Polygon &&p): contour(std::move(p)) {}
-
Polygon contour;
Polygons holes;
diff --git a/src/libslic3r/Fill/Fill.hpp b/src/libslic3r/Fill/Fill.hpp
index c04305c04..9e3545084 100644
--- a/src/libslic3r/Fill/Fill.hpp
+++ b/src/libslic3r/Fill/Fill.hpp
@@ -29,8 +29,6 @@ public:
FillParams params;
};
-void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out);
-
} // namespace Slic3r
#endif // slic3r_Fill_hpp_
diff --git a/src/libslic3r/Fill/FillBase.hpp b/src/libslic3r/Fill/FillBase.hpp
index fb7947893..3b834dede 100644
--- a/src/libslic3r/Fill/FillBase.hpp
+++ b/src/libslic3r/Fill/FillBase.hpp
@@ -15,6 +15,7 @@
namespace Slic3r {
+class ExPolygon;
class Surface;
struct FillParams
diff --git a/src/libslic3r/GCode/CoolingBuffer.hpp b/src/libslic3r/GCode/CoolingBuffer.hpp
index 511089ad0..b0c35ecc5 100644
--- a/src/libslic3r/GCode/CoolingBuffer.hpp
+++ b/src/libslic3r/GCode/CoolingBuffer.hpp
@@ -1,7 +1,7 @@
#ifndef slic3r_CoolingBuffer_hpp_
#define slic3r_CoolingBuffer_hpp_
-#include "libslic3r.h"
+#include "../libslic3r.h"
#include <map>
#include <string>
diff --git a/src/libslic3r/GCodeReader.hpp b/src/libslic3r/GCodeReader.hpp
index f64605a9c..24f979267 100644
--- a/src/libslic3r/GCodeReader.hpp
+++ b/src/libslic3r/GCodeReader.hpp
@@ -29,6 +29,8 @@ public:
float value(Axis axis) const { return m_axis[axis]; }
bool has(char axis) const;
bool has_value(char axis, float &value) const;
+ float new_X(const GCodeReader &reader) const { return this->has(X) ? this->x() : reader.x(); }
+ float new_Y(const GCodeReader &reader) const { return this->has(Y) ? this->y() : reader.y(); }
float new_Z(const GCodeReader &reader) const { return this->has(Z) ? this->z() : reader.z(); }
float new_E(const GCodeReader &reader) const { return this->has(E) ? this->e() : reader.e(); }
float new_F(const GCodeReader &reader) const { return this->has(F) ? this->f() : reader.f(); }
diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp
index 51fca58f6..364ba12ae 100644
--- a/src/libslic3r/GCodeWriter.cpp
+++ b/src/libslic3r/GCodeWriter.cpp
@@ -269,7 +269,7 @@ std::string GCodeWriter::set_speed(double F, const std::string &comment, const s
assert(F > 0.);
assert(F < 100000.);
std::ostringstream gcode;
- gcode << "G1 F" << F;
+ gcode << "G1 F" << XYZF_NUM(F);
COMMENT(comment);
gcode << cooling_marker;
gcode << "\n";
diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp
index 3adf8c670..e926b9997 100644
--- a/src/libslic3r/Geometry.cpp
+++ b/src/libslic3r/Geometry.cpp
@@ -9,6 +9,7 @@
#include <cmath>
#include <list>
#include <map>
+#include <numeric>
#include <set>
#include <utility>
#include <stack>
@@ -16,6 +17,7 @@
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
+#include <boost/log/trivial.hpp>
#ifdef SLIC3R_DEBUG
#include "SVG.hpp"
@@ -335,6 +337,93 @@ double rad2deg_dir(double angle)
return rad2deg(angle);
}
+Point circle_taubin_newton(const Points::const_iterator& input_begin, const Points::const_iterator& input_end, size_t cycles)
+{
+ Vec2ds tmp;
+ tmp.reserve(std::distance(input_begin, input_end));
+ std::transform(input_begin, input_end, std::back_inserter(tmp), [] (const Point& in) { return unscale(in); } );
+ Vec2d center = circle_taubin_newton(tmp.cbegin(), tmp.end(), cycles);
+ return Point::new_scale(center.x(), center.y());
+}
+
+/// Adapted from work in "Circular and Linear Regression: Fitting circles and lines by least squares", pg 126
+/// Returns a point corresponding to the center of a circle for which all of the points from input_begin to input_end
+/// lie on.
+Vec2d circle_taubin_newton(const Vec2ds::const_iterator& input_begin, const Vec2ds::const_iterator& input_end, size_t cycles)
+{
+ // calculate the centroid of the data set
+ const Vec2d sum = std::accumulate(input_begin, input_end, Vec2d(0,0));
+ const size_t n = std::distance(input_begin, input_end);
+ const double n_flt = static_cast<double>(n);
+ const Vec2d centroid { sum / n_flt };
+
+ // Compute the normalized moments of the data set.
+ double Mxx = 0, Myy = 0, Mxy = 0, Mxz = 0, Myz = 0, Mzz = 0;
+ for (auto it = input_begin; it < input_end; ++it) {
+ // center/normalize the data.
+ double Xi {it->x() - centroid.x()};
+ double Yi {it->y() - centroid.y()};
+ double Zi {Xi*Xi + Yi*Yi};
+ Mxy += (Xi*Yi);
+ Mxx += (Xi*Xi);
+ Myy += (Yi*Yi);
+ Mxz += (Xi*Zi);
+ Myz += (Yi*Zi);
+ Mzz += (Zi*Zi);
+ }
+
+ // divide by number of points to get the moments
+ Mxx /= n_flt;
+ Myy /= n_flt;
+ Mxy /= n_flt;
+ Mxz /= n_flt;
+ Myz /= n_flt;
+ Mzz /= n_flt;
+
+ // Compute the coefficients of the characteristic polynomial for the circle
+ // eq 5.60
+ const double Mz {Mxx + Myy}; // xx + yy = z
+ const double Cov_xy {Mxx*Myy - Mxy*Mxy}; // this shows up a couple times so cache it here.
+ const double C3 {4.0*Mz};
+ const double C2 {-3.0*(Mz*Mz) - Mzz};
+ const double C1 {Mz*(Mzz - (Mz*Mz)) + 4.0*Mz*Cov_xy - (Mxz*Mxz) - (Myz*Myz)};
+ const double C0 {(Mxz*Mxz)*Myy + (Myz*Myz)*Mxx - 2.0*Mxz*Myz*Mxy - Cov_xy*(Mzz - (Mz*Mz))};
+
+ const double C22 = {C2 + C2};
+ const double C33 = {C3 + C3 + C3};
+
+ // solve the characteristic polynomial with Newton's method.
+ double xnew = 0.0;
+ double ynew = 1e20;
+
+ for (size_t i = 0; i < cycles; ++i) {
+ const double yold {ynew};
+ ynew = C0 + xnew * (C1 + xnew*(C2 + xnew * C3));
+ if (std::abs(ynew) > std::abs(yold)) {
+ BOOST_LOG_TRIVIAL(error) << "Geometry: Fit is going in the wrong direction.\n";
+ return Vec2d(std::nan(""), std::nan(""));
+ }
+ const double Dy {C1 + xnew*(C22 + xnew*C33)};
+
+ const double xold {xnew};
+ xnew = xold - (ynew / Dy);
+
+ if (std::abs((xnew-xold) / xnew) < 1e-12) i = cycles; // converged, we're done here
+
+ if (xnew < 0) {
+ // reset, we went negative
+ xnew = 0.0;
+ }
+ }
+
+ // compute the determinant and the circle's parameters now that we've solved.
+ double DET = xnew*xnew - xnew*Mz + Cov_xy;
+
+ Vec2d center(Mxz * (Myy - xnew) - Myz * Mxy, Myz * (Mxx - xnew) - Mxz*Mxy);
+ center /= (DET * 2.);
+ return center + centroid;
+}
+
void simplify_polygons(const Polygons &polygons, double tolerance, Polygons* retval)
{
Polygons pp;
diff --git a/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp
index 32b66663e..44303711b 100644
--- a/src/libslic3r/Geometry.hpp
+++ b/src/libslic3r/Geometry.hpp
@@ -162,6 +162,15 @@ template<typename T> T angle_to_0_2PI(T angle)
return angle;
}
+
+/// Find the center of the circle corresponding to the vector of Points as an arc.
+Point circle_taubin_newton(const Points::const_iterator& input_start, const Points::const_iterator& input_end, size_t cycles = 20);
+inline Point circle_taubin_newton(const Points& input, size_t cycles = 20) { return circle_taubin_newton(input.cbegin(), input.cend(), cycles); }
+
+/// Find the center of the circle corresponding to the vector of Pointfs as an arc.
+Vec2d circle_taubin_newton(const Vec2ds::const_iterator& input_start, const Vec2ds::const_iterator& input_end, size_t cycles = 20);
+inline Vec2d circle_taubin_newton(const Vec2ds& input, size_t cycles = 20) { return circle_taubin_newton(input.cbegin(), input.cend(), cycles); }
+
void simplify_polygons(const Polygons &polygons, double tolerance, Polygons* retval);
double linint(double value, double oldmin, double oldmax, double newmin, double newmax);
diff --git a/src/libslic3r/Line.cpp b/src/libslic3r/Line.cpp
index baa04795a..e5f7b8fa9 100644
--- a/src/libslic3r/Line.cpp
+++ b/src/libslic3r/Line.cpp
@@ -86,10 +86,7 @@ bool Line::intersection(const Line &l2, Point *intersection) const
const Line &l1 = *this;
const Vec2d v1 = (l1.b - l1.a).cast<double>();
const Vec2d v2 = (l2.b - l2.a).cast<double>();
- const Vec2d v12 = (l1.a - l2.a).cast<double>();
double denom = cross2(v1, v2);
- double nume_a = cross2(v2, v12);
- double nume_b = cross2(v1, v12);
if (fabs(denom) < EPSILON)
#if 0
// Lines are collinear. Return true if they are coincident (overlappign).
@@ -97,6 +94,9 @@ bool Line::intersection(const Line &l2, Point *intersection) const
#else
return false;
#endif
+ const Vec2d v12 = (l1.a - l2.a).cast<double>();
+ double nume_a = cross2(v2, v12);
+ double nume_b = cross2(v1, v12);
double t1 = nume_a / denom;
double t2 = nume_b / denom;
if (t1 >= 0 && t1 <= 1.0f && t2 >= 0 && t2 <= 1.0f) {
diff --git a/src/libslic3r/Point.hpp b/src/libslic3r/Point.hpp
index 994f45e59..dced5c02a 100644
--- a/src/libslic3r/Point.hpp
+++ b/src/libslic3r/Point.hpp
@@ -38,6 +38,7 @@ typedef std::vector<Point*> PointPtrs;
typedef std::vector<const Point*> PointConstPtrs;
typedef std::vector<Vec3crd> Points3;
typedef std::vector<Vec2d> Pointfs;
+typedef std::vector<Vec2d> Vec2ds;
typedef std::vector<Vec3d> Pointf3s;
typedef Eigen::Matrix<float, 2, 2, Eigen::DontAlign> Matrix2f;
@@ -87,12 +88,13 @@ class Point : public Vec2crd
public:
typedef coord_t coord_type;
- Point() : Vec2crd() { (*this)(0) = 0; (*this)(1) = 0; }
- Point(coord_t x, coord_t y) { (*this)(0) = x; (*this)(1) = y; }
- Point(int64_t x, int64_t y) { (*this)(0) = coord_t(x); (*this)(1) = coord_t(y); } // for Clipper
- Point(double x, double y) { (*this)(0) = coord_t(lrint(x)); (*this)(1) = coord_t(lrint(y)); }
+ Point() : Vec2crd(0, 0) {}
+ Point(coord_t x, coord_t y) : Vec2crd(x, y) {}
+ Point(int64_t x, int64_t y) : Vec2crd(coord_t(x), coord_t(y)) {} // for Clipper
+ Point(double x, double y) : Vec2crd(coord_t(lrint(x)), coord_t(lrint(y))) {}
Point(const Point &rhs) { *this = rhs; }
- // This constructor allows you to construct Point from Eigen expressions
+ explicit Point(const Vec2d& rhs) : Vec2crd(coord_t(lrint(rhs.x())), coord_t(lrint(rhs.y()))) {}
+ // This constructor allows you to construct Point from Eigen expressions
template<typename OtherDerived>
Point(const Eigen::MatrixBase<OtherDerived> &other) : Vec2crd(other) {}
static Point new_scale(coordf_t x, coordf_t y) { return Point(coord_t(scale_(x)), coord_t(scale_(y))); }
@@ -126,6 +128,36 @@ public:
Point projection_onto(const Line &line) const;
};
+inline bool is_approx(const Point &p1, const Point &p2, coord_t epsilon = coord_t(SCALED_EPSILON))
+{
+ Point d = (p2 - p1).cwiseAbs();
+ return d.x() < epsilon && d.y() < epsilon;
+}
+
+inline bool is_approx(const Vec2f &p1, const Vec2f &p2, float epsilon = float(EPSILON))
+{
+ Vec2f d = (p2 - p1).cwiseAbs();
+ return d.x() < epsilon && d.y() < epsilon;
+}
+
+inline bool is_approx(const Vec2d &p1, const Vec2d &p2, double epsilon = EPSILON)
+{
+ Vec2d d = (p2 - p1).cwiseAbs();
+ return d.x() < epsilon && d.y() < epsilon;
+}
+
+inline bool is_approx(const Vec3f &p1, const Vec3f &p2, float epsilon = float(EPSILON))
+{
+ Vec3f d = (p2 - p1).cwiseAbs();
+ return d.x() < epsilon && d.y() < epsilon && d.z() < epsilon;
+}
+
+inline bool is_approx(const Vec3d &p1, const Vec3d &p2, double epsilon = EPSILON)
+{
+ Vec3d d = (p2 - p1).cwiseAbs();
+ return d.x() < epsilon && d.y() < epsilon && d.z() < epsilon;
+}
+
namespace int128 {
// Exact orientation predicate,
// returns +1: CCW, 0: collinear, -1: CW.
diff --git a/src/libslic3r/Polygon.cpp b/src/libslic3r/Polygon.cpp
index 5ef5d0ceb..bd5ec3de5 100644
--- a/src/libslic3r/Polygon.cpp
+++ b/src/libslic3r/Polygon.cpp
@@ -15,7 +15,7 @@ Polyline Polygon::split_at_vertex(const Point &point) const
// find index of point
for (const Point &pt : this->points)
if (pt == point)
- return this->split_at_index(&pt - &this->points.front());
+ return this->split_at_index(int(&pt - &this->points.front()));
throw std::invalid_argument("Point not found");
return Polyline();
}
@@ -175,16 +175,16 @@ Point Polygon::centroid() const
Points Polygon::concave_points(double angle) const
{
Points points;
- angle = 2*PI - angle;
+ angle = 2. * PI - angle + EPSILON;
// check whether first point forms a concave angle
if (this->points.front().ccw_angle(this->points.back(), *(this->points.begin()+1)) <= angle)
points.push_back(this->points.front());
// check whether points 1..(n-1) form concave angles
- for (Points::const_iterator p = this->points.begin()+1; p != this->points.end()-1; ++p) {
- if (p->ccw_angle(*(p-1), *(p+1)) <= angle) points.push_back(*p);
- }
+ for (Points::const_iterator p = this->points.begin()+1; p != this->points.end()-1; ++ p)
+ if (p->ccw_angle(*(p-1), *(p+1)) <= angle)
+ points.push_back(*p);
// check whether last point forms a concave angle
if (this->points.back().ccw_angle(*(this->points.end()-2), this->points.front()) <= angle)
@@ -198,7 +198,7 @@ Points Polygon::concave_points(double angle) const
Points Polygon::convex_points(double angle) const
{
Points points;
- angle = 2*PI - angle;
+ angle = 2*PI - angle - EPSILON;
// check whether first point forms a convex angle
if (this->points.front().ccw_angle(this->points.back(), *(this->points.begin()+1)) >= angle)
@@ -394,4 +394,45 @@ bool remove_small(Polygons &polys, double min_area)
return modified;
}
+void remove_collinear(Polygon &poly)
+{
+ if (poly.points.size() > 2) {
+ // copy points and append both 1 and last point in place to cover the boundaries
+ Points pp;
+ pp.reserve(poly.points.size()+2);
+ pp.push_back(poly.points.back());
+ pp.insert(pp.begin()+1, poly.points.begin(), poly.points.end());
+ pp.push_back(poly.points.front());
+ // delete old points vector. Will be re-filled in the loop
+ poly.points.clear();
+
+ size_t i = 0;
+ size_t k = 0;
+ while (i < pp.size()-2) {
+ k = i+1;
+ const Point &p1 = pp[i];
+ while (k < pp.size()-1) {
+ const Point &p2 = pp[k];
+ const Point &p3 = pp[k+1];
+ Line l(p1, p3);
+ if(l.distance_to(p2) < SCALED_EPSILON) {
+ k++;
+ } else {
+ if(i > 0) poly.points.push_back(p1); // implicitly removes the first point we appended above
+ i = k;
+ break;
+ }
+ }
+ if(k > pp.size()-2) break; // all remaining points are collinear and can be skipped
+ }
+ poly.points.push_back(pp[i]);
+ }
+}
+
+void remove_collinear(Polygons &polys)
+{
+ for (Polygon &poly : polys)
+ remove_collinear(poly);
+}
+
}
diff --git a/src/libslic3r/Polygon.hpp b/src/libslic3r/Polygon.hpp
index 42f938c72..19be3068b 100644
--- a/src/libslic3r/Polygon.hpp
+++ b/src/libslic3r/Polygon.hpp
@@ -86,6 +86,8 @@ extern bool remove_sticks(Polygons &polys);
// Remove polygons with less than 3 edges.
extern bool remove_degenerate(Polygons &polys);
extern bool remove_small(Polygons &polys, double min_area);
+extern void remove_collinear(Polygon &poly);
+extern void remove_collinear(Polygons &polys);
// Append a vector of polygons at the end of another vector of polygons.
inline void polygons_append(Polygons &dst, const Polygons &src) { dst.insert(dst.end(), src.begin(), src.end()); }
diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp
index b3957218d..59d7b7036 100644
--- a/src/libslic3r/PrintConfig.cpp
+++ b/src/libslic3r/PrintConfig.cpp
@@ -29,7 +29,7 @@ PrintConfigDef::PrintConfigDef()
this->init_common_params();
assign_printer_technology_to_unknown(this->options, ptAny);
this->init_fff_params();
- this->init_extruder_retract_keys();
+ this->init_extruder_option_keys();
assign_printer_technology_to_unknown(this->options, ptFFF);
this->init_sla_params();
assign_printer_technology_to_unknown(this->options, ptSLA);
@@ -2270,8 +2270,17 @@ void PrintConfigDef::init_fff_params()
}
}
-void PrintConfigDef::init_extruder_retract_keys()
+void PrintConfigDef::init_extruder_option_keys()
{
+ // ConfigOptionFloats, ConfigOptionPercents, ConfigOptionBools, ConfigOptionStrings
+ m_extruder_option_keys = {
+ "nozzle_diameter", "min_layer_height", "max_layer_height", "extruder_offset",
+ "retract_length", "retract_lift", "retract_lift_above", "retract_lift_below", "retract_speed", "deretract_speed",
+ "retract_before_wipe", "retract_restart_extra", "retract_before_travel", "wipe",
+ "retract_layer_change", "retract_length_toolchange", "retract_restart_extra_toolchange", "extruder_colour",
+ "default_filament_profile"
+ };
+
m_extruder_retract_keys = {
"deretract_speed",
"retract_before_travel",
@@ -2889,9 +2898,13 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
const PrintConfigDef print_config_def;
-DynamicPrintConfig* DynamicPrintConfig::new_from_defaults()
+DynamicPrintConfig DynamicPrintConfig::full_print_config()
+{
+ return DynamicPrintConfig((const PrintRegionConfig&)FullPrintConfig::defaults());
+}
+
+DynamicPrintConfig::DynamicPrintConfig(const StaticPrintConfig& rhs) : DynamicConfig(rhs, rhs.keys_ref())
{
- return new_from_defaults_keys(FullPrintConfig::defaults().keys());
}
DynamicPrintConfig* DynamicPrintConfig::new_from_defaults_keys(const std::vector<std::string> &keys)
@@ -2938,6 +2951,20 @@ void DynamicPrintConfig::normalize()
}
}
+void DynamicPrintConfig::set_num_extruders(unsigned int num_extruders)
+{
+ const auto &defaults = FullPrintConfig::defaults();
+ for (const std::string &key : print_config_def.extruder_option_keys()) {
+ if (key == "default_filament_profile")
+ continue;
+ auto *opt = this->option(key, false);
+ assert(opt != nullptr);
+ assert(opt->is_vector());
+ if (opt != nullptr && opt->is_vector())
+ static_cast<ConfigOptionVectorBase*>(opt)->resize(num_extruders, defaults.option(key));
+ }
+}
+
std::string DynamicPrintConfig::validate()
{
// Full print config is initialized from the defaults.
diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp
index 372e70e34..5c287ba93 100644
--- a/src/libslic3r/PrintConfig.hpp
+++ b/src/libslic3r/PrintConfig.hpp
@@ -193,6 +193,8 @@ public:
static void handle_legacy(t_config_option_key &opt_key, std::string &value);
+ // Array options growing with the number of extruders
+ const std::vector<std::string>& extruder_option_keys() const { return m_extruder_option_keys; }
// Options defining the extruder retract properties. These keys are sorted lexicographically.
// The extruder retract keys could be overidden by the same values defined at the Filament level
// (then the key is further prefixed with the "filament_" prefix).
@@ -201,9 +203,10 @@ public:
private:
void init_common_params();
void init_fff_params();
- void init_extruder_retract_keys();
+ void init_extruder_option_keys();
void init_sla_params();
+ std::vector<std::string> m_extruder_option_keys;
std::vector<std::string> m_extruder_retract_keys;
};
@@ -211,6 +214,8 @@ private:
// This definition is constant.
extern const PrintConfigDef print_config_def;
+class StaticPrintConfig;
+
// Slic3r dynamic configuration, used to override the configuration
// per object, per modification volume or per printing material.
// The dynamic configuration is also used to store user modifications of the print global parameters,
@@ -221,9 +226,11 @@ class DynamicPrintConfig : public DynamicConfig
{
public:
DynamicPrintConfig() {}
- DynamicPrintConfig(const DynamicPrintConfig &other) : DynamicConfig(other) {}
+ DynamicPrintConfig(const DynamicPrintConfig &rhs) : DynamicConfig(rhs) {}
+ explicit DynamicPrintConfig(const StaticPrintConfig &rhs);
+ explicit DynamicPrintConfig(const ConfigBase &rhs) : DynamicConfig(rhs) {}
- static DynamicPrintConfig* new_from_defaults();
+ static DynamicPrintConfig full_print_config();
static DynamicPrintConfig* new_from_defaults_keys(const std::vector<std::string> &keys);
// Overrides ConfigBase::def(). Static configuration definition. Any value stored into this ConfigBase shall have its definition here.
@@ -231,6 +238,8 @@ public:
void normalize();
+ void set_num_extruders(unsigned int num_extruders);
+
// Validate the PrintConfig. Returns an empty string on success, otherwise an error message is returned.
std::string validate();
@@ -257,6 +266,8 @@ public:
// Overrides ConfigBase::def(). Static configuration definition. Any value stored into this ConfigBase shall have its definition here.
const ConfigDef* def() const override { return &print_config_def; }
+ // Reference to the cached list of keys.
+ virtual const t_config_option_keys& keys_ref() const = 0;
protected:
// Verify whether the opt_key has not been obsoleted or renamed.
@@ -345,6 +356,7 @@ public: \
{ return s_cache_##CLASS_NAME.optptr(opt_key, this); } \
/* Overrides ConfigBase::keys(). Collect names of all configuration values maintained by this configuration store. */ \
t_config_option_keys keys() const override { return s_cache_##CLASS_NAME.keys(); } \
+ const t_config_option_keys& keys_ref() const override { return s_cache_##CLASS_NAME.keys(); } \
static const CLASS_NAME& defaults() { initialize_cache(); return s_cache_##CLASS_NAME.defaults(); } \
private: \
static void initialize_cache() \
diff --git a/src/libslic3r/Time.cpp b/src/libslic3r/Time.cpp
index 063dbb41c..8faa14ade 100644
--- a/src/libslic3r/Time.cpp
+++ b/src/libslic3r/Time.cpp
@@ -11,7 +11,7 @@
#include <map>
#endif
-#include "libslic3r/Utils.hpp"
+// #include "libslic3r/Utils.hpp"
namespace Slic3r {
namespace Utils {
diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp
index 16d289d9c..5cd97522d 100644
--- a/src/libslic3r/TriangleMesh.cpp
+++ b/src/libslic3r/TriangleMesh.cpp
@@ -593,6 +593,16 @@ TriangleMesh TriangleMesh::convex_hull_3d() const
return output_mesh;
}
+std::vector<ExPolygons> TriangleMesh::slice(const std::vector<double> &z)
+{
+ // convert doubles to floats
+ std::vector<float> z_f(z.begin(), z.end());
+ TriangleMeshSlicer mslicer(this);
+ std::vector<ExPolygons> layers;
+ mslicer.slice(z_f, 0.0004f, &layers, [](){});
+ return layers;
+}
+
void TriangleMesh::require_shared_vertices()
{
BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - start";
@@ -1861,7 +1871,8 @@ void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower)
}
// Generate the vertex list for a cube solid of arbitrary size in X/Y/Z.
-TriangleMesh make_cube(double x, double y, double z) {
+TriangleMesh make_cube(double x, double y, double z)
+{
Vec3d pv[8] = {
Vec3d(x, y, 0), Vec3d(x, 0, 0), Vec3d(0, 0, 0),
Vec3d(0, y, 0), Vec3d(x, y, z), Vec3d(0, y, z),
@@ -1878,7 +1889,8 @@ TriangleMesh make_cube(double x, double y, double z) {
Pointf3s vertices(&pv[0], &pv[0]+8);
TriangleMesh mesh(vertices ,facets);
- return mesh;
+ mesh.repair();
+ return mesh;
}
// Generate the mesh for a cylinder and return it, using
@@ -1922,7 +1934,9 @@ TriangleMesh make_cylinder(double r, double h, double fa)
facets.emplace_back(Vec3crd(id, 2, 3));
facets.emplace_back(Vec3crd(id, id - 1, 2));
- return TriangleMesh(std::move(vertices), std::move(facets));
+ TriangleMesh mesh(std::move(vertices), std::move(facets));
+ mesh.repair();
+ return mesh;
}
// Generates mesh for a sphere centered about the origin, using the generated angle
@@ -1978,7 +1992,9 @@ TriangleMesh make_sphere(double radius, double fa)
k2 = k2_next;
}
}
- return TriangleMesh(std::move(vertices), std::move(facets));
+ TriangleMesh mesh(std::move(vertices), std::move(facets));
+ mesh.repair();
+ return mesh;
}
}
diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp
index 86ca1625e..9c9f82040 100644
--- a/src/libslic3r/TriangleMesh.hpp
+++ b/src/libslic3r/TriangleMesh.hpp
@@ -58,8 +58,14 @@ public:
BoundingBoxf3 bounding_box() const;
// Returns the bbox of this TriangleMesh transformed by the given transformation
BoundingBoxf3 transformed_bounding_box(const Transform3d &trafo) const;
+ // Return the size of the mesh in coordinates.
+ Vec3d size() const { return stl.stats.size.cast<double>(); }
+ /// Return the center of the related bounding box.
+ Vec3d center() const { return this->bounding_box().center(); }
// Returns the convex hull of this TriangleMesh
TriangleMesh convex_hull_3d() const;
+ // Slice this mesh at the provided Z levels and return the vector
+ std::vector<ExPolygons> slice(const std::vector<double>& z);
void reset_repair_stats();
bool needed_repair() const;
void require_shared_vertices();
diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp
index ae995aa44..a360aaf1a 100644
--- a/src/slic3r/GUI/Preset.cpp
+++ b/src/slic3r/GUI/Preset.cpp
@@ -245,27 +245,13 @@ std::string Preset::remove_suffix_modified(const std::string &name)
name;
}
-void Preset::set_num_extruders(DynamicPrintConfig &config, unsigned int num_extruders)
-{
- const auto &defaults = FullPrintConfig::defaults();
- for (const std::string &key : Preset::nozzle_options()) {
- if (key == "default_filament_profile")
- continue;
- auto *opt = config.option(key, false);
- assert(opt != nullptr);
- assert(opt->is_vector());
- if (opt != nullptr && opt->is_vector())
- static_cast<ConfigOptionVectorBase*>(opt)->resize(num_extruders, defaults.option(key));
- }
-}
-
// Update new extruder fields at the printer profile.
void Preset::normalize(DynamicPrintConfig &config)
{
auto *nozzle_diameter = dynamic_cast<const ConfigOptionFloats*>(config.option("nozzle_diameter"));
if (nozzle_diameter != nullptr)
// Loaded the FFF Printer settings. Verify, that all extruder dependent values have enough values.
- set_num_extruders(config, (unsigned int)nozzle_diameter->values.size());
+ config.set_num_extruders((unsigned int)nozzle_diameter->values.size());
if (config.option("filament_diameter") != nullptr) {
// This config contains single or multiple filament presets.
// Ensure that the filament preset vector options contain the correct number of values.
@@ -469,15 +455,7 @@ const std::vector<std::string>& Preset::printer_options()
// of the nozzle_diameter vector.
const std::vector<std::string>& Preset::nozzle_options()
{
- // ConfigOptionFloats, ConfigOptionPercents, ConfigOptionBools, ConfigOptionStrings
- static std::vector<std::string> s_opts {
- "nozzle_diameter", "min_layer_height", "max_layer_height", "extruder_offset",
- "retract_length", "retract_lift", "retract_lift_above", "retract_lift_below", "retract_speed", "deretract_speed",
- "retract_before_wipe", "retract_restart_extra", "retract_before_travel", "wipe",
- "retract_layer_change", "retract_length_toolchange", "retract_restart_extra_toolchange", "extruder_colour",
- "default_filament_profile"
- };
- return s_opts;
+ return print_config_def.extruder_option_keys();
}
const std::vector<std::string>& Preset::sla_print_options()
diff --git a/src/slic3r/GUI/Preset.hpp b/src/slic3r/GUI/Preset.hpp
index 6ed818719..e2e4baa88 100644
--- a/src/slic3r/GUI/Preset.hpp
+++ b/src/slic3r/GUI/Preset.hpp
@@ -202,7 +202,7 @@ public:
void set_visible_from_appconfig(const AppConfig &app_config);
// Resize the extruder specific fields, initialize them with the content of the 1st extruder.
- void set_num_extruders(unsigned int n) { set_num_extruders(this->config, n); }
+ void set_num_extruders(unsigned int n) { this->config.set_num_extruders(n); }
// Sort lexicographically by a preset name. The preset name shall be unique across a single PresetCollection.
bool operator<(const Preset &other) const { return this->name < other.name; }
@@ -227,8 +227,6 @@ public:
protected:
friend class PresetCollection;
friend class PresetBundle;
- // Resize the extruder specific vectors ()
- static void set_num_extruders(DynamicPrintConfig &config, unsigned int n);
static std::string remove_suffix_modified(const std::string &name);
};
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index e2eaebfb8..e957c0c20 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -10,24 +10,18 @@ target_include_directories(Catch2 INTERFACE ${CMAKE_CURRENT_LIST_DIR})
add_library(Catch2::Catch2 ALIAS Catch2)
include(Catch)
-add_library(test_catch2_common INTERFACE)
-target_compile_definitions(test_catch2_common INTERFACE TEST_DATA_DIR=R"\(${TEST_DATA_DIR}\)" CATCH_CONFIG_FAST_COMPILE)
-target_link_libraries(test_catch2_common INTERFACE Catch2::Catch2)
-
add_library(test_common INTERFACE)
+target_compile_definitions(test_common INTERFACE TEST_DATA_DIR=R"\(${TEST_DATA_DIR}\)" CATCH_CONFIG_FAST_COMPILE)
+target_link_libraries(test_common INTERFACE Catch2::Catch2)
+
if (APPLE)
target_link_libraries(test_common INTERFACE "-liconv -framework IOKit" "-framework CoreFoundation" -lc++)
endif()
-target_link_libraries(test_common INTERFACE test_catch2_common)
-
-# DEPRECATED:
-#find_package(GTest REQUIRED)
-#add_library(test_gtest_common INTERFACE)
-#target_compile_definitions(test_gtest_common INTERFACE TEST_DATA_DIR=R"\(${TEST_DATA_DIR}\)")
-#target_link_libraries(test_gtest_common INTERFACE GTest::GTest GTest::Main)
+set_property(GLOBAL PROPERTY USE_FOLDERS ON)
add_subdirectory(libnest2d)
+add_subdirectory(libslic3r)
add_subdirectory(timeutils)
+add_subdirectory(fff_print)
add_subdirectory(sla_print)
-
diff --git a/tests/data/fff_print_tests/test_gcodewriter/config_lift_unlift.ini b/tests/data/fff_print_tests/test_gcodewriter/config_lift_unlift.ini
new file mode 100644
index 000000000..9d44cd43e
--- /dev/null
+++ b/tests/data/fff_print_tests/test_gcodewriter/config_lift_unlift.ini
@@ -0,0 +1,30 @@
+before_layer_gcode =
+between_objects_gcode =
+end_filament_gcode = "; Filament-specific end gcode \n;END gcode for filament\n"
+end_gcode = M104 S0 ; turn off temperature\nG28 X0 ; home X axis\nM84 ; disable motors\n
+extrusion_axis = E
+extrusion_multiplier = 1
+filament_cost = 0
+filament_density = 0
+filament_diameter = 3
+filament_max_volumetric_speed = 0
+gcode_comments = 0
+gcode_flavor = reprap
+layer_gcode =
+max_print_speed = 80
+max_volumetric_speed = 0
+retract_length = 2
+retract_length_toolchange = 10
+retract_lift = 1.5
+retract_lift_above = 0
+retract_lift_below = 0
+retract_restart_extra = 0
+retract_restart_extra_toolchange = 0
+retract_speed = 40
+start_filament_gcode = "; Filament gcode\n"
+start_gcode = G28 ; home all axes\nG1 Z5 F5000 ; lift nozzle\n
+toolchange_gcode =
+travel_speed = 130
+use_firmware_retraction = 0
+use_relative_e_distances = 0
+use_volumetric_e = 0
diff --git a/tests/data/test_3mf/Geräte/Büchse.3mf b/tests/data/test_3mf/Geräte/Büchse.3mf
new file mode 100644
index 000000000..3b5a8350d
--- /dev/null
+++ b/tests/data/test_3mf/Geräte/Büchse.3mf
Binary files differ
diff --git a/tests/example/CMakeLists.txt b/tests/example/CMakeLists.txt
index 95f5e3762..d62f0a96c 100644
--- a/tests/example/CMakeLists.txt
+++ b/tests/example/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)
-target_link_libraries(${_TEST_NAME}_tests test_common libslic3r ${Boost_LIBRARIES} ${TBB_LIBRARIES} ${Boost_LIBRARIES})
+target_link_libraries(${_TEST_NAME}_tests test_common libslic3r
+#${Boost_LIBRARIES} ${TBB_LIBRARIES} ${Boost_LIBRARIES}
+)
-catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ")
+# catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ")
+add_test(${_TEST_NAME}_tests ${_TEST_NAME}_tests "--durations yes") \ No newline at end of file
diff --git a/tests/fff_print/CMakeLists.txt b/tests/fff_print/CMakeLists.txt
new file mode 100644
index 000000000..e75bcc4c1
--- /dev/null
+++ b/tests/fff_print/CMakeLists.txt
@@ -0,0 +1,20 @@
+get_filename_component(_TEST_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
+add_executable(${_TEST_NAME}_tests
+ ${_TEST_NAME}_tests.cpp
+ test_data.cpp
+ test_data.hpp
+ test_fill.cpp
+ test_flow.cpp
+ test_gcodewriter.cpp
+ test_model.cpp
+ test_print.cpp
+ test_printgcode.cpp
+ test_printobject.cpp
+ test_skirt_brim.cpp
+ test_trianglemesh.cpp
+ )
+target_link_libraries(${_TEST_NAME}_tests test_common libslic3r)
+set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER "tests")
+
+# catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ")
+add_test(${_TEST_NAME}_tests ${_TEST_NAME}_tests "--durations yes")
diff --git a/tests/fff_print/fff_print_tests.cpp b/tests/fff_print/fff_print_tests.cpp
new file mode 100644
index 000000000..5e9b82f80
--- /dev/null
+++ b/tests/fff_print/fff_print_tests.cpp
@@ -0,0 +1,4 @@
+#define CATCH_CONFIG_MAIN
+#include <catch2/catch.hpp>
+
+#include "libslic3r/libslic3r.h"
diff --git a/tests/fff_print/test_data.cpp b/tests/fff_print/test_data.cpp
new file mode 100644
index 000000000..bf2732828
--- /dev/null
+++ b/tests/fff_print/test_data.cpp
@@ -0,0 +1,327 @@
+#include "test_data.hpp"
+
+#include "libslic3r/TriangleMesh.hpp"
+#include "libslic3r/GCodeReader.hpp"
+#include "libslic3r/Config.hpp"
+#include "libslic3r/Print.hpp"
+
+#include <cstdlib>
+#include <string>
+
+#include <boost/nowide/cstdio.hpp>
+#include <boost/filesystem.hpp>
+
+using namespace std;
+
+namespace Slic3r { namespace Test {
+
+// Mesh enumeration to name mapping
+const std::unordered_map<TestMesh, const char*, TestMeshHash> mesh_names {
+ std::pair<TestMesh, const char*>(TestMesh::A,"A"),
+ std::pair<TestMesh, const char*>(TestMesh::L,"L"),
+ std::pair<TestMesh, const char*>(TestMesh::V,"V"),
+ std::pair<TestMesh, const char*>(TestMesh::_40x10,"40x10"),
+ std::pair<TestMesh, const char*>(TestMesh::cube_20x20x20,"cube_20x20x20"),
+ std::pair<TestMesh, const char*>(TestMesh::sphere_50mm,"sphere_50mm"),
+ std::pair<TestMesh, const char*>(TestMesh::bridge,"bridge"),
+ std::pair<TestMesh, const char*>(TestMesh::bridge_with_hole,"bridge_with_hole"),
+ std::pair<TestMesh, const char*>(TestMesh::cube_with_concave_hole,"cube_with_concave_hole"),
+ std::pair<TestMesh, const char*>(TestMesh::cube_with_hole,"cube_with_hole"),
+ std::pair<TestMesh, const char*>(TestMesh::gt2_teeth,"gt2_teeth"),
+ std::pair<TestMesh, const char*>(TestMesh::ipadstand,"ipadstand"),
+ std::pair<TestMesh, const char*>(TestMesh::overhang,"overhang"),
+ std::pair<TestMesh, const char*>(TestMesh::pyramid,"pyramid"),
+ std::pair<TestMesh, const char*>(TestMesh::sloping_hole,"sloping_hole"),
+ std::pair<TestMesh, const char*>(TestMesh::slopy_cube,"slopy_cube"),
+ std::pair<TestMesh, const char*>(TestMesh::small_dorito,"small_dorito"),
+ std::pair<TestMesh, const char*>(TestMesh::step,"step"),
+ std::pair<TestMesh, const char*>(TestMesh::two_hollow_squares,"two_hollow_squares")
+};
+
+TriangleMesh mesh(TestMesh m)
+{
+ std::vector<Vec3crd> facets;
+ std::vector<Vec3d> vertices;
+ switch(m) {
+ case TestMesh::cube_with_hole:
+ vertices = { Vec3d(0,0,0), Vec3d(0,0,10), Vec3d(0,20,0), Vec3d(0,20,10), Vec3d(20,0,0), Vec3d(20,0,10), Vec3d(5,5,0), Vec3d(15,5,0), Vec3d(5,15,0), Vec3d(20,20,0), Vec3d(15,15,0), Vec3d(20,20,10), Vec3d(5,5,10), Vec3d(5,15,10), Vec3d(15,5,10), Vec3d(15,15,10) };
+ facets = std::vector<Vec3crd>({
+ Vec3crd(0,1,2), Vec3crd(2,1,3), Vec3crd(1,0,4), Vec3crd(5,1,4), Vec3crd(6,7,4), Vec3crd(8,2,9), Vec3crd(0,2,8), Vec3crd(10,8,9), Vec3crd(0,8,6), Vec3crd(0,6,4), Vec3crd(4,7,9), Vec3crd(7,10,9), Vec3crd(2,3,9), Vec3crd(9,3,11), Vec3crd(12,1,5), Vec3crd(13,3,12), Vec3crd(14,12,5), Vec3crd(3,1,12), Vec3crd(11,3,13), Vec3crd(11,15,5), Vec3crd(11,13,15), Vec3crd(15,14,5), Vec3crd(5,4,9), Vec3crd(11,5,9), Vec3crd(8,13,12), Vec3crd(6,8,12), Vec3crd(10,15,13), Vec3crd(8,10,13), Vec3crd(15,10,14), Vec3crd(14,10,7), Vec3crd(14,7,12), Vec3crd(12,7,6)
+ });
+ break;
+ case TestMesh::cube_with_concave_hole:
+ vertices = std::vector<Vec3d>({
+ Vec3d(-10,-10,-5), Vec3d(-10,-10,5), Vec3d(-10,10,-5), Vec3d(-10,10,5), Vec3d(10,-10,-5), Vec3d(10,-10,5), Vec3d(-5,-5,-5), Vec3d(5,-5,-5), Vec3d(5,5,-5), Vec3d(5,10,-5), Vec3d(-5,5,-5), Vec3d(3.06161699911402e-16,5,-5), Vec3d(5,0,-5), Vec3d(0,0,-5), Vec3d(10,5,-5), Vec3d(5,10,5), Vec3d(-5,-5,5), Vec3d(5,0,5), Vec3d(5,-5,5), Vec3d(-5,5,5), Vec3d(10,5,5), Vec3d(5,5,5), Vec3d(3.06161699911402e-16,5,5), Vec3d(0,0,5)
+ });
+ facets = std::vector<Vec3crd>({
+ Vec3crd(0,1,2), Vec3crd(2,1,3), Vec3crd(1,0,4), Vec3crd(5,1,4), Vec3crd(6,7,4), Vec3crd(8,2,9), Vec3crd(10,2,11), Vec3crd(11,12,13), Vec3crd(0,2,10), Vec3crd(0,10,6), Vec3crd(0,6,4), Vec3crd(11,2,8), Vec3crd(4,7,12), Vec3crd(4,12,8), Vec3crd(12,11,8), Vec3crd(14,4,8), Vec3crd(2,3,9), Vec3crd(9,3,15), Vec3crd(16,1,5), Vec3crd(17,18,5), Vec3crd(19,3,16), Vec3crd(20,21,5), Vec3crd(18,16,5), Vec3crd(3,1,16), Vec3crd(22,3,19), Vec3crd(21,3,22), Vec3crd(21,17,5), Vec3crd(21,22,17), Vec3crd(21,15,3), Vec3crd(23,17,22), Vec3crd(5,4,14), Vec3crd(20,5,14), Vec3crd(20,14,21), Vec3crd(21,14,8), Vec3crd(9,15,21), Vec3crd(8,9,21), Vec3crd(10,19,16), Vec3crd(6,10,16), Vec3crd(11,22,19), Vec3crd(10,11,19), Vec3crd(13,23,11), Vec3crd(11,23,22), Vec3crd(23,13,12), Vec3crd(17,23,12), Vec3crd(17,12,18), Vec3crd(18,12,7), Vec3crd(18,7,16), Vec3crd(16,7,6)
+ });
+ break;
+ case TestMesh::V:
+ vertices = std::vector<Vec3d>({
+ Vec3d(-14,0,20), Vec3d(-14,15,20), Vec3d(0,0,0), Vec3d(0,15,0), Vec3d(-4,0,20), Vec3d(-4,15,20), Vec3d(5,0,7.14286), Vec3d(10,0,0), Vec3d(24,0,20), Vec3d(14,0,20), Vec3d(10,15,0), Vec3d(5,15,7.14286), Vec3d(14,15,20), Vec3d(24,15,20)
+ });
+ facets = std::vector<Vec3crd>({
+ Vec3crd(0,1,2), Vec3crd(2,1,3), Vec3crd(1,0,4), Vec3crd(5,1,4), Vec3crd(4,0,2), Vec3crd(6,4,2), Vec3crd(7,6,2), Vec3crd(8,9,7), Vec3crd(9,6,7), Vec3crd(2,3,7), Vec3crd(7,3,10), Vec3crd(1,5,3), Vec3crd(3,5,11), Vec3crd(11,12,13), Vec3crd(11,13,3), Vec3crd(3,13,10), Vec3crd(5,4,6), Vec3crd(11,5,6), Vec3crd(6,9,11), Vec3crd(11,9,12), Vec3crd(12,9,8), Vec3crd(13,12,8), Vec3crd(8,7,10), Vec3crd(13,8,10)
+ });
+ break;
+ case TestMesh::L:
+ vertices = std::vector<Vec3d>({
+ Vec3d(0,10,0), Vec3d(0,10,10), Vec3d(0,20,0), Vec3d(0,20,10), Vec3d(10,10,0), Vec3d(10,10,10), Vec3d(20,20,0), Vec3d(20,0,0), Vec3d(10,0,0), Vec3d(20,20,10), Vec3d(10,0,10), Vec3d(20,0,10)
+ });
+ facets = std::vector<Vec3crd>({
+ Vec3crd(0,1,2), Vec3crd(2,1,3), Vec3crd(4,5,1), Vec3crd(0,4,1), Vec3crd(0,2,4), Vec3crd(4,2,6), Vec3crd(4,6,7), Vec3crd(4,7,8), Vec3crd(2,3,6), Vec3crd(6,3,9), Vec3crd(3,1,5), Vec3crd(9,3,5), Vec3crd(10,11,5), Vec3crd(11,9,5), Vec3crd(5,4,10), Vec3crd(10,4,8), Vec3crd(10,8,7), Vec3crd(11,10,7), Vec3crd(11,7,6), Vec3crd(9,11,6)
+ });
+ break;
+ case TestMesh::overhang:
+ vertices = std::vector<Vec3d>({
+ Vec3d(1364.68505859375,614.398010253906,20.002498626709), Vec3d(1389.68505859375,614.398010253906,20.002498626709), Vec3d(1377.18505859375,589.398986816406,20.002498626709), Vec3d(1389.68505859375,589.398986816406,20.002498626709), Vec3d(1389.68505859375,564.398986816406,20.0014991760254), Vec3d(1364.68505859375,589.398986816406,20.002498626709), Vec3d(1364.68505859375,564.398986816406,20.0014991760254), Vec3d(1360.93505859375,589.398986816406,17.0014991760254), Vec3d(1360.93505859375,585.64697265625,17.0014991760254), Vec3d(1357.18505859375,564.398986816406,17.0014991760254), Vec3d(1364.68505859375,589.398986816406,17.0014991760254), Vec3d(1364.68505859375,571.899963378906,17.0014991760254), Vec3d(1364.68505859375,564.398986816406,17.0014991760254), Vec3d(1348.43603515625,564.398986816406,17.0014991760254), Vec3d(1352.80908203125,589.398986816406,17.0014991760254), Vec3d(1357.18408203125,589.398986816406,17.0014991760254), Vec3d(1357.18310546875,614.398010253906,17.0014991760254), Vec3d(1364.68505859375,606.89599609375,17.0014991760254), Vec3d(1364.68505859375,614.398010253906,17.0014991760254), Vec3d(1352.18603515625,564.398986816406,20.0014991760254), Vec3d(1363.65405273438,589.398986816406,23.3004989624023), Vec3d(1359.46704101562,589.398986816406,23.3004989624023), Vec3d(1358.37109375,564.398986816406,23.3004989624023), Vec3d(1385.56103515625,564.398986816406,23.3004989624023), Vec3d(1373.06311035156,589.398986816406,23.3004989624023), Vec3d(1368.80810546875,564.398986816406,23.3004989624023), Vec3d(1387.623046875,589.398986816406,23.3004989624023), Vec3d(1387.623046875,585.276000976562,23.3004989624023), Vec3d(1389.68505859375,589.398986816406,23.3004989624023), Vec3d(1389.68505859375,572.64599609375,23.3004989624023), Vec3d(1389.68505859375,564.398986816406,23.3004989624023), Vec3d(1367.77709960938,589.398986816406,23.3004989624023), Vec3d(1366.7470703125,564.398986816406,23.3004989624023), Vec3d(1354.31201171875,589.398986816406,23.3004989624023), Vec3d(1352.18603515625,564.398986816406,23.3004989624023), Vec3d(1389.68505859375,614.398010253906,23.3004989624023), Vec3d(1377.31701660156,614.398010253906,23.3004989624023), Vec3d(1381.43908691406,589.398986816406,23.3004989624023), Vec3d(1368.80700683594,614.398010253906,23.3004989624023), Vec3d(1368.80810546875,589.398986816406,23.3004989624023), Vec3d(1356.43908691406,614.398010253906,23.3004989624023), Vec3d(1357.40502929688,589.398986816406,23.3004989624023), Vec3d(1360.56201171875,614.398010253906,23.3004989624023), Vec3d(1348.705078125,614.398010253906,23.3004989624023), Vec3d(1350.44506835938,589.398986816406,23.3004989624023), Vec3d(1389.68505859375,606.153015136719,23.3004989624023), Vec3d(1347.35205078125,589.398986816406,23.3004989624023), Vec3d(1346.56005859375,589.398986816406,23.3004989624023), Vec3d(1346.56005859375,594.159912109375,17.0014991760254), Vec3d(1346.56005859375,589.398986816406,17.0014991760254), Vec3d(1346.56005859375,605.250427246094,23.3004989624023), Vec3d(1346.56005859375,614.398010253906,23.3004989624023), Vec3d(1346.56005859375,614.398010253906,20.8258285522461), Vec3d(1346.56005859375,614.398010253906,17.0014991760254), Vec3d(1346.56005859375,564.398986816406,19.10133934021), Vec3d(1346.56005859375,567.548583984375,23.3004989624023), Vec3d(1346.56005859375,564.398986816406,17.0020332336426), Vec3d(1346.56005859375,564.398986816406,23.0018501281738), Vec3d(1346.56005859375,564.398986816406,23.3004989624023), Vec3d(1346.56005859375,575.118957519531,17.0014991760254), Vec3d(1346.56005859375,574.754028320312,23.3004989624023)
+ });
+ facets = std::vector<Vec3crd>({
+ Vec3crd(0,1,2), Vec3crd(2,3,4), Vec3crd(2,5,0), Vec3crd(4,6,2), Vec3crd(2,6,5), Vec3crd(2,1,3), Vec3crd(7,8,9), Vec3crd(10,9,8), Vec3crd(11,9,10), Vec3crd(12,9,11), Vec3crd(9,13,14), Vec3crd(7,15,16), Vec3crd(10,17,0), Vec3crd(10,0,5), Vec3crd(12,11,6), Vec3crd(18,16,0), Vec3crd(6,19,13), Vec3crd(6,13,9), Vec3crd(9,12,6), Vec3crd(17,18,0), Vec3crd(11,10,5), Vec3crd(11,5,6), Vec3crd(14,16,15), Vec3crd(17,7,18), Vec3crd(16,18,7), Vec3crd(14,15,9), Vec3crd(7,9,15), Vec3crd(7,17,8), Vec3crd(10,8,17), Vec3crd(20,21,22), Vec3crd(23,24,25), Vec3crd(26,23,27), Vec3crd(28,27,23), Vec3crd(29,28,23), Vec3crd(30,29,23), Vec3crd(25,31,32), Vec3crd(22,33,34), Vec3crd(35,36,37), Vec3crd(24,38,39), Vec3crd(21,40,41), Vec3crd(38,42,20), Vec3crd(33,43,44), Vec3crd(6,4,23), Vec3crd(6,23,25), Vec3crd(36,35,1), Vec3crd(1,0,38), Vec3crd(1,38,36), Vec3crd(29,30,4), Vec3crd(25,32,6), Vec3crd(40,42,0), Vec3crd(35,45,1), Vec3crd(4,3,28), Vec3crd(4,28,29), Vec3crd(3,1,45), Vec3crd(3,45,28), Vec3crd(22,34,19), Vec3crd(19,6,32), Vec3crd(19,32,22), Vec3crd(42,38,0), Vec3crd(30,23,4), Vec3crd(0,16,43), Vec3crd(0,43,40), Vec3crd(24,37,36), Vec3crd(38,24,36), Vec3crd(24,23,37), Vec3crd(37,23,26), Vec3crd(22,32,20), Vec3crd(20,32,31), Vec3crd(33,41,40), Vec3crd(43,33,40), Vec3crd(45,35,26), Vec3crd(37,26,35), Vec3crd(33,44,34), Vec3crd(44,43,46), Vec3crd(20,42,21), Vec3crd(40,21,42), Vec3crd(31,39,38), Vec3crd(20,31,38), Vec3crd(33,22,41), Vec3crd(21,41,22), Vec3crd(31,25,39), Vec3crd(24,39,25), Vec3crd(26,27,45), Vec3crd(28,45,27), Vec3crd(47,48,49), Vec3crd(47,50,48), Vec3crd(51,48,50), Vec3crd(52,48,51), Vec3crd(53,48,52), Vec3crd(54,55,56), Vec3crd(57,55,54), Vec3crd(58,55,57), Vec3crd(49,59,47), Vec3crd(60,56,55), Vec3crd(59,56,60), Vec3crd(60,47,59), Vec3crd(48,53,16), Vec3crd(56,13,19), Vec3crd(54,56,19), Vec3crd(56,59,13), Vec3crd(59,49,14), Vec3crd(59,14,13), Vec3crd(49,48,16), Vec3crd(49,16,14), Vec3crd(44,46,60), Vec3crd(44,60,55), Vec3crd(51,50,43), Vec3crd(19,34,58), Vec3crd(19,58,57), Vec3crd(53,52,16), Vec3crd(43,16,52), Vec3crd(43,52,51), Vec3crd(57,54,19), Vec3crd(47,60,46), Vec3crd(55,58,34), Vec3crd(55,34,44), Vec3crd(50,47,46), Vec3crd(50,46,43)
+ });
+ break;
+ case TestMesh::_40x10:
+ vertices = std::vector<Vec3d>({
+ Vec3d(12.8680295944214,29.5799007415771,12), Vec3d(11.7364797592163,29.8480796813965,12), Vec3d(11.1571502685547,29.5300102233887,12), Vec3d(10.5814504623413,29.9830799102783,12), Vec3d(10,29.6000003814697,12), Vec3d(9.41855144500732,29.9830799102783,12), Vec3d(8.84284687042236,29.5300102233887,12), Vec3d(8.26351833343506,29.8480796813965,12), Vec3d(7.70256900787354,29.3210391998291,12), Vec3d(7.13196802139282,29.5799007415771,12), Vec3d(6.59579277038574,28.9761600494385,12), Vec3d(6.03920221328735,29.1821594238281,12), Vec3d(5.53865718841553,28.5003795623779,12), Vec3d(5,28.6602592468262,12), Vec3d(4.54657793045044,27.9006500244141,12), Vec3d(4.02841377258301,28.0212306976318,12), Vec3d(3.63402199745178,27.1856994628906,12), Vec3d(3.13758301734924,27.2737407684326,12), Vec3d(2.81429696083069,26.3659801483154,12), Vec3d(2.33955597877502,26.4278793334961,12), Vec3d(2.0993549823761,25.4534206390381,12), Vec3d(1.64512205123901,25.4950904846191,12), Vec3d(1.49962198734283,24.4613399505615,12), Vec3d(1.0636739730835,24.4879894256592,12), Vec3d(1.02384400367737,23.4042091369629,12), Vec3d(0.603073298931122,23.4202003479004,12), Vec3d(0.678958415985107,22.2974300384521,12), Vec3d(0.269550800323486,22.3061599731445,12), Vec3d(0.469994693994522,21.1571502685547,12), Vec3d(0.067615881562233,21.1609306335449,12), Vec3d(0.399999290704727,20,12), Vec3d(0,20,12), Vec3d(0.399999290704727,5,12), Vec3d(0,5,12), Vec3d(0.456633001565933,4.2804012298584,12), Vec3d(0.0615576282143593,4.21782684326172,12), Vec3d(0.625140011310577,3.5785219669342,12), Vec3d(0.244717106223106,3.45491504669189,12), Vec3d(0.901369392871857,2.91164398193359,12), Vec3d(0.544967114925385,2.73004698753357,12), Vec3d(1.27852201461792,2.29618692398071,12), Vec3d(0.954914808273315,2.06107401847839,12), Vec3d(1.74730801582336,1.74730801582336,12), Vec3d(1.46446597576141,1.46446597576141,12), Vec3d(2.29618692398071,1.27852201461792,12), Vec3d(2.06107401847839,0.954914808273315,12), Vec3d(2.91164398193359,0.901369392871857,12), Vec3d(2.73004698753357,0.544967114925385,12), Vec3d(3.5785219669342,0.625140011310577,12), Vec3d(3.45491504669189,0.244717106223106,12), Vec3d(4.2804012298584,0.456633001565933,12), Vec3d(4.21782684326172,0.0615576282143593,12), Vec3d(5,0.399999290704727,12), Vec3d(5,0,12), Vec3d(19.6000003814697,0.399999290704727,12), Vec3d(20,0,12), Vec3d(19.6000003814697,20,12), Vec3d(20,20,12), Vec3d(19.5300102233887,21.1571502685547,12), Vec3d(19.9323806762695,21.1609306335449,12), Vec3d(19.3210391998291,22.2974300384521,12), Vec3d(19.7304496765137,22.3061599731445,12), Vec3d(18.9761600494385,23.4042091369629,12), Vec3d(19.3969306945801,23.4202003479004,12), Vec3d(18.5003795623779,24.4613399505615,12), Vec3d(18.9363307952881,24.4879894256592,12), Vec3d(17.9006500244141,25.4534206390381,12), Vec3d(18.3548793792725,25.4950904846191,12), Vec3d(17.1856994628906,26.3659801483154,12), Vec3d(17.6604404449463,26.4278793334961,12), Vec3d(16.3659801483154,27.1856994628906,12), Vec3d(16.862419128418,27.2737407684326,12), Vec3d(15.4534196853638,27.9006500244141,12), Vec3d(15.9715900421143,28.0212306976318,12), Vec3d(14.4613399505615,28.5003795623779,12), Vec3d(15,28.6602592468262,12), Vec3d(13.4042100906372,28.9761600494385,12), Vec3d(13.9608001708984,29.1821594238281,12), Vec3d(12.2974300384521,29.3210391998291,12), Vec3d(7.13196802139282,29.5799007415771,0), Vec3d(8.26351833343506,29.8480796813965,0), Vec3d(8.84284687042236,29.5300102233887,0), Vec3d(9.41855144500732,29.9830799102783,0), Vec3d(10,29.6000003814697,0), Vec3d(10.5814504623413,29.9830799102783,0), Vec3d(11.1571502685547,29.5300102233887,0), Vec3d(11.7364797592163,29.8480796813965,0), Vec3d(12.2974300384521,29.3210391998291,0), Vec3d(12.8680295944214,29.5799007415771,0), Vec3d(13.4042100906372,28.9761600494385,0), Vec3d(13.9608001708984,29.1821594238281,0), Vec3d(14.4613399505615,28.5003795623779,0), Vec3d(15,28.6602592468262,0), Vec3d(15.4534196853638,27.9006500244141,0), Vec3d(15.9715900421143,28.0212306976318,0), Vec3d(16.3659801483154,27.1856994628906,0), Vec3d(16.862419128418,27.2737407684326,0), Vec3d(17.1856994628906,26.3659801483154,0), Vec3d(17.6604404449463,26.4278793334961,0), Vec3d(17.9006500244141,25.4534206390381,0), Vec3d(18.3548793792725,25.4950904846191,0), Vec3d(18.5003795623779,24.4613399505615,0), Vec3d(18.9363307952881,24.4879894256592,0), Vec3d(18.9761600494385,23.4042091369629,0), Vec3d(19.3969306945801,23.4202003479004,0), Vec3d(19.3210391998291,22.2974300384521,0), Vec3d(19.7304496765137,22.3061599731445,0), Vec3d(19.5300102233887,21.1571502685547,0), Vec3d(19.9323806762695,21.1609306335449,0), Vec3d(19.6000003814697,20,0), Vec3d(20,20,0), Vec3d(19.6000003814697,0.399999290704727,0), Vec3d(20,0,0), Vec3d(5,0.399999290704727,0), Vec3d(5,0,0), Vec3d(4.2804012298584,0.456633001565933,0), Vec3d(4.21782684326172,0.0615576282143593,0), Vec3d(3.5785219669342,0.625140011310577,0), Vec3d(3.45491504669189,0.244717106223106,0), Vec3d(2.91164398193359,0.901369392871857,0), Vec3d(2.73004698753357,0.544967114925385,0), Vec3d(2.29618692398071,1.27852201461792,0), Vec3d(2.06107401847839,0.954914808273315,0), Vec3d(1.74730801582336,1.74730801582336,0), Vec3d(1.46446597576141,1.46446597576141,0), Vec3d(1.27852201461792,2.29618692398071,0), Vec3d(0.954914808273315,2.06107401847839,0), Vec3d(0.901369392871857,2.91164398193359,0), Vec3d(0.544967114925385,2.73004698753357,0), Vec3d(0.625140011310577,3.5785219669342,0), Vec3d(0.244717106223106,3.45491504669189,0), Vec3d(0.456633001565933,4.2804012298584,0), Vec3d(0.0615576282143593,4.21782684326172,0), Vec3d(0.399999290704727,5,0), Vec3d(0,5,0), Vec3d(0.399999290704727,20,0), Vec3d(0,20,0), Vec3d(0.469994693994522,21.1571502685547,0), Vec3d(0.067615881562233,21.1609306335449,0), Vec3d(0.678958415985107,22.2974300384521,0), Vec3d(0.269550800323486,22.3061599731445,0), Vec3d(1.02384400367737,23.4042091369629,0), Vec3d(0.603073298931122,23.4202003479004,0), Vec3d(1.49962198734283,24.4613399505615,0), Vec3d(1.0636739730835,24.4879894256592,0), Vec3d(2.0993549823761,25.4534206390381,0), Vec3d(1.64512205123901,25.4950904846191,0), Vec3d(2.81429696083069,26.3659801483154,0), Vec3d(2.33955597877502,26.4278793334961,0), Vec3d(3.63402199745178,27.1856994628906,0), Vec3d(3.13758301734924,27.2737407684326,0), Vec3d(4.54657793045044,27.9006500244141,0), Vec3d(4.02841377258301,28.0212306976318,0), Vec3d(5.53865718841553,28.5003795623779,0), Vec3d(5,28.6602592468262,0), Vec3d(6.59579277038574,28.9761600494385,0), Vec3d(6.03920221328735,29.1821594238281,0), Vec3d(7.70256900787354,29.3210391998291,0)
+ });
+ facets = std::vector<Vec3crd>({
+ Vec3crd(0,1,2), Vec3crd(2,1,3), Vec3crd(2,3,4), Vec3crd(4,3,5), Vec3crd(4,5,6), Vec3crd(6,5,7), Vec3crd(6,7,8), Vec3crd(8,7,9), Vec3crd(8,9,10), Vec3crd(10,9,11), Vec3crd(10,11,12), Vec3crd(12,11,13), Vec3crd(12,13,14), Vec3crd(14,13,15), Vec3crd(14,15,16), Vec3crd(16,15,17), Vec3crd(16,17,18), Vec3crd(18,17,19), Vec3crd(18,19,20), Vec3crd(20,19,21), Vec3crd(20,21,22), Vec3crd(22,21,23), Vec3crd(22,23,24), Vec3crd(24,23,25), Vec3crd(24,25,26), Vec3crd(26,25,27), Vec3crd(26,27,28), Vec3crd(28,27,29), Vec3crd(28,29,30), Vec3crd(30,29,31), Vec3crd(30,31,32), Vec3crd(32,31,33), Vec3crd(32,33,34), Vec3crd(34,33,35), Vec3crd(34,35,36), Vec3crd(36,35,37), Vec3crd(36,37,38), Vec3crd(38,37,39), Vec3crd(38,39,40), Vec3crd(40,39,41), Vec3crd(40,41,42), Vec3crd(42,41,43), Vec3crd(42,43,44), Vec3crd(44,43,45), Vec3crd(44,45,46), Vec3crd(46,45,47), Vec3crd(46,47,48), Vec3crd(48,47,49), Vec3crd(48,49,50), Vec3crd(50,49,51), Vec3crd(50,51,52), Vec3crd(52,51,53), Vec3crd(52,53,54), Vec3crd(54,53,55), Vec3crd(54,55,56), Vec3crd(56,55,57), Vec3crd(56,57,58), Vec3crd(58,57,59), Vec3crd(58,59,60), Vec3crd(60,59,61), Vec3crd(60,61,62), Vec3crd(62,61,63), Vec3crd(62,63,64), Vec3crd(64,63,65), Vec3crd(64,65,66), Vec3crd(66,65,67), Vec3crd(66,67,68), Vec3crd(68,67,69), Vec3crd(68,69,70), Vec3crd(70,69,71), Vec3crd(70,71,72), Vec3crd(72,71,73), Vec3crd(72,73,74), Vec3crd(74,73,75), Vec3crd(74,75,76), Vec3crd(76,75,77), Vec3crd(76,77,78), Vec3crd(78,77,0), Vec3crd(78,0,2), Vec3crd(79,80,81), Vec3crd(81,80,82), Vec3crd(81,82,83), Vec3crd(83,82,84), Vec3crd(83,84,85), Vec3crd(85,84,86), Vec3crd(85,86,87), Vec3crd(87,86,88), Vec3crd(87,88,89), Vec3crd(89,88,90), Vec3crd(89,90,91), Vec3crd(91,90,92), Vec3crd(91,92,93), Vec3crd(93,92,94), Vec3crd(93,94,95), Vec3crd(95,94,96), Vec3crd(95,96,97), Vec3crd(97,96,98), Vec3crd(97,98,99), Vec3crd(99,98,100), Vec3crd(99,100,101), Vec3crd(101,100,102), Vec3crd(101,102,103), Vec3crd(103,102,104), Vec3crd(103,104,105), Vec3crd(105,104,106), Vec3crd(105,106,107), Vec3crd(107,106,108), Vec3crd(107,108,109), Vec3crd(109,108,110), Vec3crd(109,110,111), Vec3crd(111,110,112), Vec3crd(111,112,113), Vec3crd(113,112,114), Vec3crd(113,114,115), Vec3crd(115,114,116), Vec3crd(115,116,117), Vec3crd(117,116,118), Vec3crd(117,118,119), Vec3crd(119,118,120), Vec3crd(119,120,121), Vec3crd(121,120,122), Vec3crd(121,122,123), Vec3crd(123,122,124), Vec3crd(123,124,125), Vec3crd(125,124,126), Vec3crd(125,126,127), Vec3crd(127,126,128), Vec3crd(127,128,129), Vec3crd(129,128,130), Vec3crd(129,130,131), Vec3crd(131,130,132), Vec3crd(131,132,133), Vec3crd(133,132,134), Vec3crd(133,134,135), Vec3crd(135,134,136), Vec3crd(135,136,137), Vec3crd(137,136,138), Vec3crd(137,138,139), Vec3crd(139,138,140), Vec3crd(139,140,141), Vec3crd(141,140,142), Vec3crd(141,142,143), Vec3crd(143,142,144), Vec3crd(143,144,145), Vec3crd(145,144,146), Vec3crd(145,146,147), Vec3crd(147,146,148), Vec3crd(147,148,149), Vec3crd(149,148,150), Vec3crd(149,150,151), Vec3crd(151,150,152), Vec3crd(151,152,153), Vec3crd(153,152,154), Vec3crd(153,154,155), Vec3crd(155,154,156), Vec3crd(155,156,157), Vec3crd(157,156,79), Vec3crd(157,79,81), Vec3crd(57,110,108), Vec3crd(57,108,59), Vec3crd(59,108,106), Vec3crd(59,106,61), Vec3crd(61,106,104), Vec3crd(61,104,63), Vec3crd(63,104,102), Vec3crd(63,102,65), Vec3crd(65,102,100), Vec3crd(65,100,67), Vec3crd(67,100,98), Vec3crd(67,98,69), Vec3crd(69,98,96), Vec3crd(69,96,71), Vec3crd(71,96,94), Vec3crd(71,94,73), Vec3crd(73,94,92), Vec3crd(73,92,75), Vec3crd(75,92,90), Vec3crd(75,90,77), Vec3crd(77,90,88), Vec3crd(77,88,0), Vec3crd(0,88,86), Vec3crd(0,86,1), Vec3crd(1,86,84), Vec3crd(1,84,3), Vec3crd(3,84,82), Vec3crd(3,82,5), Vec3crd(5,82,80), Vec3crd(5,80,7), Vec3crd(7,80,79), Vec3crd(7,79,9), Vec3crd(9,79,156), Vec3crd(9,156,11), Vec3crd(11,156,154), Vec3crd(11,154,13), Vec3crd(13,154,152), Vec3crd(13,152,15), Vec3crd(15,152,150), Vec3crd(15,150,17), Vec3crd(17,150,148), Vec3crd(17,148,19), Vec3crd(19,148,146), Vec3crd(19,146,21), Vec3crd(21,146,144), Vec3crd(21,144,23), Vec3crd(23,144,142), Vec3crd(23,142,25), Vec3crd(25,142,140), Vec3crd(25,140,27), Vec3crd(27,140,138), Vec3crd(27,138,29), Vec3crd(29,138,136), Vec3crd(29,136,31), Vec3crd(33,31,134), Vec3crd(134,31,136), Vec3crd(33,134,132), Vec3crd(33,132,35), Vec3crd(35,132,130), Vec3crd(35,130,37), Vec3crd(37,130,128), Vec3crd(37,128,39), Vec3crd(39,128,126), Vec3crd(39,126,41), Vec3crd(41,126,124), Vec3crd(41,124,43), Vec3crd(43,124,122), Vec3crd(43,122,45), Vec3crd(45,122,120), Vec3crd(45,120,47), Vec3crd(47,120,118), Vec3crd(47,118,49), Vec3crd(49,118,116), Vec3crd(49,116,51), Vec3crd(51,116,114), Vec3crd(51,114,53), Vec3crd(55,53,112), Vec3crd(112,53,114), Vec3crd(57,55,110), Vec3crd(110,55,112), Vec3crd(30,135,137), Vec3crd(30,137,28), Vec3crd(28,137,139), Vec3crd(28,139,26), Vec3crd(26,139,141), Vec3crd(26,141,24), Vec3crd(24,141,143), Vec3crd(24,143,22), Vec3crd(22,143,145), Vec3crd(22,145,20), Vec3crd(20,145,147), Vec3crd(20,147,18), Vec3crd(18,147,149), Vec3crd(18,149,16), Vec3crd(16,149,151), Vec3crd(16,151,14), Vec3crd(14,151,153), Vec3crd(14,153,12), Vec3crd(12,153,155), Vec3crd(12,155,10), Vec3crd(10,155,157), Vec3crd(10,157,8), Vec3crd(8,157,81), Vec3crd(8,81,6), Vec3crd(6,81,83), Vec3crd(6,83,4), Vec3crd(4,83,85), Vec3crd(4,85,2), Vec3crd(2,85,87), Vec3crd(2,87,78), Vec3crd(78,87,89), Vec3crd(78,89,76), Vec3crd(76,89,91), Vec3crd(76,91,74), Vec3crd(74,91,93), Vec3crd(74,93,72), Vec3crd(72,93,95), Vec3crd(72,95,70), Vec3crd(70,95,97), Vec3crd(70,97,68), Vec3crd(68,97,99), Vec3crd(68,99,66), Vec3crd(66,99,101), Vec3crd(66,101,64), Vec3crd(64,101,103), Vec3crd(64,103,62), Vec3crd(62,103,105), Vec3crd(62,105,60), Vec3crd(60,105,107), Vec3crd(60,107,58), Vec3crd(58,107,109), Vec3crd(58,109,56), Vec3crd(30,32,135), Vec3crd(135,32,133), Vec3crd(52,113,115), Vec3crd(52,115,50), Vec3crd(50,115,117), Vec3crd(50,117,48), Vec3crd(48,117,119), Vec3crd(48,119,46), Vec3crd(46,119,121), Vec3crd(46,121,44), Vec3crd(44,121,123), Vec3crd(44,123,42), Vec3crd(42,123,125), Vec3crd(42,125,40), Vec3crd(40,125,127), Vec3crd(40,127,38), Vec3crd(38,127,129), Vec3crd(38,129,36), Vec3crd(36,129,131), Vec3crd(36,131,34), Vec3crd(34,131,133), Vec3crd(34,133,32), Vec3crd(52,54,113), Vec3crd(113,54,111), Vec3crd(54,56,111), Vec3crd(111,56,109)
+ });
+ break;
+ case TestMesh::sloping_hole:
+ vertices = std::vector<Vec3d>({
+ Vec3d(-20,-20,-5), Vec3d(-20,-20,5), Vec3d(-20,20,-5), Vec3d(-20,20,5), Vec3d(20,-20,-5), Vec3d(20,-20,5), Vec3d(4.46294021606445,7.43144989013672,-5), Vec3d(20,20,-5), Vec3d(-19.1420993804932,0,-5), Vec3d(-18.8330993652344,-2.07911992073059,-5), Vec3d(-17.9195003509521,-4.06736993789673,-5), Vec3d(-16.4412002563477,-5.87785005569458,-5), Vec3d(-14.4629001617432,-7.43144989013672,-5), Vec3d(-12.0711002349854,-8.66024971008301,-5), Vec3d(-9.37016010284424,-9.51056003570557,-5), Vec3d(-3.5217399597168,-9.94521999359131,-5), Vec3d(-6.4782600402832,-9.94521999359131,-5), Vec3d(-0.629840016365051,-9.51056003570557,-5), Vec3d(2.07106995582581,-8.66024971008301,-5), Vec3d(6.44122982025146,-5.87785005569458,-5), Vec3d(4.46294021606445,-7.43144989013672,-5), Vec3d(-12.0711002349854,8.66024971008301,-5), Vec3d(-9.37016010284424,9.51056003570557,-5), Vec3d(7.91947984695435,-4.06736993789673,-5), Vec3d(8.83310031890869,-2.07911992073059,-5), Vec3d(-6.4782600402832,9.94521999359131,-5), Vec3d(-0.629840016365051,9.51056003570557,-5), Vec3d(2.07106995582581,8.66024971008301,-5), Vec3d(9.14214038848877,0,-5), Vec3d(8.83310031890869,2.07911992073059,-5), Vec3d(-3.5217399597168,9.94521999359131,-5), Vec3d(7.91947984695435,4.06736993789673,-5), Vec3d(6.44122982025146,5.87785005569458,-5), Vec3d(-14.4629001617432,7.43144989013672,-5), Vec3d(-16.4412002563477,5.87785005569458,-5), Vec3d(-17.9195003509521,4.06736993789673,-5), Vec3d(-18.8330993652344,2.07911992073059,-5), Vec3d(20,20,5), Vec3d(3.5217399597168,-9.94521999359131,5), Vec3d(-8.83310031890869,-2.07911992073059,5), Vec3d(-9.14214038848877,0,5), Vec3d(-8.83310031890869,2.07911992073059,5), Vec3d(6.4782600402832,-9.94521999359131,5), Vec3d(-7.91947984695435,4.06736993789673,5), Vec3d(-6.44122982025146,5.87785005569458,5), Vec3d(-4.46294021606445,7.43144989013672,5), Vec3d(-2.07106995582581,8.66024971008301,5), Vec3d(0.629840016365051,9.51056003570557,5), Vec3d(12.0711002349854,-8.66024971008301,5), Vec3d(9.37016010284424,-9.51056003570557,5), Vec3d(3.5217399597168,9.94521999359131,5), Vec3d(6.4782600402832,9.94521999359131,5), Vec3d(9.37016010284424,9.51056003570557,5), Vec3d(12.0711002349854,8.66024971008301,5), Vec3d(14.4629001617432,7.43144989013672,5), Vec3d(16.4412002563477,-5.87785005569458,5), Vec3d(14.4629001617432,-7.43144989013672,5), Vec3d(16.4412002563477,5.87785005569458,5), Vec3d(17.9195003509521,4.06736993789673,5), Vec3d(18.8330993652344,-2.07911992073059,5), Vec3d(17.9195003509521,-4.06736993789673,5), Vec3d(18.8330993652344,2.07911992073059,5), Vec3d(19.1420993804932,0,5), Vec3d(0.629840016365051,-9.51056003570557,5), Vec3d(-2.07106995582581,-8.66024971008301,5), Vec3d(-4.46294021606445,-7.43144989013672,5), Vec3d(-6.44122982025146,-5.87785005569458,5), Vec3d(-7.91947984695435,-4.06736993789673,5)
+ });
+ facets = std::vector<Vec3crd>({
+ Vec3crd(0,1,2), Vec3crd(2,1,3), Vec3crd(1,0,4), Vec3crd(5,1,4), Vec3crd(6,2,7), Vec3crd(0,2,8), Vec3crd(0,8,9), Vec3crd(0,9,10), Vec3crd(0,10,11), Vec3crd(0,11,12), Vec3crd(0,12,13), Vec3crd(0,13,4), Vec3crd(13,14,4), Vec3crd(15,4,16), Vec3crd(17,4,15), Vec3crd(18,4,17), Vec3crd(19,4,20), Vec3crd(18,20,4), Vec3crd(21,2,22), Vec3crd(4,19,23), Vec3crd(4,23,7), Vec3crd(23,24,7), Vec3crd(22,2,25), Vec3crd(26,2,27), Vec3crd(28,29,7), Vec3crd(25,2,30), Vec3crd(29,31,7), Vec3crd(30,2,26), Vec3crd(31,32,7), Vec3crd(27,2,6), Vec3crd(32,6,7), Vec3crd(28,7,24), Vec3crd(33,2,21), Vec3crd(34,2,33), Vec3crd(35,2,34), Vec3crd(36,2,35), Vec3crd(8,2,36), Vec3crd(16,4,14), Vec3crd(2,3,7), Vec3crd(7,3,37), Vec3crd(38,1,5), Vec3crd(3,1,39), Vec3crd(3,39,40), Vec3crd(3,40,41), Vec3crd(42,38,5), Vec3crd(3,41,43), Vec3crd(3,43,44), Vec3crd(37,3,45), Vec3crd(37,45,46), Vec3crd(37,46,47), Vec3crd(48,49,5), Vec3crd(37,47,50), Vec3crd(49,42,5), Vec3crd(37,50,51), Vec3crd(37,51,52), Vec3crd(37,52,53), Vec3crd(37,53,54), Vec3crd(55,56,5), Vec3crd(37,54,57), Vec3crd(37,57,58), Vec3crd(59,60,5), Vec3crd(37,58,61), Vec3crd(37,62,5), Vec3crd(37,61,62), Vec3crd(62,59,5), Vec3crd(60,55,5), Vec3crd(63,1,38), Vec3crd(64,1,63), Vec3crd(65,1,64), Vec3crd(66,1,65), Vec3crd(67,1,66), Vec3crd(39,1,67), Vec3crd(44,45,3), Vec3crd(56,48,5), Vec3crd(5,4,7), Vec3crd(37,5,7), Vec3crd(41,40,36), Vec3crd(36,40,8), Vec3crd(39,9,40), Vec3crd(40,9,8), Vec3crd(43,41,35), Vec3crd(35,41,36), Vec3crd(44,43,34), Vec3crd(34,43,35), Vec3crd(33,45,44), Vec3crd(34,33,44), Vec3crd(21,46,45), Vec3crd(33,21,45), Vec3crd(22,47,46), Vec3crd(21,22,46), Vec3crd(25,50,47), Vec3crd(22,25,47), Vec3crd(30,51,50), Vec3crd(25,30,50), Vec3crd(26,52,51), Vec3crd(30,26,51), Vec3crd(27,53,52), Vec3crd(26,27,52), Vec3crd(6,54,53), Vec3crd(27,6,53), Vec3crd(32,57,54), Vec3crd(6,32,54), Vec3crd(31,58,57), Vec3crd(32,31,57), Vec3crd(29,61,58), Vec3crd(31,29,58), Vec3crd(28,62,61), Vec3crd(29,28,61), Vec3crd(59,62,28), Vec3crd(24,59,28), Vec3crd(60,59,24), Vec3crd(23,60,24), Vec3crd(55,60,23), Vec3crd(19,55,23), Vec3crd(55,19,56), Vec3crd(56,19,20), Vec3crd(56,20,48), Vec3crd(48,20,18), Vec3crd(48,18,49), Vec3crd(49,18,17), Vec3crd(49,17,42), Vec3crd(42,17,15), Vec3crd(42,15,38), Vec3crd(38,15,16), Vec3crd(38,16,63), Vec3crd(63,16,14), Vec3crd(63,14,64), Vec3crd(64,14,13), Vec3crd(64,13,65), Vec3crd(65,13,12), Vec3crd(65,12,66), Vec3crd(66,12,11), Vec3crd(66,11,67), Vec3crd(67,11,10), Vec3crd(67,10,39), Vec3crd(39,10,9)
+ });
+ break;
+ case TestMesh::ipadstand:
+ vertices = std::vector<Vec3d>({
+ Vec3d(17.4344673156738,-2.69879599481136e-16,9.5), Vec3d(14.2814798355103,10,9.5), Vec3d(0,0,9.5), Vec3d(31.7159481048584,10,9.5), Vec3d(62.2344741821289,2.06667568800577e-16,20), Vec3d(31.7159481048584,10,20), Vec3d(17.4344673156738,-2.69879599481136e-16,20), Vec3d(62.2344741821289,10,20), Vec3d(98.2079696655273,10,0), Vec3d(98.2079696655273,8.56525380796383e-16,10), Vec3d(98.2079696655273,0,0), Vec3d(98.2079696655273,10,20), Vec3d(98.2079696655273,0,20), Vec3d(81.6609649658203,-4.39753856997999e-16,10), Vec3d(90.0549850463867,10,10), Vec3d(78.5079803466797,10,10), Vec3d(93.2079696655273,8.56525380796383e-16,10), Vec3d(14.2814798355103,10,20), Vec3d(0,0,20), Vec3d(87.4344711303711,2.81343962782118e-15,20), Vec3d(84.2814788818359,10,20), Vec3d(0,10,20), Vec3d(0,0,0), Vec3d(0,10,0), Vec3d(62.2344741821289,2.06667568800577e-16,30), Vec3d(66.9609756469727,10,30), Vec3d(62.2344741821289,10,30), Vec3d(70.1139602661133,8.5525763717214e-16,30), Vec3d(67.7053375244141,10,28.7107200622559), Vec3d(71.6787109375,1.24046736339707e-15,27.2897701263428)
+ });
+ facets = std::vector<Vec3crd>({
+ Vec3crd(0,1,2), Vec3crd(1,0,3), Vec3crd(4,5,6), Vec3crd(5,4,7), Vec3crd(8,9,10), Vec3crd(9,11,12), Vec3crd(11,9,8), Vec3crd(13,14,15), Vec3crd(14,13,16), Vec3crd(17,2,1), Vec3crd(2,17,18), Vec3crd(19,11,20), Vec3crd(11,19,12), Vec3crd(17,21,18), Vec3crd(21,2,18), Vec3crd(2,21,22), Vec3crd(22,21,23), Vec3crd(8,22,23), Vec3crd(22,8,10), Vec3crd(24,25,26), Vec3crd(25,24,27), Vec3crd(23,1,8), Vec3crd(1,23,21), Vec3crd(1,21,17), Vec3crd(5,15,3), Vec3crd(15,5,7), Vec3crd(15,7,28), Vec3crd(28,7,26), Vec3crd(28,26,25), Vec3crd(8,14,11), Vec3crd(14,8,3), Vec3crd(3,8,1), Vec3crd(14,3,15), Vec3crd(11,14,20), Vec3crd(26,4,24), Vec3crd(4,26,7), Vec3crd(12,16,9), Vec3crd(16,12,19), Vec3crd(29,4,13), Vec3crd(4,29,24), Vec3crd(24,29,27), Vec3crd(9,22,10), Vec3crd(22,9,0), Vec3crd(0,9,16), Vec3crd(0,16,13), Vec3crd(0,13,6), Vec3crd(6,13,4), Vec3crd(2,22,0), Vec3crd(19,14,16), Vec3crd(14,19,20), Vec3crd(15,29,13), Vec3crd(29,25,27), Vec3crd(25,29,15), Vec3crd(25,15,28), Vec3crd(6,3,0), Vec3crd(3,6,5)
+ });
+ break;
+ case TestMesh::A:
+ vertices = std::vector<Vec3d>({
+ Vec3d(513.075988769531,51.6074333190918,36.0009002685547), Vec3d(516.648803710938,51.7324333190918,36.0009002685547), Vec3d(513.495178222656,51.7324333190918,36.0009002685547), Vec3d(489.391204833984,51.4824333190918,24.0011005401611), Vec3d(488.928588867188,51.7324333190918,24.0011005401611), Vec3d(492.06201171875,51.7324333190918,24.0011005401611), Vec3d(496.840393066406,51.2324333190918,24.0011005401611), Vec3d(495.195404052734,51.7324333190918,24.0011005401611), Vec3d(498.981994628906,51.7324333190918,24.0011005401611), Vec3d(506.966613769531,51.6074333190918,24.0011005401611), Vec3d(510.342010498047,51.7324333190918,24.0011005401611), Vec3d(507.163818359375,51.6074333190918,24.0011005401611), Vec3d(512.515380859375,54.7190322875977,36.0009002685547), Vec3d(514.161987304688,54.5058326721191,36.0009002685547), Vec3d(493.06201171875,54.7190322875977,36.0009002685547), Vec3d(495.195404052734,51.7324333190918,36.0009002685547), Vec3d(496.195404052734,54.7190322875977,36.0009002685547), Vec3d(497.195404052734,57.7058334350586,36.0009002685547), Vec3d(500.851989746094,60.2658309936523,36.0009002685547), Vec3d(498.915405273438,62.8258323669434,36.0009002685547), Vec3d(506.701995849609,62.8258323669434,36.0009002685547), Vec3d(503.648590087891,60.2658309936523,36.0009002685547), Vec3d(508.381805419922,57.7058334350586,36.0009002685547), Vec3d(496.418792724609,60.052433013916,36.0009002685547), Vec3d(506.515197753906,72.2124328613281,36.0009002685547), Vec3d(502.808807373047,74.5324325561523,36.0009002685547), Vec3d(503.781982421875,71.6058349609375,36.0009002685547), Vec3d(515.358764648438,55.4658317565918,36.0009002685547), Vec3d(499.375183105469,76.9058380126953,36.0009002685547), Vec3d(501.168792724609,78.0658340454102,36.0009002685547), Vec3d(504.568786621094,78.0658340454102,36.0009002685547), Vec3d(506.32861328125,81.599235534668,36.0009002685547), Vec3d(502.928588867188,81.599235534668,36.0009002685547), Vec3d(499.528594970703,81.599235534668,36.0009002685547), Vec3d(498.20361328125,77.8658294677734,36.0009002685547), Vec3d(495.195404052734,51.7324333190918,30.0011005401611), Vec3d(498.981994628906,51.7324333190918,27.0011005401611), Vec3d(506.555206298828,51.7324333190918,33.0009002685547), Vec3d(506.555206298828,51.7324333190918,36.0009002685547), Vec3d(510.342010498047,51.7324333190918,36.0009002685547), Vec3d(512.515380859375,54.7190322875977,24.0011005401611), Vec3d(509.361999511719,54.7190322875977,24.0011005401611), Vec3d(508.381805419922,57.7058334350586,24.0011005401611), Vec3d(506.701995849609,62.8258323669434,24.0011005401611), Vec3d(509.188812255859,60.052433013916,24.0011005401611), Vec3d(493.06201171875,54.7190322875977,24.0011005401611), Vec3d(503.648590087891,60.2658309936523,24.0011005401611), Vec3d(500.851989746094,60.2658309936523,24.0011005401611), Vec3d(498.915405273438,62.8258323669434,24.0011005401611), Vec3d(502.808807373047,62.8258323669434,24.0011005401611), Vec3d(491.425201416016,54.5058326721191,24.0011005401611), Vec3d(506.421813964844,76.9058380126953,24.0011005401611), Vec3d(502.808807373047,74.5324325561523,24.0011005401611), Vec3d(504.568786621094,78.0658340454102,24.0011005401611), Vec3d(506.32861328125,81.599235534668,24.0011005401611), Vec3d(507.618804931641,77.8658294677734,24.0011005401611), Vec3d(499.221801757812,72.2124328613281,24.0011005401611), Vec3d(501.835388183594,71.6058349609375,24.0011005401611), Vec3d(501.168792724609,78.0658340454102,24.0011005401611), Vec3d(499.528594970703,81.599235534668,24.0011005401611), Vec3d(502.048583984375,79.8324356079102,24.0011005401611), Vec3d(490.253601074219,55.4658317565918,24.0011005401611), Vec3d(488.928588867188,51.7324333190918,30.0011005401611), Vec3d(488.928588867188,51.7324333190918,36.0009002685547), Vec3d(490.253601074219,55.4658317565918,31.5009002685547), Vec3d(498.20361328125,77.8658294677734,34.5009002685547), Vec3d(508.381805419922,57.7058334350586,30.0011005401611), Vec3d(505.585388183594,57.7058334350586,27.0011005401611), Vec3d(502.788818359375,57.7058334350586,36.0009002685547), Vec3d(499.992004394531,57.7058334350586,33.0009002685547), Vec3d(509.851989746094,53.2258338928223,33.0009002685547), Vec3d(509.361999511719,54.7190322875977,36.0009002685547), Vec3d(508.871795654297,56.2124328613281,27.0011005401611), Vec3d(496.695404052734,56.2124328613281,33.0009002685547), Vec3d(495.695404052734,53.2258338928223,27.0011005401611), Vec3d(506.32861328125,81.599235534668,30.0011005401611), Vec3d(507.618804931641,77.8658294677734,25.5011005401611), Vec3d(515.358764648438,55.4658317565918,34.5009002685547), Vec3d(501.228607177734,81.599235534668,33.0009002685547), Vec3d(504.628601074219,81.599235534668,27.0011005401611), Vec3d(503.781982421875,71.6058349609375,33.0009002685547), Vec3d(502.808807373047,74.5324325561523,30.0011005401611), Vec3d(498.915405273438,62.8258323669434,30.0011005401611), Vec3d(500.861999511719,62.8258323669434,27.0011005401611), Vec3d(502.808807373047,62.8258323669434,36.0009002685547), Vec3d(504.755187988281,62.8258323669434,33.0009002685547), Vec3d(501.835388183594,71.6058349609375,33.0009002685547), Vec3d(499.888793945312,65.7524337768555,33.0009002685547), Vec3d(499.888793945312,65.7524337768555,36.0009002685547), Vec3d(513.128601074219,51.4824333190918,36.0009002685547), Vec3d(513.075988769531,51.6074333190918,24.0011005401611), Vec3d(516.648803710938,51.7324333190918,24.0011005401611), Vec3d(513.128601074219,51.4824333190918,24.0011005401611), Vec3d(513.495178222656,51.7324333190918,24.0011005401611), Vec3d(506.966613769531,51.6074333190918,36.0009002685547), Vec3d(507.163818359375,51.6074333190918,36.0009002685547), Vec3d(490.337799072266,51.4824333190918,24.0011005401611), Vec3d(489.391204833984,51.4824333190918,36.0009002685547), Vec3d(492.06201171875,51.7324333190918,36.0009002685547), Vec3d(490.337799072266,51.4824333190918,36.0009002685547), Vec3d(513.233764648438,51.2324333190918,24.0011005401611), Vec3d(513.233764648438,51.2324333190918,36.0009002685547), Vec3d(504.773803710938,51.4824333190918,36.0009002685547), Vec3d(504.773803710938,51.4824333190918,24.0011005401611), Vec3d(489.266998291016,51.2324333190918,24.0011005401611), Vec3d(489.266998291016,51.2324333190918,36.0009002685547), Vec3d(490.253601074219,55.4658317565918,25.5011005401611), Vec3d(499.528594970703,81.599235534668,30.0011005401611), Vec3d(498.20361328125,77.8658294677734,31.5009002685547), Vec3d(515.358764648438,55.4658317565918,28.5011005401611), Vec3d(515.358764648438,55.4658317565918,25.5011005401611), Vec3d(495.246795654297,61.0124320983887,36.0009002685547), Vec3d(490.253601074219,55.4658317565918,34.5009002685547), Vec3d(490.253601074219,55.4658317565918,36.0009002685547), Vec3d(494.228607177734,66.6658325195312,24.0011005401611), Vec3d(499.068786621094,67.5192337036133,24.0011005401611), Vec3d(498.20361328125,77.8658294677734,25.5011005401611), Vec3d(498.20361328125,77.8658294677734,24.0011005401611), Vec3d(506.608795166016,67.5192337036133,36.0009002685547), Vec3d(509.09521484375,64.7458343505859,36.0009002685547), Vec3d(507.618804931641,77.8658294677734,34.5009002685547), Vec3d(507.618804931641,77.8658294677734,36.0009002685547), Vec3d(510.385406494141,61.0124320983887,24.0011005401611), Vec3d(515.358764648438,55.4658317565918,24.0011005401611), Vec3d(489.32861328125,47.7324333190918,31.5009002685547), Vec3d(492.95361328125,47.7324333190918,33.5634994506836), Vec3d(489.32861328125,47.7324333190918,34.5009002685547), Vec3d(489.32861328125,47.7324333190918,28.5011005401611), Vec3d(489.32861328125,47.7324333190918,25.5011005401611), Vec3d(492.95361328125,47.7324333190918,26.4385013580322), Vec3d(492.95361328125,47.7324333190918,30.5635013580322), Vec3d(492.95361328125,47.7324333190918,32.0634994506836), Vec3d(492.95361328125,47.7324333190918,31.3135013580322), Vec3d(492.95361328125,47.7324333190918,35.4384994506836), Vec3d(489.32861328125,47.7324333190918,36.0009002685547), Vec3d(492.95361328125,47.7324333190918,34.3134994506836), Vec3d(492.95361328125,47.7324333190918,34.6884994506836), Vec3d(492.95361328125,47.7324333190918,27.9385013580322), Vec3d(492.95361328125,47.7324333190918,28.6885013580322), Vec3d(492.95361328125,47.7324333190918,29.0635013580322), Vec3d(489.32861328125,47.7324333190918,24.0011005401611), Vec3d(492.95361328125,47.7324333190918,24.5635013580322), Vec3d(492.95361328125,47.7324333190918,25.6885013580322), Vec3d(492.95361328125,47.7324333190918,25.3135013580322), Vec3d(492.95361328125,47.7324333190918,24.1885013580322), Vec3d(492.95361328125,47.7324333190918,24.0011005401611), Vec3d(513.443786621094,50.7324333190918,24.0011005401611), Vec3d(492.95361328125,47.7324333190918,35.8134994506836), Vec3d(492.95361328125,47.7324333190918,36.0009002685547), Vec3d(513.443786621094,50.7324333190918,36.0009002685547), Vec3d(506.350402832031,51.4824333190918,36.0009002685547), Vec3d(506.350402832031,51.4824333190918,24.0011005401611), Vec3d(492.743804931641,48.2324333190918,24.0011005401611), Vec3d(492.638793945312,48.4824333190918,24.0011005401611), Vec3d(492.743804931641,48.2324333190918,36.0009002685547), Vec3d(492.638793945312,48.4824333190918,36.0009002685547), Vec3d(490.089599609375,50.9824333190918,36.0009002685547), Vec3d(490.089599609375,50.9824333190918,24.0011005401611), Vec3d(510.342010498047,51.7324333190918,30.0011005401611), Vec3d(499.068786621094,67.5192337036133,36.0009002685547), Vec3d(494.228607177734,66.6658325195312,36.0009002685547), Vec3d(499.375183105469,76.9058380126953,24.0011005401611), Vec3d(506.421813964844,76.9058380126953,36.0009002685547), Vec3d(506.608795166016,67.5192337036133,24.0011005401611), Vec3d(505.728607177734,65.7524337768555,24.0011005401611), Vec3d(509.09521484375,64.7458343505859,24.0011005401611), Vec3d(506.701995849609,62.8258323669434,30.0011005401611), Vec3d(505.728607177734,65.7524337768555,27.0011005401611), Vec3d(501.835388183594,71.6058349609375,27.0011005401611), Vec3d(499.888793945312,65.7524337768555,27.0011005401611), Vec3d(494.228607177734,66.6658325195312,30.0011005401611), Vec3d(495.553588867188,70.3992309570312,28.5011005401611), Vec3d(492.903594970703,62.9324340820312,28.5011005401611), Vec3d(495.553588867188,70.3992309570312,31.5009002685547), Vec3d(492.903594970703,62.9324340820312,31.5009002685547), Vec3d(511.488800048828,66.6658325195312,24.0011005401611), Vec3d(511.488800048828,66.6658325195312,30.0011005401611), Vec3d(512.778564453125,62.9324340820312,28.5011005401611), Vec3d(515.358764648438,55.4658317565918,31.5009002685547), Vec3d(507.618804931641,77.8658294677734,31.5009002685547), Vec3d(510.198791503906,70.3992309570312,28.5011005401611), Vec3d(511.488800048828,66.6658325195312,36.0009002685547), Vec3d(512.778564453125,62.9324340820312,31.5009002685547), Vec3d(510.198791503906,70.3992309570312,31.5009002685547), Vec3d(502.788818359375,57.7058334350586,24.0011005401611), Vec3d(497.195404052734,57.7058334350586,30.0011005401611), Vec3d(492.903594970703,62.9324340820312,34.5009002685547), Vec3d(492.903594970703,62.9324340820312,36.0009002685547), Vec3d(495.553588867188,70.3992309570312,24.0011005401611), Vec3d(496.725189208984,69.4392318725586,24.0011005401611), Vec3d(495.553588867188,70.3992309570312,25.5011005401611), Vec3d(495.246795654297,61.0124320983887,24.0011005401611), Vec3d(492.903594970703,62.9324340820312,25.5011005401611), Vec3d(492.903594970703,62.9324340820312,24.0011005401611), Vec3d(495.553588867188,70.3992309570312,36.0009002685547), Vec3d(496.725189208984,69.4392318725586,36.0009002685547), Vec3d(495.553588867188,70.3992309570312,34.5009002685547), Vec3d(510.198791503906,70.3992309570312,36.0009002685547), Vec3d(509.002014160156,69.4392318725586,36.0009002685547), Vec3d(510.198791503906,70.3992309570312,34.5009002685547), Vec3d(512.778564453125,62.9324340820312,25.5011005401611), Vec3d(512.778564453125,62.9324340820312,24.0011005401611), Vec3d(510.198791503906,70.3992309570312,24.0011005401611), Vec3d(509.002014160156,69.4392318725586,24.0011005401611), Vec3d(510.198791503906,70.3992309570312,25.5011005401611), Vec3d(510.385406494141,61.0124320983887,36.0009002685547), Vec3d(512.778564453125,62.9324340820312,34.5009002685547), Vec3d(512.778564453125,62.9324340820312,36.0009002685547), Vec3d(496.840393066406,51.2324333190918,36.0009002685547), Vec3d(498.981994628906,51.7324333190918,36.0009002685547), Vec3d(498.981994628906,51.7324333190918,33.0009002685547), Vec3d(506.555206298828,51.7324333190918,24.0011005401611), Vec3d(506.555206298828,51.7324333190918,27.0011005401611), Vec3d(503.82861328125,47.7324333190918,30.7509002685547), Vec3d(507.45361328125,47.7324333190918,32.8134994506836), Vec3d(503.82861328125,47.7324333190918,33.7509002685547), Vec3d(503.82861328125,47.7324333190918,29.2511005401611), Vec3d(503.82861328125,47.7324333190918,26.2511005401611), Vec3d(507.45361328125,47.7324333190918,27.1885013580322), Vec3d(493.921813964844,57.2792320251465,36.0009002685547), Vec3d(491.425201416016,54.5058326721191,36.0009002685547), Vec3d(497.195404052734,57.7058334350586,24.0011005401611), Vec3d(496.418792724609,60.052433013916,24.0011005401611), Vec3d(509.188812255859,60.052433013916,36.0009002685547), Vec3d(511.675415039062,57.2792320251465,24.0011005401611), Vec3d(514.161987304688,54.5058326721191,24.0011005401611), Vec3d(507.45361328125,47.7324333190918,34.3134994506836), Vec3d(503.82861328125,47.7324333190918,35.2509002685547), Vec3d(507.45361328125,47.7324333190918,25.6885013580322), Vec3d(503.82861328125,47.7324333190918,24.7511005401611), Vec3d(500.20361328125,47.7324333190918,31.6885013580322), Vec3d(500.20361328125,47.7324333190918,28.3135013580322), Vec3d(500.20361328125,47.7324333190918,30.1885013580322), Vec3d(507.45361328125,47.7324333190918,29.8135013580322), Vec3d(507.45361328125,47.7324333190918,31.3135013580322), Vec3d(507.45361328125,47.7324333190918,30.5635013580322), Vec3d(503.82861328125,47.7324333190918,36.0009002685547), Vec3d(507.45361328125,47.7324333190918,35.4384994506836), Vec3d(507.45361328125,47.7324333190918,35.0634994506836), Vec3d(507.45361328125,47.7324333190918,28.6885013580322), Vec3d(507.45361328125,47.7324333190918,29.4385013580322), Vec3d(503.82861328125,47.7324333190918,24.0011005401611), Vec3d(507.45361328125,47.7324333190918,24.5635013580322), Vec3d(507.45361328125,47.7324333190918,24.9385013580322), Vec3d(500.20361328125,47.7324333190918,34.6884994506836), Vec3d(500.20361328125,47.7324333190918,33.1884994506836), Vec3d(500.20361328125,47.7324333190918,33.9384994506836), Vec3d(500.20361328125,47.7324333190918,25.3135013580322), Vec3d(500.20361328125,47.7324333190918,26.8135013580322), Vec3d(500.20361328125,47.7324333190918,26.0635013580322), Vec3d(500.20361328125,47.7324333190918,30.9385013580322), Vec3d(500.20361328125,47.7324333190918,35.0634994506836), Vec3d(500.20361328125,47.7324333190918,35.4384994506836), Vec3d(500.20361328125,47.7324333190918,29.0635013580322), Vec3d(500.20361328125,47.7324333190918,29.4385013580322), Vec3d(500.20361328125,47.7324333190918,24.9385013580322), Vec3d(500.20361328125,47.7324333190918,24.5635013580322), Vec3d(507.45361328125,47.7324333190918,24.1885013580322), Vec3d(507.45361328125,47.7324333190918,24.0011005401611), Vec3d(513.86376953125,49.7324333190918,24.0011005401611), Vec3d(507.45361328125,47.7324333190918,35.8134994506836), Vec3d(507.45361328125,47.7324333190918,36.0009002685547), Vec3d(513.86376953125,49.7324333190918,36.0009002685547), Vec3d(500.20361328125,47.7324333190918,24.1885013580322), Vec3d(500.20361328125,47.7324333190918,24.0011005401611), Vec3d(502.988800048828,49.7324333190918,24.0011005401611), Vec3d(500.20361328125,47.7324333190918,35.8134994506836), Vec3d(500.20361328125,47.7324333190918,36.0009002685547), Vec3d(502.988800048828,49.7324333190918,36.0009002685547), Vec3d(504.755187988281,62.8258323669434,27.0011005401611), Vec3d(499.205383300781,51.2324333190918,36.0009002685547), Vec3d(498.786193847656,51.1074333190918,36.0009002685547), Vec3d(502.358795166016,51.2324333190918,36.0009002685547), Vec3d(499.205383300781,51.2324333190918,24.0011005401611), Vec3d(502.358795166016,51.2324333190918,24.0011005401611), Vec3d(498.786193847656,51.1074333190918,24.0011005401611), Vec3d(502.568786621094,50.7324333190918,24.0011005401611), Vec3d(505.931213378906,51.3574333190918,24.0011005401611), Vec3d(509.503601074219,51.4824333190918,24.0011005401611), Vec3d(502.568786621094,50.7324333190918,36.0009002685547), Vec3d(505.931213378906,51.3574333190918,36.0009002685547), Vec3d(509.503601074219,51.4824333190918,36.0009002685547), Vec3d(499.048583984375,50.4824333190918,36.0009002685547), Vec3d(492.428588867188,48.9824333190918,36.0009002685547), Vec3d(499.048583984375,50.4824333190918,24.0011005401611), Vec3d(492.428588867188,48.9824333190918,24.0011005401611), Vec3d(506.088806152344,50.9824333190918,24.0011005401611), Vec3d(506.036010742188,51.1074333190918,24.0011005401611), Vec3d(506.088806152344,50.9824333190918,36.0009002685547), Vec3d(506.036010742188,51.1074333190918,36.0009002685547), Vec3d(498.891204833984,50.8574333190918,36.0009002685547), Vec3d(498.943786621094,50.7324333190918,36.0009002685547), Vec3d(498.891204833984,50.8574333190918,24.0011005401611), Vec3d(498.943786621094,50.7324333190918,24.0011005401611), Vec3d(499.573608398438,49.2324333190918,24.0011005401611), Vec3d(499.783813476562,48.7324333190918,24.0011005401611), Vec3d(499.573608398438,49.2324333190918,36.0009002685547), Vec3d(499.783813476562,48.7324333190918,36.0009002685547), Vec3d(506.403594970703,50.2324333190918,24.0011005401611), Vec3d(506.298797607422,50.4824333190918,24.0011005401611), Vec3d(506.403594970703,50.2324333190918,36.0009002685547), Vec3d(506.298797607422,50.4824333190918,36.0009002685547), Vec3d(501.228607177734,81.599235534668,27.0011005401611), Vec3d(502.928588867188,81.599235534668,24.0011005401611), Vec3d(499.2587890625,49.9824333190918,36.0009002685547), Vec3d(499.363800048828,49.7324333190918,36.0009002685547), Vec3d(499.2587890625,49.9824333190918,24.0011005401611), Vec3d(499.363800048828,49.7324333190918,24.0011005401611), Vec3d(496.695404052734,56.2124328613281,27.0011005401611), Vec3d(496.195404052734,54.7190322875977,24.0011005401611), Vec3d(509.851989746094,53.2258338928223,27.0011005401611), Vec3d(493.464782714844,51.1074333190918,36.0009002685547), Vec3d(493.464782714844,51.1074333190918,24.0011005401611), Vec3d(502.768798828125,51.7324333190918,24.0011005401611), Vec3d(500.215789794922,51.3574333190918,24.0011005401611), Vec3d(497.628601074219,51.2324333190918,24.0011005401611), Vec3d(502.768798828125,51.7324333190918,36.0009002685547), Vec3d(500.215789794922,51.3574333190918,36.0009002685547), Vec3d(497.628601074219,51.2324333190918,36.0009002685547), Vec3d(507.033813476562,48.7324333190918,24.0011005401611), Vec3d(506.823791503906,49.2324333190918,24.0011005401611), Vec3d(507.033813476562,48.7324333190918,36.0009002685547), Vec3d(506.823791503906,49.2324333190918,36.0009002685547), Vec3d(494.4501953125,51.1074333190918,24.0011005401611), Vec3d(494.4501953125,51.1074333190918,36.0009002685547), Vec3d(500.807006835938,51.3574333190918,36.0009002685547), Vec3d(503.591186523438,51.4824333190918,36.0009002685547), Vec3d(503.591186523438,51.4824333190918,24.0011005401611), Vec3d(500.807006835938,51.3574333190918,24.0011005401611), Vec3d(505.728607177734,65.7524337768555,36.0009002685547), Vec3d(505.728607177734,65.7524337768555,33.0009002685547), Vec3d(499.221801757812,72.2124328613281,36.0009002685547), Vec3d(501.835388183594,71.6058349609375,36.0009002685547), Vec3d(506.515197753906,72.2124328613281,24.0011005401611), Vec3d(503.781982421875,71.6058349609375,24.0011005401611), Vec3d(503.781982421875,71.6058349609375,27.0011005401611), Vec3d(499.888793945312,65.7524337768555,24.0011005401611), Vec3d(495.695404052734,53.2258338928223,33.0009002685547), Vec3d(516.648803710938,51.7324333190918,30.0011005401611), Vec3d(498.20361328125,77.8658294677734,28.5011005401611), Vec3d(505.585388183594,57.7058334350586,33.0009002685547), Vec3d(508.871795654297,56.2124328613281,33.0009002685547), Vec3d(499.992004394531,57.7058334350586,27.0011005401611), Vec3d(504.628601074219,81.599235534668,33.0009002685547), Vec3d(500.861999511719,62.8258323669434,33.0009002685547), Vec3d(496.878601074219,74.1324310302734,27.0011005401611), Vec3d(496.878601074219,74.1324310302734,33.0009002685547), Vec3d(491.57861328125,59.199031829834,27.0011005401611), Vec3d(490.253601074219,55.4658317565918,28.5011005401611), Vec3d(491.57861328125,59.199031829834,33.0009002685547), Vec3d(514.068786621094,59.199031829834,27.0011005401611), Vec3d(514.068786621094,59.199031829834,33.0009002685547), Vec3d(508.908813476562,74.1324310302734,27.0011005401611), Vec3d(507.618804931641,77.8658294677734,28.5011005401611), Vec3d(508.908813476562,74.1324310302734,33.0009002685547), Vec3d(491.271789550781,50.9824333190918,36.0009002685547), Vec3d(490.877807617188,50.9824333190918,36.0009002685547), Vec3d(491.271789550781,50.9824333190918,24.0011005401611), Vec3d(490.877807617188,50.9824333190918,24.0011005401611), Vec3d(495.213806152344,50.9824333190918,36.0009002685547), Vec3d(493.636993408203,50.9824333190918,36.0009002685547), Vec3d(495.213806152344,50.9824333190918,24.0011005401611), Vec3d(493.636993408203,50.9824333190918,24.0011005401611), Vec3d(503.985412597656,51.4824333190918,36.0009002685547), Vec3d(503.985412597656,51.4824333190918,24.0011005401611), Vec3d(511.675415039062,57.2792320251465,36.0009002685547), Vec3d(493.921813964844,57.2792320251465,24.0011005401611), Vec3d(502.768798828125,51.7324333190918,30.0011005401611), Vec3d(506.555206298828,51.7324333190918,30.0011005401611), Vec3d(498.981994628906,51.7324333190918,30.0011005401611), Vec3d(492.848815917969,50.9824333190918,24.0011005401611), Vec3d(492.848815917969,50.9824333190918,36.0009002685547), Vec3d(500.861999511719,68.6792297363281,36.0009002685547), Vec3d(500.861999511719,68.6792297363281,24.0011005401611), Vec3d(496.878601074219,74.1324310302734,24.0011005401611), Vec3d(496.878601074219,74.1324310302734,36.0009002685547), Vec3d(504.755187988281,68.6792297363281,24.0011005401611), Vec3d(504.755187988281,68.6792297363281,36.0009002685547), Vec3d(508.908813476562,74.1324310302734,36.0009002685547), Vec3d(508.908813476562,74.1324310302734,24.0011005401611), Vec3d(505.728607177734,65.7524337768555,30.0011005401611), Vec3d(504.755187988281,68.6792297363281,30.0011005401611), Vec3d(503.781982421875,71.6058349609375,30.0011005401611), Vec3d(500.861999511719,68.6792297363281,30.0011005401611), Vec3d(499.888793945312,65.7524337768555,30.0011005401611), Vec3d(501.835388183594,71.6058349609375,30.0011005401611), Vec3d(491.57861328125,59.199031829834,24.0011005401611), Vec3d(491.57861328125,59.199031829834,36.0009002685547), Vec3d(514.068786621094,59.199031829834,36.0009002685547), Vec3d(514.068786621094,59.199031829834,24.0011005401611), Vec3d(511.07861328125,47.7324333190918,34.8759002685547), Vec3d(511.07861328125,47.7324333190918,31.8759002685547), Vec3d(514.70361328125,47.7324333190918,33.9384994506836), Vec3d(511.07861328125,47.7324333190918,25.1261005401611), Vec3d(514.70361328125,47.7324333190918,26.0635013580322), Vec3d(511.07861328125,47.7324333190918,28.1261005401611), Vec3d(502.788818359375,57.7058334350586,30.0011005401611), Vec3d(502.048583984375,79.8324356079102,36.0009002685547), Vec3d(514.70361328125,47.7324333190918,30.9385013580322), Vec3d(511.07861328125,47.7324333190918,30.3759002685547), Vec3d(514.70361328125,47.7324333190918,29.0635013580322), Vec3d(511.07861328125,47.7324333190918,29.6261005401611), Vec3d(496.57861328125,47.7324333190918,31.1259002685547), Vec3d(496.57861328125,47.7324333190918,32.6259002685547), Vec3d(496.57861328125,47.7324333190918,34.1259002685547), Vec3d(496.57861328125,47.7324333190918,28.8761005401611), Vec3d(496.57861328125,47.7324333190918,27.3761005401611), Vec3d(496.57861328125,47.7324333190918,25.8761005401611), Vec3d(496.57861328125,47.7324333190918,29.6261005401611), Vec3d(514.70361328125,47.7324333190918,35.4384994506836), Vec3d(511.07861328125,47.7324333190918,35.6259002685547), Vec3d(514.70361328125,47.7324333190918,24.5635013580322), Vec3d(511.07861328125,47.7324333190918,24.3761005401611), Vec3d(496.57861328125,47.7324333190918,34.8759002685547), Vec3d(496.57861328125,47.7324333190918,25.1261005401611), Vec3d(496.57861328125,47.7324333190918,35.6259002685547), Vec3d(496.57861328125,47.7324333190918,24.3761005401611), Vec3d(511.07861328125,47.7324333190918,36.0009002685547), Vec3d(511.07861328125,47.7324333190918,24.0011005401611), Vec3d(514.70361328125,47.7324333190918,30.1885013580322), Vec3d(514.70361328125,47.7324333190918,35.8134994506836), Vec3d(514.70361328125,47.7324333190918,29.8135013580322), Vec3d(514.70361328125,47.7324333190918,24.1885013580322), Vec3d(496.57861328125,47.7324333190918,36.0009002685547), Vec3d(496.57861328125,47.7324333190918,24.0011005401611), Vec3d(510.238800048828,49.7324333190918,24.0011005401611), Vec3d(510.238800048828,49.7324333190918,36.0009002685547), Vec3d(514.70361328125,47.7324333190918,24.0011005401611), Vec3d(514.70361328125,47.7324333190918,36.0009002685547), Vec3d(496.158813476562,48.7324333190918,36.0009002685547), Vec3d(496.158813476562,48.7324333190918,24.0011005401611), Vec3d(502.808807373047,62.8258323669434,30.0011005401611), Vec3d(509.608795166016,51.2324333190918,24.0011005401611), Vec3d(509.608795166016,51.2324333190918,36.0009002685547), Vec3d(491.641204833984,50.8574333190918,24.0011005401611), Vec3d(495.423797607422,50.4824333190918,36.0009002685547), Vec3d(495.423797607422,50.4824333190918,24.0011005401611), Vec3d(491.641204833984,50.8574333190918,36.0009002685547), Vec3d(495.528594970703,50.2324333190918,24.0011005401611), Vec3d(492.0087890625,49.9824333190918,24.0011005401611), Vec3d(509.818786621094,50.7324333190918,24.0011005401611), Vec3d(495.948608398438,49.2324333190918,36.0009002685547), Vec3d(495.528594970703,50.2324333190918,36.0009002685547), Vec3d(495.948608398438,49.2324333190918,24.0011005401611), Vec3d(509.818786621094,50.7324333190918,36.0009002685547), Vec3d(492.0087890625,49.9824333190918,36.0009002685547), Vec3d(491.956207275391,50.1074333190918,24.0011005401611), Vec3d(491.956207275391,50.1074333190918,36.0009002685547), Vec3d(502.928588867188,81.599235534668,30.0011005401611), Vec3d(491.851013183594,50.3574333190918,36.0009002685547), Vec3d(491.851013183594,50.3574333190918,24.0011005401611), Vec3d(496.195404052734,54.7190322875977,30.0011005401611), Vec3d(509.361999511719,54.7190322875977,30.0011005401611), Vec3d(488.632598876953,51.7256317138672,30.0011005401611), Vec3d(488.632598876953,51.7256317138672,29.5091018676758), Vec3d(488.632598876953,51.7188339233398,24.0011005401611), Vec3d(488.632598876953,51.7256317138672,27.4929008483887), Vec3d(488.632598876953,51.7324333190918,30.0011005401611), Vec3d(488.632598876953,51.7324333190918,29.0175018310547), Vec3d(488.632598876953,51.7324333190918,24.9847011566162), Vec3d(488.632598876953,51.7324333190918,24.0011005401611), Vec3d(488.632598876953,51.7188339233398,30.0011005401611), Vec3d(488.632598876953,51.7176322937012,24.0011005401611), Vec3d(488.632598876953,51.7182312011719,30.0011005401611), Vec3d(488.632598876953,51.7176322937012,30.0011005401611), Vec3d(488.632598876953,51.715030670166,24.0011005401611), Vec3d(488.632598876953,51.7162322998047,30.0011005401611), Vec3d(488.632598876953,50.761833190918,24.0011005401611), Vec3d(488.632598876953,50.7578315734863,24.0011005401611), Vec3d(488.632598876953,50.7598342895508,30.0011005401611), Vec3d(488.632598876953,50.7522315979004,24.0011005401611), Vec3d(488.632598876953,49.7838325500488,24.0011005401611), Vec3d(488.632598876953,50.2680320739746,30.0011005401611), Vec3d(488.632598876953,51.7046318054199,24.0011005401611), Vec3d(488.632598876953,51.709831237793,30.0011005401611), Vec3d(488.632598876953,50.9120330810547,24.0011005401611), Vec3d(488.632598876953,50.8882331848145,24.0011005401611), Vec3d(488.632598876953,50.9002304077148,30.0011005401611), Vec3d(488.632598876953,47.7324333190918,24.0370998382568), Vec3d(488.632598876953,48.5612335205078,30.0011005401611), Vec3d(488.632598876953,47.7324333190918,24.0011005401611), Vec3d(488.632598876953,47.7324333190918,24.1091003417969), Vec3d(488.632598876953,48.5612335205078,30.0189018249512), Vec3d(488.632598876953,47.7324333190918,25.3211002349854), Vec3d(488.632598876953,48.5612335205078,30.0551013946533), Vec3d(488.632598876953,47.7324333190918,25.4651012420654), Vec3d(488.632598876953,48.5612335205078,30.6609001159668), Vec3d(488.632598876953,47.7324333190918,25.5371017456055), Vec3d(488.632598876953,48.5612335205078,30.7329006195068), Vec3d(488.632598876953,47.7324333190918,25.6091003417969), Vec3d(488.632598876953,48.5612335205078,30.7689018249512), Vec3d(488.632598876953,47.7324333190918,25.8971004486084), Vec3d(488.632598876953,48.5612335205078,30.8051013946533), Vec3d(488.632598876953,47.7324333190918,28.321102142334), Vec3d(488.632598876953,48.5612335205078,30.9491004943848), Vec3d(488.632598876953,47.7324333190918,28.4651012420654), Vec3d(488.632598876953,48.5612335205078,32.1609001159668), Vec3d(488.632598876953,47.7324333190918,28.5371017456055), Vec3d(488.632598876953,48.5612335205078,32.2329025268555), Vec3d(488.632598876953,47.7324333190918,28.6811008453369), Vec3d(488.632598876953,48.5612335205078,32.2689018249512), Vec3d(488.632598876953,47.7324333190918,31.1049003601074), Vec3d(488.632598876953,48.5612335205078,32.3411026000977), Vec3d(488.632598876953,47.7324333190918,31.3929004669189), Vec3d(488.632598876953,49.3900299072266,36.0009002685547), Vec3d(488.632598876953,47.7324333190918,31.536901473999), Vec3d(488.632598876953,47.7324333190918,31.6809005737305), Vec3d(488.632598876953,47.7324333190918,34.1049003601074), Vec3d(488.632598876953,47.7324333190918,34.3929023742676), Vec3d(488.632598876953,47.7324333190918,34.464900970459), Vec3d(488.632598876953,47.7324333190918,34.5369033813477), Vec3d(488.632598876953,47.7324333190918,34.6809005737305), Vec3d(488.632598876953,47.7324333190918,35.8929023742676), Vec3d(488.632598876953,47.7324333190918,35.964900970459), Vec3d(488.632598876953,47.7324333190918,36.0009002685547), Vec3d(488.632598876953,50.8816299438477,24.0011005401611), Vec3d(488.632598876953,50.8850326538086,30.0011005401611), Vec3d(488.632598876953,49.7480316162109,24.0011005401611), Vec3d(488.632598876953,49.7426300048828,24.0011005401611), Vec3d(488.632598876953,49.745231628418,30.0011005401611), Vec3d(488.632598876953,49.7592315673828,24.0011005401611), Vec3d(488.632598876953,49.7536315917969,30.0011005401611), Vec3d(488.632598876953,49.3900299072266,24.0011005401611), Vec3d(488.632598876953,49.5664329528809,30.0011005401611), Vec3d(488.632598876953,50.8786315917969,24.0011005401611), Vec3d(488.632598876953,50.7764320373535,24.0011005401611), Vec3d(488.632598876953,50.8274307250977,30.0011005401611), Vec3d(488.632598876953,50.7550315856934,30.0011005401611), Vec3d(488.632598876953,50.7692337036133,30.0011005401611), Vec3d(488.632598876953,50.9284324645996,24.0011005401611), Vec3d(488.632598876953,50.9202308654785,30.0011005401611), Vec3d(488.632598876953,51.1788330078125,24.0011005401611), Vec3d(488.632598876953,51.139232635498,24.0011005401611), Vec3d(488.632598876953,51.1590309143066,30.0011005401611), Vec3d(488.632598876953,51.2324333190918,24.0011005401611), Vec3d(488.632598876953,51.2056312561035,30.0011005401611), Vec3d(488.632598876953,51.4340324401855,24.0011005401611), Vec3d(488.632598876953,51.3946304321289,24.0011005401611), Vec3d(488.632598876953,51.4142303466797,30.0011005401611), Vec3d(488.632598876953,51.4498329162598,24.0011005401611), Vec3d(488.632598876953,51.5772323608398,30.0011005401611), Vec3d(488.632598876953,51.4418334960938,30.0011005401611), Vec3d(488.632598876953,51.3136329650879,30.0011005401611), Vec3d(488.632598876953,49.7714309692383,30.0011005401611), Vec3d(488.632598876953,51.0338325500488,30.0011005401611), Vec3d(488.632598876953,50.8816299438477,30.0011005401611), Vec3d(488.632598876953,50.8800315856934,30.0011005401611), Vec3d(488.632598876953,51.7188339233398,36.0009002685547), Vec3d(488.632598876953,51.7176322937012,36.0009002685547), Vec3d(488.632598876953,49.3900299072266,30.0011005401611), Vec3d(488.632598876953,50.7522315979004,30.0011005401611), Vec3d(488.632598876953,50.7522315979004,36.0009002685547), Vec3d(488.632598876953,49.7426300048828,30.0011005401611), Vec3d(488.632598876953,49.7426300048828,36.0009002685547), Vec3d(488.632598876953,49.7480316162109,30.0011005401611), Vec3d(488.632598876953,49.7480316162109,36.0009002685547), Vec3d(488.632598876953,51.715030670166,30.0011005401611), Vec3d(488.632598876953,51.715030670166,36.0009002685547), Vec3d(488.632598876953,50.7578315734863,30.0011005401611), Vec3d(488.632598876953,50.7578315734863,36.0009002685547), Vec3d(488.632598876953,50.761833190918,30.0011005401611), Vec3d(488.632598876953,50.761833190918,36.0009002685547), Vec3d(488.632598876953,50.8882331848145,30.0011005401611), Vec3d(488.632598876953,50.8882331848145,36.0009002685547), Vec3d(488.632598876953,49.7592315673828,30.0011005401611), Vec3d(488.632598876953,49.7592315673828,36.0009002685547), Vec3d(488.632598876953,51.1788330078125,30.0011005401611), Vec3d(488.632598876953,51.1788330078125,36.0009002685547), Vec3d(488.632598876953,50.9120330810547,30.0011005401611), Vec3d(488.632598876953,50.9120330810547,36.0009002685547), Vec3d(488.632598876953,51.4498329162598,30.0011005401611), Vec3d(488.632598876953,51.4498329162598,36.0009002685547), Vec3d(488.632598876953,51.7046318054199,30.0011005401611), Vec3d(488.632598876953,51.7046318054199,36.0009002685547), Vec3d(488.632598876953,51.2324333190918,30.0011005401611), Vec3d(488.632598876953,51.2324333190918,36.0009002685547), Vec3d(488.632598876953,51.3946304321289,30.0011005401611), Vec3d(488.632598876953,51.3946304321289,36.0009002685547), Vec3d(488.632598876953,51.4340324401855,30.0011005401611), Vec3d(488.632598876953,51.4340324401855,36.0009002685547), Vec3d(488.632598876953,49.7838325500488,30.0011005401611), Vec3d(488.632598876953,49.7838325500488,36.0009002685547), Vec3d(488.632598876953,50.7764320373535,30.0011005401611), Vec3d(488.632598876953,50.7764320373535,36.0009002685547), Vec3d(488.632598876953,51.139232635498,30.0011005401611), Vec3d(488.632598876953,51.139232635498,36.0009002685547), Vec3d(488.632598876953,50.9284324645996,30.0011005401611), Vec3d(488.632598876953,50.9284324645996,36.0009002685547), Vec3d(488.632598876953,50.8816299438477,36.0009002685547), Vec3d(488.632598876953,50.8786315917969,30.0011005401611), Vec3d(488.632598876953,50.8786315917969,36.0009002685547), Vec3d(488.632598876953,51.7324333190918,35.0173034667969), Vec3d(488.632598876953,51.7324333190918,36.0009002685547), Vec3d(488.632598876953,51.7324333190918,30.9847011566162), Vec3d(517.188415527344,51.7140884399414,24.0011005401611), Vec3d(517.188415527344,51.7140884399414,36.0009002685547), Vec3d(517.188415527344,50.4475173950195,24.0011005401611), Vec3d(517.188415527344,51.7324333190918,35.3734130859375), Vec3d(517.188415527344,51.7324333190918,36.0009002685547), Vec3d(517.188415527344,51.7324333190918,34.1185760498047), Vec3d(517.188415527344,51.7324333190918,31.88330078125), Vec3d(517.188415527344,51.7324333190918,30.0011005401611), Vec3d(517.188415527344,51.7324333190918,28.1187744140625), Vec3d(517.188415527344,51.7324333190918,25.8834266662598), Vec3d(517.188415527344,51.7324333190918,24.6285915374756), Vec3d(517.188415527344,51.7324333190918,24.0011005401611), Vec3d(517.188415527344,47.7324333190918,24.0600452423096), Vec3d(517.188415527344,47.7324333190918,24.0011005401611), Vec3d(517.188415527344,50.4475173950195,36.0009002685547), Vec3d(517.188415527344,47.7324333190918,24.1779975891113), Vec3d(517.188415527344,47.7324333190918,24.6498031616211), Vec3d(517.188415527344,47.7324333190918,28.7625770568848), Vec3d(517.188415527344,47.7324333190918,29.7061901092529), Vec3d(517.188415527344,47.7324333190918,29.9420928955078), Vec3d(517.188415527344,47.7324333190918,30.0600452423096), Vec3d(517.188415527344,47.7324333190918,30.2959480285645), Vec3d(517.188415527344,47.7324333190918,31.2395629882812), Vec3d(517.188415527344,47.7324333190918,35.3521995544434), Vec3d(517.188415527344,47.7324333190918,35.8240051269531), Vec3d(517.188415527344,47.7324333190918,35.9419555664062), Vec3d(517.188415527344,47.7324333190918,36.0009002685547)
+ });
+ facets = std::vector<Vec3crd>({
+ Vec3crd(0,1,2), Vec3crd(3,4,5), Vec3crd(6,7,8), Vec3crd(9,10,11), Vec3crd(12,2,1), Vec3crd(12,1,13), Vec3crd(14,15,16), Vec3crd(17,18,19), Vec3crd(20,21,22), Vec3crd(17,19,23), Vec3crd(24,25,26), Vec3crd(27,13,1), Vec3crd(28,25,29), Vec3crd(30,31,32), Vec3crd(28,33,34), Vec3crd(35,36,7), Vec3crd(37,38,39), Vec3crd(40,10,41), Vec3crd(42,43,44), Vec3crd(45,5,4), Vec3crd(46,47,48), Vec3crd(46,48,49), Vec3crd(45,4,50), Vec3crd(51,52,53), Vec3crd(51,54,55), Vec3crd(56,52,57), Vec3crd(58,59,60), Vec3crd(61,50,4), Vec3crd(62,63,64), Vec3crd(65,34,33), Vec3crd(66,67,42), Vec3crd(68,17,69), Vec3crd(70,71,22), Vec3crd(66,42,72), Vec3crd(73,16,15), Vec3crd(35,7,74), Vec3crd(75,76,54), Vec3crd(77,27,1), Vec3crd(78,32,31), Vec3crd(75,54,79), Vec3crd(80,26,25), Vec3crd(81,80,25), Vec3crd(82,83,48), Vec3crd(84,20,85), Vec3crd(81,25,86), Vec3crd(87,88,19), Vec3crd(0,89,1), Vec3crd(90,91,92), Vec3crd(90,10,93), Vec3crd(38,94,39), Vec3crd(94,95,39), Vec3crd(3,7,96), Vec3crd(97,15,98), Vec3crd(97,99,15), Vec3crd(92,91,100), Vec3crd(89,101,1), Vec3crd(102,39,95), Vec3crd(103,11,10), Vec3crd(104,96,7), Vec3crd(105,15,99), Vec3crd(106,61,4), Vec3crd(107,108,33), Vec3crd(76,55,54), Vec3crd(109,91,110), Vec3crd(111,23,19), Vec3crd(112,63,113), Vec3crd(114,115,48), Vec3crd(116,59,117), Vec3crd(118,20,119), Vec3crd(120,31,121), Vec3crd(122,44,43), Vec3crd(110,91,123), Vec3crd(124,125,126), Vec3crd(127,128,129), Vec3crd(127,130,124), Vec3crd(131,124,132), Vec3crd(126,133,134), Vec3crd(135,136,126), Vec3crd(137,138,127), Vec3crd(139,127,138), Vec3crd(128,140,141), Vec3crd(142,128,143), Vec3crd(144,140,145), Vec3crd(100,91,146), Vec3crd(147,148,134), Vec3crd(101,149,1), Vec3crd(102,150,39), Vec3crd(103,10,151), Vec3crd(145,140,152), Vec3crd(152,140,153), Vec3crd(148,154,134), Vec3crd(154,155,134), Vec3crd(156,15,105), Vec3crd(157,104,7), Vec3crd(36,8,7), Vec3crd(158,37,39), Vec3crd(159,19,88), Vec3crd(160,19,159), Vec3crd(161,59,58), Vec3crd(161,117,59), Vec3crd(162,31,30), Vec3crd(162,121,31), Vec3crd(163,43,164), Vec3crd(163,165,43), Vec3crd(166,167,43), Vec3crd(167,164,43), Vec3crd(168,57,52), Vec3crd(82,48,169), Vec3crd(114,170,171), Vec3crd(108,65,33), Vec3crd(64,63,112), Vec3crd(114,172,170), Vec3crd(160,173,170), Vec3crd(171,170,173), Vec3crd(172,174,170), Vec3crd(160,170,174), Vec3crd(175,176,177), Vec3crd(178,77,1), Vec3crd(179,31,120), Vec3crd(175,180,176), Vec3crd(181,182,176), Vec3crd(177,176,182), Vec3crd(180,183,176), Vec3crd(181,176,183), Vec3crd(184,42,67), Vec3crd(185,69,17), Vec3crd(160,111,19), Vec3crd(186,187,160), Vec3crd(188,189,114), Vec3crd(190,188,114), Vec3crd(114,48,191), Vec3crd(192,114,193), Vec3crd(194,160,195), Vec3crd(196,160,194), Vec3crd(197,198,181), Vec3crd(199,197,181), Vec3crd(122,43,165), Vec3crd(200,201,175), Vec3crd(202,175,203), Vec3crd(204,175,202), Vec3crd(205,119,20), Vec3crd(206,181,207), Vec3crd(208,209,15), Vec3crd(210,15,209), Vec3crd(211,10,9), Vec3crd(212,10,211), Vec3crd(213,214,215), Vec3crd(216,217,218), Vec3crd(219,14,17), Vec3crd(113,63,220), Vec3crd(221,222,48), Vec3crd(191,48,222), Vec3crd(22,223,20), Vec3crd(205,20,223), Vec3crd(224,40,42), Vec3crd(123,91,225), Vec3crd(214,226,215), Vec3crd(227,215,226), Vec3crd(218,217,228), Vec3crd(229,228,217), Vec3crd(215,230,213), Vec3crd(125,135,126), Vec3crd(217,216,231), Vec3crd(129,128,142), Vec3crd(216,213,232), Vec3crd(130,132,124), Vec3crd(213,216,233), Vec3crd(234,213,235), Vec3crd(236,227,237), Vec3crd(238,237,227), Vec3crd(239,240,216), Vec3crd(233,216,240), Vec3crd(241,242,229), Vec3crd(243,229,242), Vec3crd(215,227,244), Vec3crd(245,215,246), Vec3crd(217,247,229), Vec3crd(248,249,217), Vec3crd(232,213,250), Vec3crd(230,250,213), Vec3crd(133,147,134), Vec3crd(244,227,251), Vec3crd(236,252,227), Vec3crd(251,227,252), Vec3crd(231,216,253), Vec3crd(254,253,216), Vec3crd(141,140,144), Vec3crd(247,255,229), Vec3crd(241,229,256), Vec3crd(255,256,229), Vec3crd(257,241,258), Vec3crd(259,146,91), Vec3crd(260,261,236), Vec3crd(262,1,149), Vec3crd(263,264,241), Vec3crd(265,241,264), Vec3crd(266,236,267), Vec3crd(268,267,236), Vec3crd(49,48,83), Vec3crd(166,43,269), Vec3crd(270,271,272), Vec3crd(273,274,275), Vec3crd(276,274,277), Vec3crd(278,151,10), Vec3crd(279,280,272), Vec3crd(281,39,150), Vec3crd(272,282,279), Vec3crd(155,283,134), Vec3crd(274,276,284), Vec3crd(153,140,285), Vec3crd(286,276,287), Vec3crd(265,276,286), Vec3crd(288,289,279), Vec3crd(268,288,279), Vec3crd(290,291,272), Vec3crd(271,290,272), Vec3crd(292,274,293), Vec3crd(275,274,292), Vec3crd(294,265,295), Vec3crd(276,265,294), Vec3crd(296,297,268), Vec3crd(279,296,268), Vec3crd(241,265,298), Vec3crd(298,265,299), Vec3crd(236,300,268), Vec3crd(300,301,268), Vec3crd(107,33,78), Vec3crd(302,303,59), Vec3crd(304,305,279), Vec3crd(282,304,279), Vec3crd(306,276,307), Vec3crd(284,276,306), Vec3crd(185,17,73), Vec3crd(308,309,221), Vec3crd(158,39,70), Vec3crd(310,41,10), Vec3crd(15,311,208), Vec3crd(7,6,312), Vec3crd(313,314,6), Vec3crd(315,6,314), Vec3crd(316,208,317), Vec3crd(318,317,208), Vec3crd(258,241,319), Vec3crd(319,241,320), Vec3crd(261,321,236), Vec3crd(321,322,236), Vec3crd(6,315,323), Vec3crd(208,324,318), Vec3crd(270,325,318), Vec3crd(326,318,325), Vec3crd(327,328,315), Vec3crd(273,315,328), Vec3crd(118,329,20), Vec3crd(330,20,329), Vec3crd(331,332,25), Vec3crd(86,25,332), Vec3crd(333,334,52), Vec3crd(335,52,334), Vec3crd(115,336,48), Vec3crd(169,48,336), Vec3crd(62,106,4), Vec3crd(35,15,210), Vec3crd(35,337,15), Vec3crd(158,10,212), Vec3crd(158,310,10), Vec3crd(338,178,1), Vec3crd(339,59,116), Vec3crd(107,302,59), Vec3crd(66,22,340), Vec3crd(66,341,22), Vec3crd(185,221,342), Vec3crd(185,308,221), Vec3crd(75,31,179), Vec3crd(75,343,31), Vec3crd(166,20,330), Vec3crd(166,85,20), Vec3crd(81,52,335), Vec3crd(81,168,52), Vec3crd(82,19,344), Vec3crd(82,87,19), Vec3crd(108,339,345), Vec3crd(346,108,345), Vec3crd(64,347,348), Vec3crd(349,347,64), Vec3crd(178,109,350), Vec3crd(351,178,350), Vec3crd(179,352,353), Vec3crd(354,352,179), Vec3crd(355,208,356), Vec3crd(356,208,311), Vec3crd(357,358,6), Vec3crd(358,312,6), Vec3crd(68,22,21), Vec3crd(68,340,22), Vec3crd(221,48,47), Vec3crd(184,342,221), Vec3crd(359,270,360), Vec3crd(318,360,270), Vec3crd(361,362,273), Vec3crd(315,273,362), Vec3crd(272,102,270), Vec3crd(363,270,102), Vec3crd(274,273,103), Vec3crd(364,103,273), Vec3crd(21,19,18), Vec3crd(21,20,84), Vec3crd(184,46,42), Vec3crd(43,42,46), Vec3crd(12,22,71), Vec3crd(365,22,12), Vec3crd(14,98,15), Vec3crd(14,220,63), Vec3crd(40,93,10), Vec3crd(40,225,91), Vec3crd(45,221,309), Vec3crd(366,221,45), Vec3crd(313,367,212), Vec3crd(212,367,368), Vec3crd(36,369,367), Vec3crd(313,36,367), Vec3crd(316,37,367), Vec3crd(37,368,367), Vec3crd(210,367,369), Vec3crd(316,367,210), Vec3crd(362,370,315), Vec3crd(370,323,315), Vec3crd(360,318,371), Vec3crd(371,318,324), Vec3crd(372,331,159), Vec3crd(159,195,160), Vec3crd(373,115,56), Vec3crd(115,114,189), Vec3crd(52,56,161), Vec3crd(374,161,56), Vec3crd(25,28,331), Vec3crd(375,331,28), Vec3crd(376,333,163), Vec3crd(163,203,175), Vec3crd(377,118,24), Vec3crd(118,181,198), Vec3crd(25,24,162), Vec3crd(378,162,24), Vec3crd(52,51,333), Vec3crd(379,333,51), Vec3crd(167,380,381), Vec3crd(376,167,381), Vec3crd(377,381,330), Vec3crd(330,381,380), Vec3crd(335,381,382), Vec3crd(376,381,335), Vec3crd(373,383,169), Vec3crd(169,383,384), Vec3crd(168,385,383), Vec3crd(373,168,383), Vec3crd(372,87,383), Vec3crd(87,384,383), Vec3crd(377,80,381), Vec3crd(80,382,381), Vec3crd(86,383,385), Vec3crd(372,383,86), Vec3crd(106,348,347), Vec3crd(386,106,347), Vec3crd(375,65,346), Vec3crd(108,346,65), Vec3crd(64,112,349), Vec3crd(387,349,112), Vec3crd(171,190,114), Vec3crd(346,345,171), Vec3crd(374,190,345), Vec3crd(171,345,190), Vec3crd(349,172,347), Vec3crd(172,114,192), Vec3crd(386,347,192), Vec3crd(172,192,347), Vec3crd(173,160,196), Vec3crd(171,173,346), Vec3crd(375,346,196), Vec3crd(173,196,346), Vec3crd(172,349,174), Vec3crd(174,186,160), Vec3crd(387,186,349), Vec3crd(174,349,186), Vec3crd(64,348,62), Vec3crd(106,62,348), Vec3crd(108,107,339), Vec3crd(59,339,107), Vec3crd(374,345,116), Vec3crd(339,116,345), Vec3crd(76,353,352), Vec3crd(379,76,352), Vec3crd(388,77,351), Vec3crd(178,351,77), Vec3crd(179,120,354), Vec3crd(378,354,120), Vec3crd(177,200,175), Vec3crd(351,350,177), Vec3crd(389,200,350), Vec3crd(177,350,200), Vec3crd(354,180,352), Vec3crd(180,175,204), Vec3crd(379,352,204), Vec3crd(180,204,352), Vec3crd(182,181,206), Vec3crd(177,182,351), Vec3crd(388,351,206), Vec3crd(182,206,351), Vec3crd(180,354,183), Vec3crd(183,199,181), Vec3crd(378,199,354), Vec3crd(183,354,199), Vec3crd(91,109,338), Vec3crd(178,338,109), Vec3crd(76,75,353), Vec3crd(179,353,75), Vec3crd(389,350,110), Vec3crd(109,110,350), Vec3crd(390,391,392), Vec3crd(393,394,395), Vec3crd(224,122,389), Vec3crd(122,175,201), Vec3crd(365,388,205), Vec3crd(205,207,181), Vec3crd(66,340,396), Vec3crd(68,396,340), Vec3crd(184,396,342), Vec3crd(185,342,396), Vec3crd(66,396,67), Vec3crd(184,67,396), Vec3crd(68,69,396), Vec3crd(185,396,69), Vec3crd(219,111,387), Vec3crd(111,160,187), Vec3crd(366,386,191), Vec3crd(191,193,114), Vec3crd(150,272,280), Vec3crd(102,272,150), Vec3crd(151,277,274), Vec3crd(103,151,274), Vec3crd(161,374,117), Vec3crd(116,117,374), Vec3crd(366,61,386), Vec3crd(106,386,61), Vec3crd(111,187,387), Vec3crd(186,387,187), Vec3crd(56,188,374), Vec3crd(190,374,188), Vec3crd(191,386,193), Vec3crd(192,193,386), Vec3crd(331,375,194), Vec3crd(196,194,375), Vec3crd(28,34,375), Vec3crd(65,375,34), Vec3crd(219,387,113), Vec3crd(112,113,387), Vec3crd(224,389,123), Vec3crd(110,123,389), Vec3crd(51,55,379), Vec3crd(76,379,55), Vec3crd(24,197,378), Vec3crd(199,378,197), Vec3crd(122,201,389), Vec3crd(200,389,201), Vec3crd(333,379,202), Vec3crd(204,202,379), Vec3crd(205,388,207), Vec3crd(206,207,388), Vec3crd(365,27,388), Vec3crd(77,388,27), Vec3crd(162,378,121), Vec3crd(120,121,378), Vec3crd(162,30,25), Vec3crd(30,29,25), Vec3crd(51,53,54), Vec3crd(303,60,59), Vec3crd(28,29,33), Vec3crd(29,397,33), Vec3crd(161,58,52), Vec3crd(53,52,58), Vec3crd(21,84,19), Vec3crd(84,344,19), Vec3crd(46,49,43), Vec3crd(49,269,43), Vec3crd(208,316,209), Vec3crd(210,209,316), Vec3crd(327,313,211), Vec3crd(212,211,313), Vec3crd(36,35,369), Vec3crd(210,369,35), Vec3crd(37,158,368), Vec3crd(212,368,158), Vec3crd(6,8,313), Vec3crd(36,313,8), Vec3crd(326,38,316), Vec3crd(37,316,38), Vec3crd(392,391,398), Vec3crd(399,398,391), Vec3crd(394,400,395), Vec3crd(401,395,400), Vec3crd(390,214,391), Vec3crd(214,213,234), Vec3crd(393,395,218), Vec3crd(218,239,216), Vec3crd(402,230,403), Vec3crd(230,215,245), Vec3crd(125,124,131), Vec3crd(404,125,403), Vec3crd(405,406,231), Vec3crd(231,248,217), Vec3crd(129,137,127), Vec3crd(407,406,129), Vec3crd(130,127,139), Vec3crd(402,130,408), Vec3crd(194,195,331), Vec3crd(159,331,195), Vec3crd(115,189,56), Vec3crd(188,56,189), Vec3crd(14,219,220), Vec3crd(113,220,219), Vec3crd(45,50,366), Vec3crd(61,366,50), Vec3crd(221,366,222), Vec3crd(191,222,366), Vec3crd(17,23,219), Vec3crd(111,219,23), Vec3crd(118,198,24), Vec3crd(197,24,198), Vec3crd(202,203,333), Vec3crd(163,333,203), Vec3crd(40,224,225), Vec3crd(123,225,224), Vec3crd(12,13,365), Vec3crd(27,365,13), Vec3crd(22,365,223), Vec3crd(205,223,365), Vec3crd(42,44,224), Vec3crd(122,224,44), Vec3crd(399,391,234), Vec3crd(214,234,391), Vec3crd(401,239,395), Vec3crd(218,395,239), Vec3crd(214,390,226), Vec3crd(226,238,227), Vec3crd(218,228,393), Vec3crd(228,229,243), Vec3crd(401,399,233), Vec3crd(233,235,213), Vec3crd(392,409,390), Vec3crd(410,390,409), Vec3crd(394,393,411), Vec3crd(412,411,393), Vec3crd(402,403,131), Vec3crd(125,131,403), Vec3crd(405,137,406), Vec3crd(129,406,137), Vec3crd(405,408,139), Vec3crd(130,139,408), Vec3crd(230,245,403), Vec3crd(404,403,245), Vec3crd(231,406,248), Vec3crd(407,248,406), Vec3crd(232,254,216), Vec3crd(402,408,232), Vec3crd(413,404,244), Vec3crd(244,246,215), Vec3crd(414,247,407), Vec3crd(247,217,249), Vec3crd(133,126,136), Vec3crd(415,133,413), Vec3crd(141,143,128), Vec3crd(416,414,141), Vec3crd(410,238,390), Vec3crd(226,390,238), Vec3crd(412,393,243), Vec3crd(228,243,393), Vec3crd(233,399,235), Vec3crd(234,235,399), Vec3crd(237,260,236), Vec3crd(238,410,237), Vec3crd(417,260,410), Vec3crd(237,410,260), Vec3crd(239,401,240), Vec3crd(233,240,401), Vec3crd(242,241,257), Vec3crd(243,242,412), Vec3crd(418,412,257), Vec3crd(242,257,412), Vec3crd(401,419,399), Vec3crd(398,399,419), Vec3crd(417,410,420), Vec3crd(409,420,410), Vec3crd(400,421,401), Vec3crd(419,401,421), Vec3crd(418,422,412), Vec3crd(411,412,422), Vec3crd(413,135,404), Vec3crd(125,404,135), Vec3crd(414,407,142), Vec3crd(129,142,407), Vec3crd(130,402,132), Vec3crd(131,132,402), Vec3crd(133,136,413), Vec3crd(135,413,136), Vec3crd(423,147,415), Vec3crd(133,415,147), Vec3crd(137,405,138), Vec3crd(139,138,405), Vec3crd(141,414,143), Vec3crd(142,143,414), Vec3crd(424,416,144), Vec3crd(141,144,416), Vec3crd(405,254,408), Vec3crd(232,408,254), Vec3crd(244,404,246), Vec3crd(245,246,404), Vec3crd(247,249,407), Vec3crd(248,407,249), Vec3crd(232,250,402), Vec3crd(230,402,250), Vec3crd(415,413,251), Vec3crd(244,251,413), Vec3crd(252,236,266), Vec3crd(251,252,415), Vec3crd(423,415,266), Vec3crd(252,266,415), Vec3crd(231,253,405), Vec3crd(254,405,253), Vec3crd(416,255,414), Vec3crd(247,414,255), Vec3crd(256,263,241), Vec3crd(255,416,256), Vec3crd(424,263,416), Vec3crd(256,416,263), Vec3crd(257,258,418), Vec3crd(425,418,258), Vec3crd(260,417,261), Vec3crd(426,261,417), Vec3crd(422,418,427), Vec3crd(427,259,91), Vec3crd(420,428,417), Vec3crd(428,1,262), Vec3crd(147,423,148), Vec3crd(429,148,423), Vec3crd(263,424,264), Vec3crd(264,295,265), Vec3crd(266,267,423), Vec3crd(267,268,297), Vec3crd(144,145,424), Vec3crd(430,424,145), Vec3crd(49,431,269), Vec3crd(166,269,431), Vec3crd(82,431,83), Vec3crd(49,83,431), Vec3crd(84,85,431), Vec3crd(166,431,85), Vec3crd(82,344,431), Vec3crd(84,431,344), Vec3crd(432,278,90), Vec3crd(10,90,278), Vec3crd(433,0,281), Vec3crd(39,281,0), Vec3crd(362,361,434), Vec3crd(435,271,359), Vec3crd(270,359,271), Vec3crd(436,361,275), Vec3crd(273,275,361), Vec3crd(360,437,359), Vec3crd(277,287,276), Vec3crd(151,278,277), Vec3crd(280,279,289), Vec3crd(150,280,281), Vec3crd(436,438,439), Vec3crd(439,285,140), Vec3crd(90,92,432), Vec3crd(440,432,92), Vec3crd(282,272,291), Vec3crd(441,282,442), Vec3crd(284,293,274), Vec3crd(443,438,284), Vec3crd(278,432,286), Vec3crd(286,299,265), Vec3crd(281,288,433), Vec3crd(288,268,301), Vec3crd(0,433,89), Vec3crd(444,89,433), Vec3crd(435,445,442), Vec3crd(445,134,283), Vec3crd(439,446,436), Vec3crd(361,436,446), Vec3crd(442,290,435), Vec3crd(271,435,290), Vec3crd(438,436,292), Vec3crd(275,292,436), Vec3crd(445,435,447), Vec3crd(359,447,435), Vec3crd(286,287,278), Vec3crd(277,278,287), Vec3crd(288,281,289), Vec3crd(280,289,281), Vec3crd(145,152,430), Vec3crd(443,430,152), Vec3crd(148,429,154), Vec3crd(441,154,429), Vec3crd(424,430,294), Vec3crd(294,307,276), Vec3crd(423,296,429), Vec3crd(296,279,305), Vec3crd(425,440,100), Vec3crd(92,100,440), Vec3crd(290,442,291), Vec3crd(282,291,442), Vec3crd(292,293,438), Vec3crd(284,438,293), Vec3crd(298,320,241), Vec3crd(432,440,298), Vec3crd(300,236,322), Vec3crd(433,300,444), Vec3crd(426,101,444), Vec3crd(89,444,101), Vec3crd(107,448,302), Vec3crd(302,79,54), Vec3crd(78,31,343), Vec3crd(107,78,448), Vec3crd(75,79,448), Vec3crd(302,448,79), Vec3crd(78,343,448), Vec3crd(75,448,343), Vec3crd(427,418,259), Vec3crd(425,259,418), Vec3crd(428,262,417), Vec3crd(426,417,262), Vec3crd(437,449,359), Vec3crd(447,359,449), Vec3crd(434,361,450), Vec3crd(446,450,361), Vec3crd(32,33,397), Vec3crd(78,33,32), Vec3crd(53,303,54), Vec3crd(302,54,303), Vec3crd(152,153,443), Vec3crd(438,443,153), Vec3crd(429,304,441), Vec3crd(282,441,304), Vec3crd(430,443,306), Vec3crd(284,306,443), Vec3crd(154,441,155), Vec3crd(442,155,441), Vec3crd(298,299,432), Vec3crd(286,432,299), Vec3crd(300,433,301), Vec3crd(288,301,433), Vec3crd(185,451,308), Vec3crd(308,74,7), Vec3crd(73,15,337), Vec3crd(185,73,451), Vec3crd(35,74,451), Vec3crd(308,451,74), Vec3crd(73,337,451), Vec3crd(35,451,337), Vec3crd(158,452,310), Vec3crd(310,72,42), Vec3crd(70,22,341), Vec3crd(158,70,452), Vec3crd(66,72,452), Vec3crd(310,452,72), Vec3crd(70,341,452), Vec3crd(66,452,341), Vec3crd(313,327,314), Vec3crd(315,314,327), Vec3crd(316,317,326), Vec3crd(318,326,317), Vec3crd(15,156,311), Vec3crd(356,311,156), Vec3crd(7,312,157), Vec3crd(358,157,312), Vec3crd(211,9,327), Vec3crd(364,327,9), Vec3crd(38,326,94), Vec3crd(363,94,326), Vec3crd(294,295,424), Vec3crd(264,424,295), Vec3crd(296,423,297), Vec3crd(267,297,423), Vec3crd(262,149,426), Vec3crd(101,426,149), Vec3crd(258,319,425), Vec3crd(440,425,319), Vec3crd(261,426,321), Vec3crd(444,321,426), Vec3crd(259,425,146), Vec3crd(100,146,425), Vec3crd(306,307,430), Vec3crd(294,430,307), Vec3crd(304,429,305), Vec3crd(296,305,429), Vec3crd(319,320,440), Vec3crd(298,440,320), Vec3crd(321,444,322), Vec3crd(300,322,444), Vec3crd(445,283,442), Vec3crd(155,442,283), Vec3crd(439,438,285), Vec3crd(153,285,438), Vec3crd(17,68,18), Vec3crd(21,18,68), Vec3crd(46,184,47), Vec3crd(221,47,184), Vec3crd(102,95,363), Vec3crd(94,363,95), Vec3crd(9,11,364), Vec3crd(103,364,11), Vec3crd(6,323,357), Vec3crd(370,357,323), Vec3crd(371,324,355), Vec3crd(208,355,324), Vec3crd(270,363,325), Vec3crd(326,325,363), Vec3crd(327,364,328), Vec3crd(273,328,364), Vec3crd(0,2,39), Vec3crd(12,39,2), Vec3crd(90,93,91), Vec3crd(40,91,93), Vec3crd(14,16,17), Vec3crd(73,17,16), Vec3crd(45,309,7), Vec3crd(308,7,309), Vec3crd(12,71,39), Vec3crd(70,39,71), Vec3crd(40,41,42), Vec3crd(310,42,41), Vec3crd(97,98,63), Vec3crd(14,63,98), Vec3crd(3,5,7), Vec3crd(45,7,5), Vec3crd(118,377,329), Vec3crd(330,329,377), Vec3crd(331,372,332), Vec3crd(86,332,372), Vec3crd(333,376,334), Vec3crd(335,334,376), Vec3crd(115,373,336), Vec3crd(169,336,373), Vec3crd(167,166,380), Vec3crd(330,380,166), Vec3crd(80,81,382), Vec3crd(335,382,81), Vec3crd(86,385,81), Vec3crd(168,81,385), Vec3crd(169,384,82), Vec3crd(87,82,384), Vec3crd(159,88,372), Vec3crd(87,372,88), Vec3crd(163,164,376), Vec3crd(167,376,164), Vec3crd(24,26,377), Vec3crd(80,377,26), Vec3crd(56,57,373), Vec3crd(168,373,57), Vec3crd(32,397,30), Vec3crd(29,30,397), Vec3crd(58,60,53), Vec3crd(303,53,60), Vec3crd(205,181,119), Vec3crd(118,119,181), Vec3crd(163,175,165), Vec3crd(122,165,175), Vec3crd(453,454,455), Vec3crd(454,456,455), Vec3crd(457,455,456), Vec3crd(458,455,457), Vec3crd(459,455,458), Vec3crd(460,455,459), Vec3crd(461,462,463), Vec3crd(464,465,466), Vec3crd(467,468,469), Vec3crd(470,471,472), Vec3crd(465,473,474), Vec3crd(475,476,477), Vec3crd(478,479,480), Vec3crd(481,482,478), Vec3crd(483,484,481), Vec3crd(485,486,483), Vec3crd(487,488,485), Vec3crd(489,490,487), Vec3crd(491,492,489), Vec3crd(493,494,491), Vec3crd(495,496,493), Vec3crd(497,498,495), Vec3crd(499,500,497), Vec3crd(501,502,499), Vec3crd(503,504,501), Vec3crd(505,504,503), Vec3crd(506,504,505), Vec3crd(507,504,506), Vec3crd(508,504,507), Vec3crd(509,504,508), Vec3crd(510,504,509), Vec3crd(511,504,510), Vec3crd(512,504,511), Vec3crd(513,504,512), Vec3crd(514,504,513), Vec3crd(476,515,516), Vec3crd(517,518,519), Vec3crd(520,517,521), Vec3crd(518,522,523), Vec3crd(522,480,479), Vec3crd(524,525,526), Vec3crd(468,470,527), Vec3crd(525,467,528), Vec3crd(529,475,530), Vec3crd(531,532,533), Vec3crd(534,531,535), Vec3crd(536,537,538), Vec3crd(473,539,540), Vec3crd(539,536,541), Vec3crd(537,534,542), Vec3crd(471,520,543), Vec3crd(532,529,544), Vec3crd(545,524,546), Vec3crd(453,461,547), Vec3crd(463,464,548), Vec3crd(523,549,504), Vec3crd(527,550,551), Vec3crd(519,552,553), Vec3crd(521,554,555), Vec3crd(466,556,557), Vec3crd(469,558,559), Vec3crd(528,560,561), Vec3crd(477,562,563), Vec3crd(543,564,565), Vec3crd(535,566,567), Vec3crd(530,568,569), Vec3crd(540,570,571), Vec3crd(474,572,573), Vec3crd(542,574,575), Vec3crd(538,576,577), Vec3crd(541,578,579), Vec3crd(472,580,581), Vec3crd(526,582,583), Vec3crd(533,584,585), Vec3crd(544,586,587), Vec3crd(516,545,588), Vec3crd(588,589,590), Vec3crd(455,460,4), Vec3crd(591,592,63), Vec3crd(462,455,4), Vec3crd(592,547,63), Vec3crd(547,548,63), Vec3crd(465,462,4), Vec3crd(548,557,63), Vec3crd(127,124,501), Vec3crd(127,501,499), Vec3crd(505,503,124), Vec3crd(124,126,507), Vec3crd(124,507,506), Vec3crd(509,508,126), Vec3crd(126,134,512), Vec3crd(126,512,511), Vec3crd(510,509,126), Vec3crd(128,127,493), Vec3crd(128,493,491), Vec3crd(497,495,127), Vec3crd(489,487,128), Vec3crd(140,128,483), Vec3crd(140,483,481), Vec3crd(487,485,128), Vec3crd(478,480,140), Vec3crd(480,522,140), Vec3crd(514,513,134), Vec3crd(504,514,134), Vec3crd(551,581,437), Vec3crd(471,470,434), Vec3crd(445,447,555), Vec3crd(445,555,553), Vec3crd(134,445,553), Vec3crd(134,553,504), Vec3crd(446,439,518), Vec3crd(446,518,517), Vec3crd(439,140,522), Vec3crd(439,522,518), Vec3crd(515,476,358), Vec3crd(563,588,356), Vec3crd(557,573,63), Vec3crd(473,465,4), Vec3crd(437,360,559), Vec3crd(437,559,551), Vec3crd(360,371,561), Vec3crd(360,561,559), Vec3crd(362,434,470), Vec3crd(362,470,468), Vec3crd(370,362,468), Vec3crd(370,468,467), Vec3crd(499,497,127), Vec3crd(506,505,124), Vec3crd(495,493,127), Vec3crd(513,512,134), Vec3crd(481,478,140), Vec3crd(447,449,565), Vec3crd(447,565,555), Vec3crd(450,446,517), Vec3crd(450,517,520), Vec3crd(356,156,569), Vec3crd(356,569,563), Vec3crd(157,358,476), Vec3crd(157,476,475), Vec3crd(357,370,467), Vec3crd(357,467,525), Vec3crd(371,355,583), Vec3crd(371,583,561), Vec3crd(460,459,4), Vec3crd(63,62,593), Vec3crd(63,593,591), Vec3crd(62,4,459), Vec3crd(62,459,458), Vec3crd(532,531,104), Vec3crd(531,534,104), Vec3crd(567,585,105), Vec3crd(575,567,105), Vec3crd(4,3,539), Vec3crd(4,539,473), Vec3crd(536,539,3), Vec3crd(97,63,573), Vec3crd(97,573,571), Vec3crd(571,579,97), Vec3crd(99,97,579), Vec3crd(99,579,577), Vec3crd(105,99,577), Vec3crd(105,577,575), Vec3crd(96,104,534), Vec3crd(96,534,537), Vec3crd(3,96,537), Vec3crd(3,537,536), Vec3crd(503,501,124), Vec3crd(508,507,126), Vec3crd(491,489,128), Vec3crd(511,510,126), Vec3crd(485,483,128), Vec3crd(434,450,520), Vec3crd(434,520,471), Vec3crd(449,437,581), Vec3crd(449,581,565), Vec3crd(156,105,585), Vec3crd(156,585,587), Vec3crd(587,569,156), Vec3crd(104,157,529), Vec3crd(104,529,532), Vec3crd(475,529,157), Vec3crd(590,583,355), Vec3crd(355,356,588), Vec3crd(355,588,590), Vec3crd(358,357,524), Vec3crd(358,524,515), Vec3crd(525,524,357), Vec3crd(458,457,62), Vec3crd(457,593,62), Vec3crd(479,478,482), Vec3crd(479,504,549), Vec3crd(479,482,504), Vec3crd(482,481,484), Vec3crd(472,551,550), Vec3crd(581,551,472), Vec3crd(482,484,504), Vec3crd(484,483,486), Vec3crd(523,553,552), Vec3crd(504,553,523), Vec3crd(540,573,572), Vec3crd(571,573,540), Vec3crd(544,585,584), Vec3crd(587,585,544), Vec3crd(542,577,576), Vec3crd(575,577,542), Vec3crd(526,590,589), Vec3crd(583,590,526), Vec3crd(535,575,574), Vec3crd(567,575,535), Vec3crd(533,567,566), Vec3crd(585,567,533), Vec3crd(538,579,578), Vec3crd(577,579,538), Vec3crd(543,581,580), Vec3crd(565,581,543), Vec3crd(477,569,568), Vec3crd(563,569,477), Vec3crd(530,587,586), Vec3crd(569,587,530), Vec3crd(541,571,570), Vec3crd(579,571,541), Vec3crd(528,583,582), Vec3crd(561,583,528), Vec3crd(591,453,592), Vec3crd(547,592,453), Vec3crd(521,565,564), Vec3crd(555,565,521), Vec3crd(474,557,556), Vec3crd(573,557,474), Vec3crd(516,563,562), Vec3crd(588,563,516), Vec3crd(519,555,554), Vec3crd(553,555,519), Vec3crd(527,559,558), Vec3crd(551,559,527), Vec3crd(469,561,560), Vec3crd(559,561,469), Vec3crd(462,461,455), Vec3crd(453,455,461), Vec3crd(461,463,547), Vec3crd(548,547,463), Vec3crd(465,464,462), Vec3crd(463,462,464), Vec3crd(464,466,548), Vec3crd(557,548,466), Vec3crd(469,560,467), Vec3crd(528,467,560), Vec3crd(472,550,470), Vec3crd(527,470,550), Vec3crd(474,556,465), Vec3crd(466,465,556), Vec3crd(477,568,475), Vec3crd(530,475,568), Vec3crd(516,562,476), Vec3crd(477,476,562), Vec3crd(519,554,517), Vec3crd(521,517,554), Vec3crd(521,564,520), Vec3crd(543,520,564), Vec3crd(523,552,518), Vec3crd(519,518,552), Vec3crd(479,549,522), Vec3crd(523,522,549), Vec3crd(526,589,524), Vec3crd(589,546,524), Vec3crd(527,558,468), Vec3crd(469,468,558), Vec3crd(528,582,525), Vec3crd(526,525,582), Vec3crd(530,586,529), Vec3crd(544,529,586), Vec3crd(533,566,531), Vec3crd(535,531,566), Vec3crd(535,574,534), Vec3crd(542,534,574), Vec3crd(538,578,536), Vec3crd(541,536,578), Vec3crd(540,572,473), Vec3crd(474,473,572), Vec3crd(541,570,539), Vec3crd(540,539,570), Vec3crd(542,576,537), Vec3crd(538,537,576), Vec3crd(543,580,471), Vec3crd(472,471,580), Vec3crd(544,584,532), Vec3crd(533,532,584), Vec3crd(524,545,515), Vec3crd(516,515,545), Vec3crd(545,546,588), Vec3crd(589,588,546), Vec3crd(453,591,454), Vec3crd(593,454,591), Vec3crd(484,486,504), Vec3crd(486,485,488), Vec3crd(486,488,504), Vec3crd(488,487,490), Vec3crd(488,490,504), Vec3crd(490,489,492), Vec3crd(490,492,504), Vec3crd(492,491,494), Vec3crd(492,494,504), Vec3crd(494,493,496), Vec3crd(494,496,504), Vec3crd(496,495,498), Vec3crd(496,498,504), Vec3crd(498,497,500), Vec3crd(498,500,504), Vec3crd(500,499,502), Vec3crd(500,502,504), Vec3crd(501,504,502), Vec3crd(454,593,456), Vec3crd(457,456,593), Vec3crd(594,595,596), Vec3crd(597,598,594), Vec3crd(599,597,594), Vec3crd(600,599,594), Vec3crd(601,600,594), Vec3crd(602,601,594), Vec3crd(603,602,594), Vec3crd(604,603,594), Vec3crd(605,604,594), Vec3crd(606,607,608), Vec3crd(609,606,608), Vec3crd(610,609,608), Vec3crd(611,610,608), Vec3crd(612,611,608), Vec3crd(613,612,608), Vec3crd(614,613,608), Vec3crd(615,614,608), Vec3crd(616,615,608), Vec3crd(617,616,608), Vec3crd(618,617,608), Vec3crd(619,618,608), Vec3crd(620,619,608), Vec3crd(596,608,607), Vec3crd(595,594,598), Vec3crd(608,596,595), Vec3crd(605,594,91), Vec3crd(91,338,602), Vec3crd(91,602,603), Vec3crd(598,597,1), Vec3crd(594,596,91), Vec3crd(608,595,1), Vec3crd(595,598,1), Vec3crd(616,617,392), Vec3crd(610,611,394), Vec3crd(419,421,613), Vec3crd(419,613,614), Vec3crd(422,427,607), Vec3crd(422,607,606), Vec3crd(427,91,596), Vec3crd(427,596,607), Vec3crd(428,420,619), Vec3crd(428,619,620), Vec3crd(1,428,620), Vec3crd(1,620,608), Vec3crd(420,409,618), Vec3crd(420,618,619), Vec3crd(411,422,606), Vec3crd(411,606,609), Vec3crd(398,419,614), Vec3crd(398,614,615), Vec3crd(421,400,612), Vec3crd(421,612,613), Vec3crd(409,392,617), Vec3crd(409,617,618), Vec3crd(394,411,609), Vec3crd(394,609,610), Vec3crd(604,605,91), Vec3crd(338,1,599), Vec3crd(338,599,600), Vec3crd(392,398,615), Vec3crd(392,615,616), Vec3crd(400,394,611), Vec3crd(400,611,612), Vec3crd(603,604,91), Vec3crd(601,602,338), Vec3crd(597,599,1), Vec3crd(600,601,338)
+ });
+ break;
+ case TestMesh::gt2_teeth:
+ vertices = std::vector<Vec3d>({
+ Vec3d(15.8899993896484,19.444055557251,2.67489433288574), Vec3d(15.9129991531372,19.1590557098389,2.67489433288574), Vec3d(15.9039993286133,19.1500549316406,2.67489433288574), Vec3d(15.9489994049072,19.2490558624268,2.67489433288574), Vec3d(15.9579992294312,19.3570556640625,2.67489433288574), Vec3d(15.8819999694824,18.690055847168,2.67489433288574), Vec3d(15.8319997787476,17.7460556030273,2.67489433288574), Vec3d(15.8489999771118,18.819055557251,2.67489433288574), Vec3d(15.8589992523193,17.7190551757812,2.67489433288574), Vec3d(15.8769998550415,19.0490550994873,2.67489433288574), Vec3d(15.7529993057251,17.8080558776855,2.67489433288574), Vec3d(15.7869997024536,19.5010547637939,2.67489433288574), Vec3d(14.0329990386963,18.7170543670654,2.67489433288574), Vec3d(13.9599990844727,18.7460556030273,2.67489433288574), Vec3d(13.9869995117188,20.2840557098389,2.67489433288574), Vec3d(14.2029991149902,20.149055480957,2.67489433288574), Vec3d(14.1939992904663,19.9560546875,2.67489433288574), Vec3d(14.1939992904663,20.1670551300049,2.67489433288574), Vec3d(14.2119998931885,20.0590553283691,2.67489433288574), Vec3d(12.1899995803833,19.1840553283691,2.67489433288574), Vec3d(12.096999168396,19.1950550079346,2.67489433288574), Vec3d(12.1099996566772,20.6690559387207,2.67489433288574), Vec3d(11.382999420166,19.9750556945801,2.67489433288574), Vec3d(11.2599992752075,19.2490558624268,2.67489433288574), Vec3d(11.2369995117188,19.9320545196533,2.67489433288574), Vec3d(11.5349998474121,20.0640544891357,2.67489433288574), Vec3d(11.6259994506836,20.1550559997559,2.67489433288574), Vec3d(11.6829986572266,20.2390556335449,2.67489433288574), Vec3d(11.7369995117188,20.3570556640625,2.67489433288574), Vec3d(11.8449993133545,20.645055770874,2.67489433288574), Vec3d(11.7729988098145,20.4640560150146,2.67489433288574), Vec3d(11.7799987792969,20.5370559692383,9.41389465332031), Vec3d(11.7639999389648,20.4470558166504,2.67489433288574), Vec3d(11.9559993743896,20.6810550689697,2.67489433288574), Vec3d(12.3079996109009,20.6020545959473,2.67489433288574), Vec3d(12.1959991455078,19.1860542297363,2.67489433288574), Vec3d(12.2059993743896,20.6540546417236,2.67489433288574), Vec3d(12.3489990234375,20.3740558624268,2.67489433288574), Vec3d(12.3579998016357,20.2750549316406,2.67489433288574), Vec3d(12.3669996261597,20.266056060791,2.67489433288574), Vec3d(12.3849992752075,20.1670551300049,2.67489433288574), Vec3d(12.4269990921021,20.0680541992188,2.67489433288574), Vec3d(12.5029993057251,19.9540557861328,2.67489433288574), Vec3d(12.6169996261597,19.8550548553467,2.67489433288574), Vec3d(12.7449989318848,19.7800559997559,2.67489433288574), Vec3d(12.7629995346069,19.7800559997559,2.67489433288574), Vec3d(12.8799991607666,19.7350559234619,2.67489433288574), Vec3d(13.0369997024536,19.7250556945801,2.67489433288574), Vec3d(13.0149993896484,19.0340557098389,2.67489433288574), Vec3d(11.1699991226196,19.2580547332764,2.67489433288574), Vec3d(11.0959987640381,19.2580547332764,2.67489433288574), Vec3d(11.1209993362427,19.9230556488037,2.67489433288574), Vec3d(13.0599994659424,19.024055480957,2.67489433288574), Vec3d(14.9049997329712,18.3170547485352,2.67489433288574), Vec3d(14.8779993057251,18.3400554656982,2.67489433288574), Vec3d(14.8779993057251,19.149055480957,2.67489433288574), Vec3d(13.3039989471436,19.77805519104,2.67489433288574), Vec3d(13.1589994430542,18.9890556335449,2.67489433288574), Vec3d(13.1559991836548,19.7350559234619,2.67489433288574), Vec3d(13.4269990921021,19.8600559234619,2.67489433288574), Vec3d(13.5339994430542,19.9700546264648,2.67389440536499), Vec3d(13.6359996795654,20.1220550537109,2.67489433288574), Vec3d(13.6359996795654,20.1400547027588,2.67489433288574), Vec3d(13.6719989776611,20.2210559844971,2.67489433288574), Vec3d(13.6899995803833,20.2300548553467,2.67489433288574), Vec3d(13.7509994506836,20.3010559082031,2.67489433288574), Vec3d(13.8539991378784,20.3180541992188,2.67489433288574), Vec3d(14.8329992294312,18.3580551147461,2.67489433288574), Vec3d(14.1849994659424,19.8530559539795,2.67489433288574), Vec3d(14.0769996643066,18.7000541687012,2.67489433288574), Vec3d(14.1099996566772,20.2400550842285,2.67489433288574), Vec3d(14.2009992599487,19.6230545043945,2.67489433288574), Vec3d(14.2729997634888,19.4670543670654,2.67489433288574), Vec3d(14.3379993438721,19.3790550231934,2.67489433288574), Vec3d(14.4549999237061,19.2770557403564,2.67489433288574), Vec3d(14.5899991989136,19.2040557861328,2.67489433288574), Vec3d(14.6079998016357,19.2040557861328,2.67489433288574), Vec3d(14.7209997177124,19.1600551605225,2.67489433288574), Vec3d(15.1379995346069,19.210054397583,2.67489433288574), Vec3d(14.9949998855591,18.2680549621582,2.67489433288574), Vec3d(15.0029993057251,19.1580543518066,2.67489433288574), Vec3d(15.2369995117188,19.2760543823242,2.67489433288574), Vec3d(15.3779993057251,19.4060554504395,2.67489433288574), Vec3d(15.4539995193481,19.520055770874,2.67489433288574), Vec3d(15.471999168396,19.52805519104,2.67489433288574), Vec3d(15.5449991226196,19.5830554962158,2.67489433288574), Vec3d(15.6529998779297,19.573055267334,2.67489433288574), Vec3d(15.7059993743896,17.8360557556152,2.67489433288574), Vec3d(15.9449996948242,18.5560550689697,2.67489433288574), Vec3d(15.8589992523193,18.9380550384521,2.67489433288574), Vec3d(14.9589996337891,18.2950553894043,2.67489433288574), Vec3d(15.7779998779297,19.5100555419922,2.67489433288574), Vec3d(14.0049991607666,20.2750549316406,2.67489433288574), Vec3d(12.3489990234375,20.5000553131104,2.67489433288574), Vec3d(13.0689992904663,19.0150547027588,2.67489433288574), Vec3d(13.0999994277954,19.0100555419922,2.67489433288574), Vec3d(15.9489994049072,19.3670558929443,9.41489505767822), Vec3d(15.9489994049072,19.2490558624268,9.41489505767822), Vec3d(15.75,17.8080558776855,9.41489505767822), Vec3d(15.6639995574951,19.5710544586182,9.41489505767822), Vec3d(15.5709991455078,17.9260559082031,9.41489505767822), Vec3d(15.8769998550415,18.690055847168,9.41489505767822), Vec3d(15.8499994277954,18.8170547485352,9.41489505767822), Vec3d(15.9459991455078,18.5520553588867,9.41489505767822), Vec3d(15.914999961853,17.6890544891357,9.41489505767822), Vec3d(15.3999996185303,19.4290542602539,9.41489505767822), Vec3d(15.3099994659424,19.339054107666,9.41489505767822), Vec3d(15.3729991912842,18.0440559387207,9.41489505767822), Vec3d(15.4579992294312,19.5170555114746,9.41489505767822), Vec3d(15.5469999313354,19.5820541381836,9.41489505767822), Vec3d(13.2309989929199,19.7610549926758,9.41489505767822), Vec3d(13.168999671936,19.7360553741455,9.41489505767822), Vec3d(13.096999168396,19.0140552520752,9.41489505767822), Vec3d(13.1999988555908,18.9870548248291,9.41489505767822), Vec3d(15.1399993896484,19.2080554962158,9.41489505767822), Vec3d(15.0159997940063,19.1600551605225,9.41489505767822), Vec3d(14.9859991073608,18.2770557403564,9.41489505767822), Vec3d(15.1749992370605,18.1690559387207,9.41489505767822), Vec3d(15.9039993286133,19.1320552825928,9.41489505767822), Vec3d(15.8949995040894,19.4460544586182,9.41489505767822), Vec3d(15.8769998550415,19.0420551300049,9.41489505767822), Vec3d(12.2169990539551,20.6500549316406,9.41489505767822), Vec3d(11.9379997253418,20.6810550689697,9.41489505767822), Vec3d(11.8629989624023,19.2130546569824,9.41489505767822), Vec3d(12.096999168396,19.1950550079346,9.41489505767822), Vec3d(14.1669998168945,18.6640548706055,9.41489505767822), Vec3d(14.1039991378784,20.2460556030273,9.41489505767822), Vec3d(13.9849996566772,18.7360553741455,9.41489505767822), Vec3d(14.7349996566772,19.1590557098389,9.41489505767822), Vec3d(14.5849990844727,19.2050552368164,9.41489505767822), Vec3d(14.5719995498657,18.4850559234619,9.41489505767822), Vec3d(14.1939992904663,19.6760559082031,9.41489505767822), Vec3d(14.1849994659424,19.9330558776855,9.41489505767822), Vec3d(14.1759996414185,18.6640548706055,9.41489505767822), Vec3d(14.261999130249,19.4890556335449,9.41489505767822), Vec3d(14.3539991378784,19.3610553741455,9.41489505767822), Vec3d(14.3559989929199,18.5830554962158,9.41489505767822), Vec3d(11.6039991378784,20.1250553131104,9.41489505767822), Vec3d(11.5209999084473,20.0520553588867,9.41489505767822), Vec3d(11.4209995269775,19.2480545043945,9.41489505767822), Vec3d(11.6989994049072,20.2690544128418,9.41389465332031), Vec3d(11.7609996795654,20.4310550689697,9.41489505767822), Vec3d(11.8359994888306,19.2130546569824,9.41489505767822), Vec3d(14.1889991760254,20.1710548400879,9.41489505767822), Vec3d(13.9689998626709,20.2840557098389,9.41489505767822), Vec3d(13.8739995956421,20.315055847168,9.41489505767822), Vec3d(13.7799997329712,18.8080558776855,9.41489505767822), Vec3d(13.9869995117188,20.2750549316406,9.41489505767822), Vec3d(12.3129997253418,20.5980548858643,9.41489505767822), Vec3d(12.3399991989136,20.5090560913086,9.41489505767822), Vec3d(12.3489990234375,20.3830547332764,9.41489505767822), Vec3d(12.3599996566772,20.2680549621582,9.41489505767822), Vec3d(12.3849992752075,20.1850547790527,9.41489505767822), Vec3d(12.3849992752075,20.1670551300049,9.41489505767822), Vec3d(12.4249992370605,20.065055847168,9.41489505767822), Vec3d(12.4729995727539,19.1350555419922,9.41489505767822), Vec3d(14.4399995803833,19.2900543212891,9.41489505767822), Vec3d(14.3649997711182,18.5740547180176,9.41489505767822), Vec3d(13.5729999542236,20.0310554504395,9.41489505767822), Vec3d(13.4889993667603,19.9140548706055,9.41489505767822), Vec3d(13.5639991760254,18.8710556030273,9.41489505767822), Vec3d(13.6389999389648,20.1310558319092,9.41489505767822), Vec3d(13.6719989776611,20.2130546569824,9.41489505767822), Vec3d(13.75,20.3020553588867,9.41489505767822), Vec3d(12.7399997711182,19.7810554504395,9.41489505767822), Vec3d(12.6189994812012,19.8520545959473,9.41489505767822), Vec3d(12.5799999237061,19.1200542449951,9.41489505767822), Vec3d(12.8349990844727,19.069055557251,9.41489505767822), Vec3d(11.2669992446899,19.9350547790527,9.41489505767822), Vec3d(11.1029987335205,19.9230556488037,9.41489505767822), Vec3d(11.0209999084473,19.2600555419922,9.41489505767822), Vec3d(11.3819999694824,19.9710559844971,9.41489505767822), Vec3d(13.418999671936,19.8530559539795,9.41489505767822), Vec3d(13.4329996109009,18.9160556793213,9.41489505767822), Vec3d(11.8399991989136,20.6430549621582,9.41489505767822), Vec3d(13.3119993209839,19.7800559997559,9.41489505767822), Vec3d(15.2189998626709,19.2600555419922,9.41489505767822), Vec3d(15.1839990615845,18.1600551605225,9.41489505767822), Vec3d(15.3639993667603,18.0520553588867,9.41489505767822), Vec3d(13.0189990997314,19.7250556945801,9.41489505767822), Vec3d(12.8949995040894,19.7350559234619,9.41489505767822), Vec3d(15.9039993286133,19.1500549316406,9.41489505767822), Vec3d(15.7699995040894,19.5140552520752,9.41489505767822), Vec3d(15.8589992523193,18.9340553283691,9.41489505767822), Vec3d(14.1939992904663,19.9510555267334,9.41489505767822), Vec3d(14.2119998931885,20.0630550384521,9.41489505767822), Vec3d(14.8589992523193,19.149055480957,9.41489505767822), Vec3d(14.8159999847412,18.3670558929443,9.41489505767822), Vec3d(14.8959999084473,18.3220558166504,9.41489505767822), Vec3d(12.5189990997314,19.9360542297363,9.41489505767822), Vec3d(11.0209999084473,19.9290542602539,9.41489505767822), Vec3d(11.0209999084473,19.2530555725098,2.67489433288574), Vec3d(11.0209999084473,19.9300556182861,2.67489433288574), Vec3d(15.9799995422363,18.505931854248,5.58724021911621), Vec3d(15.9799995422363,18.5044555664062,9.41489505767822), Vec3d(15.9799995422363,18.5041732788086,2.67489433288574), Vec3d(15.9799995422363,18.1684837341309,2.67489433288574), Vec3d(15.9799995422363,18.1288299560547,9.41489505767822), Vec3d(15.9799995422363,17.9876575469971,2.67489433288574), Vec3d(15.9799995422363,17.6247596740723,3.91620373725891), Vec3d(15.9799995422363,17.6247596740723,2.67489433288574), Vec3d(15.9799995422363,17.6254329681396,4.32245063781738), Vec3d(15.9799995422363,17.8920269012451,9.41489505767822), Vec3d(15.9799995422363,17.8795108795166,2.67489433288574), Vec3d(15.9799995422363,17.629810333252,4.58585262298584), Vec3d(15.9799995422363,17.6336059570312,5.27938556671143), Vec3d(15.9799995422363,17.8311748504639,2.67489433288574), Vec3d(15.9799995422363,17.638355255127,9.41489505767822), Vec3d(15.9799995422363,17.6346111297607,5.98653984069824), Vec3d(15.9799995422363,17.8728256225586,2.67489433288574), Vec3d(15.9799995422363,18.2221603393555,2.67489433288574)
+ });
+ facets = std::vector<Vec3crd>({
+ Vec3crd(0,1,2), Vec3crd(0,3,1), Vec3crd(0,4,3), Vec3crd(5,6,7), Vec3crd(8,6,5), Vec3crd(2,9,0), Vec3crd(6,10,11), Vec3crd(12,13,14), Vec3crd(15,16,17), Vec3crd(18,16,15), Vec3crd(19,20,21), Vec3crd(22,23,24), Vec3crd(25,23,22), Vec3crd(26,23,25), Vec3crd(27,23,26), Vec3crd(28,23,27), Vec3crd(29,30,31), Vec3crd(29,32,30), Vec3crd(29,28,32), Vec3crd(33,28,29), Vec3crd(33,23,28), Vec3crd(21,23,33), Vec3crd(20,23,21), Vec3crd(34,35,36), Vec3crd(37,35,34), Vec3crd(38,35,37), Vec3crd(39,35,38), Vec3crd(40,35,39), Vec3crd(41,35,40), Vec3crd(42,35,41), Vec3crd(43,35,42), Vec3crd(44,35,43), Vec3crd(45,35,44), Vec3crd(46,35,45), Vec3crd(47,35,46), Vec3crd(48,35,47), Vec3crd(49,50,51), Vec3crd(52,48,47), Vec3crd(23,49,24), Vec3crd(53,54,55), Vec3crd(56,57,58), Vec3crd(59,57,56), Vec3crd(60,57,59), Vec3crd(61,57,60), Vec3crd(62,57,61), Vec3crd(63,57,62), Vec3crd(64,57,63), Vec3crd(65,57,64), Vec3crd(66,57,65), Vec3crd(13,57,66), Vec3crd(54,67,55), Vec3crd(68,69,70), Vec3crd(71,69,68), Vec3crd(72,69,71), Vec3crd(73,69,72), Vec3crd(74,69,73), Vec3crd(75,69,74), Vec3crd(76,69,75), Vec3crd(77,69,76), Vec3crd(67,69,77), Vec3crd(70,16,68), Vec3crd(70,17,16), Vec3crd(78,79,80), Vec3crd(81,79,78), Vec3crd(82,79,81), Vec3crd(83,79,82), Vec3crd(84,79,83), Vec3crd(85,79,84), Vec3crd(86,79,85), Vec3crd(87,79,86), Vec3crd(88,8,5), Vec3crd(11,7,6), Vec3crd(11,89,7), Vec3crd(11,9,89), Vec3crd(11,0,9), Vec3crd(55,90,53), Vec3crd(55,79,90), Vec3crd(55,80,79), Vec3crd(91,11,10), Vec3crd(92,69,12), Vec3crd(92,70,69), Vec3crd(34,93,37), Vec3crd(47,94,52), Vec3crd(47,95,94), Vec3crd(47,57,95), Vec3crd(47,58,57), Vec3crd(51,24,49), Vec3crd(21,35,19), Vec3crd(21,36,35), Vec3crd(14,92,12), Vec3crd(86,10,87), Vec3crd(86,91,10), Vec3crd(77,55,67), Vec3crd(66,14,13), Vec3crd(96,97,4), Vec3crd(98,99,100), Vec3crd(101,102,98), Vec3crd(103,101,98), Vec3crd(104,103,98), Vec3crd(105,106,107), Vec3crd(108,105,107), Vec3crd(109,108,107), Vec3crd(100,109,107), Vec3crd(110,111,112), Vec3crd(113,110,112), Vec3crd(114,115,116), Vec3crd(117,114,116), Vec3crd(118,119,120), Vec3crd(121,122,123), Vec3crd(124,121,123), Vec3crd(125,126,127), Vec3crd(128,129,130), Vec3crd(131,132,133), Vec3crd(71,131,133), Vec3crd(134,71,133), Vec3crd(135,134,133), Vec3crd(136,135,133), Vec3crd(137,138,139), Vec3crd(140,137,139), Vec3crd(141,140,139), Vec3crd(142,31,141), Vec3crd(142,141,139), Vec3crd(143,126,132), Vec3crd(144,145,146), Vec3crd(147,144,146), Vec3crd(127,147,146), Vec3crd(148,121,124), Vec3crd(149,148,124), Vec3crd(150,149,124), Vec3crd(151,150,124), Vec3crd(152,151,124), Vec3crd(153,152,124), Vec3crd(154,153,124), Vec3crd(155,154,124), Vec3crd(129,156,157), Vec3crd(130,129,157), Vec3crd(158,159,160), Vec3crd(161,158,160), Vec3crd(162,161,160), Vec3crd(163,162,160), Vec3crd(146,163,160), Vec3crd(164,165,166), Vec3crd(167,164,166), Vec3crd(168,169,170), Vec3crd(171,168,170), Vec3crd(139,171,170), Vec3crd(159,172,173), Vec3crd(123,174,142), Vec3crd(175,110,113), Vec3crd(173,175,113), Vec3crd(106,176,177), Vec3crd(178,106,177), Vec3crd(179,180,167), Vec3crd(112,179,167), Vec3crd(175,173,172), Vec3crd(119,118,181), Vec3crd(119,181,97), Vec3crd(119,97,96), Vec3crd(182,98,102), Vec3crd(182,102,183), Vec3crd(182,183,120), Vec3crd(182,120,119), Vec3crd(143,132,184), Vec3crd(184,185,143), Vec3crd(147,127,126), Vec3crd(174,123,122), Vec3crd(159,173,160), Vec3crd(126,125,133), Vec3crd(126,133,132), Vec3crd(186,187,188), Vec3crd(186,188,116), Vec3crd(186,116,115), Vec3crd(99,98,182), Vec3crd(109,100,99), Vec3crd(106,178,107), Vec3crd(114,117,177), Vec3crd(114,177,176), Vec3crd(128,130,187), Vec3crd(128,187,186), Vec3crd(135,136,157), Vec3crd(135,157,156), Vec3crd(163,146,145), Vec3crd(164,167,180), Vec3crd(179,112,111), Vec3crd(171,139,138), Vec3crd(189,155,166), Vec3crd(189,166,165), Vec3crd(149,150,93), Vec3crd(154,155,189), Vec3crd(31,142,174), Vec3crd(114,176,78), Vec3crd(81,78,176), Vec3crd(7,89,183), Vec3crd(89,9,120), Vec3crd(89,120,183), Vec3crd(78,80,114), Vec3crd(176,106,81), Vec3crd(88,5,103), Vec3crd(183,102,7), Vec3crd(118,120,9), Vec3crd(9,2,181), Vec3crd(9,181,118), Vec3crd(115,114,80), Vec3crd(82,81,106), Vec3crd(101,103,5), Vec3crd(102,101,5), Vec3crd(5,7,102), Vec3crd(97,181,2), Vec3crd(2,1,97), Vec3crd(1,3,97), Vec3crd(80,55,115), Vec3crd(172,159,59), Vec3crd(59,56,172), Vec3crd(3,4,97), Vec3crd(4,0,96), Vec3crd(105,108,82), Vec3crd(186,115,55), Vec3crd(82,106,105), Vec3crd(83,82,108), Vec3crd(60,59,159), Vec3crd(175,172,56), Vec3crd(119,96,0), Vec3crd(0,11,119), Vec3crd(108,109,84), Vec3crd(84,83,108), Vec3crd(55,77,186), Vec3crd(56,58,110), Vec3crd(56,110,175), Vec3crd(60,159,158), Vec3crd(11,91,182), Vec3crd(182,119,11), Vec3crd(91,86,182), Vec3crd(85,84,109), Vec3crd(86,85,99), Vec3crd(128,186,77), Vec3crd(58,111,110), Vec3crd(158,161,60), Vec3crd(26,25,137), Vec3crd(138,137,25), Vec3crd(99,182,86), Vec3crd(109,99,85), Vec3crd(77,76,128), Vec3crd(58,47,111), Vec3crd(61,60,161), Vec3crd(137,140,26), Vec3crd(27,26,140), Vec3crd(25,22,138), Vec3crd(129,128,76), Vec3crd(76,75,129), Vec3crd(75,74,129), Vec3crd(74,73,156), Vec3crd(73,72,135), Vec3crd(68,16,184), Vec3crd(68,184,132), Vec3crd(16,18,185), Vec3crd(161,162,62), Vec3crd(62,61,161), Vec3crd(179,111,47), Vec3crd(171,138,22), Vec3crd(156,129,74), Vec3crd(135,156,73), Vec3crd(134,135,72), Vec3crd(72,71,134), Vec3crd(68,132,131), Vec3crd(185,184,16), Vec3crd(18,15,185), Vec3crd(63,62,162), Vec3crd(28,27,140), Vec3crd(22,24,171), Vec3crd(71,68,131), Vec3crd(15,17,143), Vec3crd(15,143,185), Vec3crd(17,70,143), Vec3crd(70,92,126), Vec3crd(162,163,64), Vec3crd(64,63,162), Vec3crd(180,179,47), Vec3crd(47,46,180), Vec3crd(140,141,28), Vec3crd(168,171,24), Vec3crd(126,143,70), Vec3crd(92,14,147), Vec3crd(147,126,92), Vec3crd(14,66,144), Vec3crd(14,144,147), Vec3crd(65,64,163), Vec3crd(66,65,145), Vec3crd(46,45,180), Vec3crd(32,28,141), Vec3crd(24,51,168), Vec3crd(145,144,66), Vec3crd(163,145,65), Vec3crd(164,180,45), Vec3crd(45,44,164), Vec3crd(44,43,164), Vec3crd(43,42,165), Vec3crd(38,37,151), Vec3crd(150,151,37), Vec3crd(37,93,150), Vec3crd(141,31,30), Vec3crd(30,32,141), Vec3crd(169,168,51), Vec3crd(165,164,43), Vec3crd(189,165,42), Vec3crd(42,41,189), Vec3crd(40,39,152), Vec3crd(40,152,153), Vec3crd(151,152,39), Vec3crd(39,38,151), Vec3crd(93,34,149), Vec3crd(154,189,41), Vec3crd(153,154,41), Vec3crd(41,40,153), Vec3crd(148,149,34), Vec3crd(34,36,148), Vec3crd(36,21,121), Vec3crd(31,174,29), Vec3crd(121,148,36), Vec3crd(21,33,122), Vec3crd(21,122,121), Vec3crd(33,29,122), Vec3crd(174,122,29), Vec3crd(116,188,53), Vec3crd(104,98,10), Vec3crd(87,10,98), Vec3crd(98,100,87), Vec3crd(79,87,100), Vec3crd(79,100,107), Vec3crd(90,79,107), Vec3crd(90,107,178), Vec3crd(178,177,90), Vec3crd(53,90,177), Vec3crd(53,177,117), Vec3crd(117,116,53), Vec3crd(54,53,188), Vec3crd(54,188,187), Vec3crd(67,54,187), Vec3crd(67,187,130), Vec3crd(69,67,130), Vec3crd(69,130,157), Vec3crd(12,69,157), Vec3crd(12,157,136), Vec3crd(136,133,12), Vec3crd(12,133,125), Vec3crd(125,127,12), Vec3crd(13,12,127), Vec3crd(127,146,13), Vec3crd(57,13,146), Vec3crd(57,146,160), Vec3crd(95,57,160), Vec3crd(95,160,173), Vec3crd(173,113,95), Vec3crd(94,95,113), Vec3crd(113,112,94), Vec3crd(52,94,112), Vec3crd(48,52,112), Vec3crd(112,167,48), Vec3crd(35,48,167), Vec3crd(35,167,166), Vec3crd(19,35,166), Vec3crd(139,170,50), Vec3crd(50,49,139), Vec3crd(166,155,19), Vec3crd(20,19,155), Vec3crd(155,124,20), Vec3crd(23,20,124), Vec3crd(23,124,123), Vec3crd(49,23,123), Vec3crd(49,123,142), Vec3crd(142,139,49), Vec3crd(190,191,170), Vec3crd(192,191,190), Vec3crd(191,192,51), Vec3crd(191,51,50), Vec3crd(170,169,190), Vec3crd(169,51,192), Vec3crd(169,192,190), Vec3crd(170,191,50), Vec3crd(193,194,195), Vec3crd(196,197,198), Vec3crd(199,200,201), Vec3crd(198,202,203), Vec3crd(204,201,200), Vec3crd(205,204,200), Vec3crd(206,207,208), Vec3crd(206,208,205), Vec3crd(206,205,200), Vec3crd(207,206,209), Vec3crd(207,209,203), Vec3crd(207,203,202), Vec3crd(202,198,197), Vec3crd(197,196,210), Vec3crd(197,210,195), Vec3crd(197,195,194), Vec3crd(8,88,195), Vec3crd(8,195,210), Vec3crd(210,196,8), Vec3crd(196,198,8), Vec3crd(198,203,8), Vec3crd(203,209,8), Vec3crd(209,206,8), Vec3crd(206,200,8), Vec3crd(202,197,104), Vec3crd(207,202,104), Vec3crd(103,104,197), Vec3crd(103,197,194), Vec3crd(193,195,88), Vec3crd(88,103,194), Vec3crd(88,194,193), Vec3crd(200,199,8), Vec3crd(199,201,8), Vec3crd(204,205,6), Vec3crd(6,8,201), Vec3crd(6,201,204), Vec3crd(10,6,205), Vec3crd(10,205,208), Vec3crd(104,10,208), Vec3crd(104,208,207)
+ });
+ break;
+ case TestMesh::pyramid:
+ vertices = std::vector<Vec3d>({
+ Vec3d(10,10,40), Vec3d(0,0,0), Vec3d(20,0,0), Vec3d(20,20,0), Vec3d(0,20,0)
+ });
+ facets = std::vector<Vec3crd>({
+ Vec3crd(0,1,2), Vec3crd(0,3,4), Vec3crd(3,1,4), Vec3crd(1,3,2), Vec3crd(3,0,2), Vec3crd(4,1,0)
+ });
+ break;
+ case TestMesh::two_hollow_squares:
+ vertices = std::vector<Vec3d>({
+ Vec3d(66.7133483886719,104.286666870117,0), Vec3d(66.7133483886719,95.7133331298828,0), Vec3d(65.6666870117188,94.6666717529297,0), Vec3d(75.2866821289062,95.7133331298828,0), Vec3d(76.3333435058594,105.333335876465,0), Vec3d(76.3333435058594,94.6666717529297,0), Vec3d(65.6666870117188,105.33332824707,0), Vec3d(75.2866821289062,104.286666870117,0), Vec3d(71.1066818237305,104.58666229248,2.79999995231628), Vec3d(66.4133529663086,104.58666229248,2.79999995231628), Vec3d(75.5866851806641,104.58666229248,2.79999995231628), Vec3d(66.4133529663086,99.8933334350586,2.79999995231628), Vec3d(66.4133529663086,95.4133377075195,2.79999995231628), Vec3d(71.1066818237305,95.4133377075195,2.79999995231628), Vec3d(75.5866851806641,95.4133377075195,2.79999995231628), Vec3d(75.5866851806641,100.106666564941,2.79999995231628), Vec3d(74.5400161743164,103.540000915527,2.79999995231628), Vec3d(70.0320129394531,103.540000915527,2.79999995231628), Vec3d(67.4600067138672,103.540000915527,2.79999995231628), Vec3d(67.4600067138672,100.968002319336,2.79999995231628), Vec3d(67.4600067138672,96.4599990844727,2.79999995231628), Vec3d(74.5400161743164,99.0319976806641,2.79999995231628), Vec3d(74.5400161743164,96.4599990844727,2.79999995231628), Vec3d(70.0320129394531,96.4599990844727,2.79999995231628), Vec3d(123.666717529297,94.6666717529297,0), Vec3d(134.333312988281,94.6666717529297,0), Vec3d(124.413360595703,95.4133377075195,2.79999995231628), Vec3d(129.106674194336,95.4133377075195,2.79999995231628), Vec3d(133.586669921875,95.4133377075195,2.79999995231628), Vec3d(123.666717529297,105.33332824707,0), Vec3d(124.413360595703,104.58666229248,2.79999995231628), Vec3d(124.413360595703,99.8933334350586,2.79999995231628), Vec3d(134.333312988281,105.33332824707,0), Vec3d(129.106674194336,104.58666229248,2.79999995231628), Vec3d(133.586669921875,104.58666229248,2.79999995231628), Vec3d(133.586669921875,100.106666564941,2.79999995231628), Vec3d(124.713317871094,104.286666870117,0), Vec3d(124.713317871094,95.7133331298828,0), Vec3d(133.286712646484,95.7133331298828,0), Vec3d(133.286712646484,104.286666870117,0), Vec3d(132.540023803711,103.540000915527,2.79999995231628), Vec3d(128.032028198242,103.540008544922,2.79999995231628), Vec3d(125.460006713867,103.540000915527,2.79999995231628), Vec3d(125.460006713867,100.968002319336,2.79999995231628), Vec3d(125.460006713867,96.4599990844727,2.79999995231628), Vec3d(132.540023803711,99.0319976806641,2.79999995231628), Vec3d(132.540023803711,96.4599990844727,2.79999995231628), Vec3d(128.032028198242,96.4599990844727,2.79999995231628)
+ });
+ facets = std::vector<Vec3crd>({
+ Vec3crd(0,1,2), Vec3crd(3,4,5), Vec3crd(6,4,0), Vec3crd(6,0,2), Vec3crd(2,1,5), Vec3crd(7,4,3), Vec3crd(1,3,5), Vec3crd(0,4,7), Vec3crd(4,6,8), Vec3crd(6,9,8), Vec3crd(4,8,10), Vec3crd(6,2,9), Vec3crd(2,11,9), Vec3crd(2,12,11), Vec3crd(2,5,12), Vec3crd(5,13,12), Vec3crd(5,14,13), Vec3crd(4,10,15), Vec3crd(5,4,14), Vec3crd(4,15,14), Vec3crd(7,16,17), Vec3crd(0,7,18), Vec3crd(7,17,18), Vec3crd(1,19,20), Vec3crd(1,0,19), Vec3crd(0,18,19), Vec3crd(7,3,21), Vec3crd(3,22,21), Vec3crd(7,21,16), Vec3crd(3,23,22), Vec3crd(3,1,23), Vec3crd(1,20,23), Vec3crd(24,25,26), Vec3crd(25,27,26), Vec3crd(25,28,27), Vec3crd(29,24,30), Vec3crd(24,31,30), Vec3crd(24,26,31), Vec3crd(32,29,33), Vec3crd(29,30,33), Vec3crd(32,33,34), Vec3crd(32,34,35), Vec3crd(25,32,28), Vec3crd(32,35,28), Vec3crd(36,37,24), Vec3crd(38,32,25), Vec3crd(29,32,36), Vec3crd(29,36,24), Vec3crd(24,37,25), Vec3crd(39,32,38), Vec3crd(37,38,25), Vec3crd(36,32,39), Vec3crd(39,40,41), Vec3crd(36,39,42), Vec3crd(39,41,42), Vec3crd(37,43,44), Vec3crd(37,36,43), Vec3crd(36,42,43), Vec3crd(39,38,45), Vec3crd(38,46,45), Vec3crd(39,45,40), Vec3crd(38,47,46), Vec3crd(38,37,47), Vec3crd(37,44,47), Vec3crd(16,8,9), Vec3crd(16,10,8), Vec3crd(10,16,15), Vec3crd(15,16,21), Vec3crd(22,15,21), Vec3crd(15,22,14), Vec3crd(22,23,14), Vec3crd(23,20,14), Vec3crd(17,16,9), Vec3crd(18,17,9), Vec3crd(19,18,9), Vec3crd(19,9,11), Vec3crd(19,11,20), Vec3crd(13,14,20), Vec3crd(20,11,12), Vec3crd(13,20,12), Vec3crd(41,40,30), Vec3crd(42,41,30), Vec3crd(43,42,30), Vec3crd(43,30,31), Vec3crd(43,31,44), Vec3crd(27,28,44), Vec3crd(44,31,26), Vec3crd(27,44,26), Vec3crd(40,33,30), Vec3crd(40,34,33), Vec3crd(34,40,35), Vec3crd(35,40,45), Vec3crd(46,35,45), Vec3crd(35,46,28), Vec3crd(46,47,28), Vec3crd(47,44,28) ,
+ });
+ break;
+ case TestMesh::small_dorito:
+ vertices = std::vector<Vec3d>({
+ Vec3d(6.00058937072754,-22.9982089996338,0), Vec3d(22.0010242462158,-49.9998741149902,0), Vec3d(-9.99957847595215,-49.999870300293,0), Vec3d(6.00071382522583,-32.2371635437012,28.0019245147705), Vec3d(11.1670551300049,-37.9727020263672,18.9601669311523), Vec3d(6.00060224533081,-26.5392456054688,10.7321853637695)
+ });
+ facets = std::vector<Vec3crd>({
+ Vec3crd(0,1,2), Vec3crd(3,4,5), Vec3crd(2,1,4), Vec3crd(2,4,3), Vec3crd(2,3,5), Vec3crd(2,5,0), Vec3crd(5,4,1), Vec3crd(5,1,0)
+ });
+ break;
+ case TestMesh::bridge:
+ vertices = std::vector<Vec3d>({
+ Vec3d(75,84.5,8), Vec3d(125,84.5,8), Vec3d(75,94.5,8), Vec3d(120,84.5,5), Vec3d(125,94.5,8), Vec3d(75,84.5,0), Vec3d(80,84.5,5), Vec3d(125,84.5,0), Vec3d(125,94.5,0), Vec3d(80,94.5,5), Vec3d(75,94.5,0), Vec3d(120,94.5,5), Vec3d(120,84.5,0), Vec3d(80,94.5,0), Vec3d(80,84.5,0), Vec3d(120,94.5,0)
+ });
+ facets = std::vector<Vec3crd>({
+ Vec3crd(0,1,2), Vec3crd(1,0,3), Vec3crd(2,1,4), Vec3crd(2,5,0), Vec3crd(0,6,3), Vec3crd(1,3,7), Vec3crd(1,8,4), Vec3crd(4,9,2), Vec3crd(10,5,2), Vec3crd(5,6,0), Vec3crd(6,11,3), Vec3crd(3,12,7), Vec3crd(7,8,1), Vec3crd(4,8,11), Vec3crd(4,11,9), Vec3crd(9,10,2), Vec3crd(10,13,5), Vec3crd(14,6,5), Vec3crd(9,11,6), Vec3crd(11,12,3), Vec3crd(12,8,7), Vec3crd(11,8,15), Vec3crd(13,10,9), Vec3crd(5,13,14), Vec3crd(14,13,6), Vec3crd(6,13,9), Vec3crd(15,12,11), Vec3crd(15,8,12)
+ });
+ break;
+ case TestMesh::bridge_with_hole:
+ vertices = std::vector<Vec3d>({
+ Vec3d(75,69.5,8), Vec3d(80,76.9091644287109,8), Vec3d(75,94.5,8), Vec3d(125,69.5,8), Vec3d(120,76.9091644287109,8), Vec3d(120,87.0908355712891,8), Vec3d(80,87.0908355712891,8), Vec3d(125,94.5,8), Vec3d(80,87.0908355712891,5), Vec3d(120,87.0908355712891,5), Vec3d(125,94.5,0), Vec3d(120,69.5,0), Vec3d(120,94.5,0), Vec3d(125,69.5,0), Vec3d(120,94.5,5), Vec3d(80,94.5,5), Vec3d(80,94.5,0), Vec3d(75,94.5,0), Vec3d(80,69.5,5), Vec3d(80,69.5,0), Vec3d(80,76.9091644287109,5), Vec3d(120,69.5,5), Vec3d(75,69.5,0), Vec3d(120,76.9091644287109,5)
+ });
+ facets = std::vector<Vec3crd>({
+ Vec3crd(0,1,2), Vec3crd(1,0,3), Vec3crd(1,3,4), Vec3crd(4,3,5), Vec3crd(2,6,7), Vec3crd(6,2,1), Vec3crd(7,6,5), Vec3crd(7,5,3), Vec3crd(5,8,9), Vec3crd(8,5,6), Vec3crd(10,11,12), Vec3crd(11,10,13), Vec3crd(14,8,15), Vec3crd(8,14,9), Vec3crd(2,16,17), Vec3crd(16,2,15), Vec3crd(15,2,14), Vec3crd(14,10,12), Vec3crd(10,14,7), Vec3crd(7,14,2), Vec3crd(16,18,19), Vec3crd(18,16,20), Vec3crd(20,16,1), Vec3crd(1,16,8), Vec3crd(8,16,15), Vec3crd(6,1,8), Vec3crd(3,11,13), Vec3crd(11,3,21), Vec3crd(21,3,18), Vec3crd(18,22,19), Vec3crd(22,18,0), Vec3crd(0,18,3), Vec3crd(16,22,17), Vec3crd(22,16,19), Vec3crd(2,22,0), Vec3crd(22,2,17), Vec3crd(5,23,4), Vec3crd(23,11,21), Vec3crd(11,23,12), Vec3crd(12,23,9), Vec3crd(9,23,5), Vec3crd(12,9,14), Vec3crd(23,18,20), Vec3crd(18,23,21), Vec3crd(10,3,13), Vec3crd(3,10,7), Vec3crd(1,23,20), Vec3crd(23,1,4)
+ });
+ break;
+ case TestMesh::step:
+ vertices = std::vector<Vec3d>({
+ Vec3d(0,20,5), Vec3d(0,20,0), Vec3d(0,0,5), Vec3d(0,0,0), Vec3d(20,0,0), Vec3d(20,0,5), Vec3d(1,19,5), Vec3d(1,1,5), Vec3d(19,1,5), Vec3d(20,20,5), Vec3d(19,19,5), Vec3d(20,20,0), Vec3d(19,19,10), Vec3d(1,19,10), Vec3d(1,1,10), Vec3d(19,1,10)
+ });
+ facets = std::vector<Vec3crd>({
+ Vec3crd(0,1,2), Vec3crd(1,3,2), Vec3crd(3,4,5), Vec3crd(2,3,5), Vec3crd(6,0,2), Vec3crd(6,2,7), Vec3crd(5,8,7), Vec3crd(5,7,2), Vec3crd(9,10,8), Vec3crd(9,8,5), Vec3crd(9,0,6), Vec3crd(9,6,10), Vec3crd(9,11,1), Vec3crd(9,1,0), Vec3crd(3,1,11), Vec3crd(4,3,11), Vec3crd(5,11,9), Vec3crd(5,4,11), Vec3crd(12,10,6), Vec3crd(12,6,13), Vec3crd(6,7,14), Vec3crd(13,6,14), Vec3crd(7,8,15), Vec3crd(14,7,15), Vec3crd(15,8,10), Vec3crd(15,10,12), Vec3crd(12,13,14), Vec3crd(12,14,15)
+ });
+ break;
+ case TestMesh::slopy_cube:
+ vertices = std::vector<Vec3d>({
+ Vec3d(-10,-10,0) , Vec3d(-10,-10,20) , Vec3d(-10,10,0) , Vec3d(-10,10,20) , Vec3d(0,-10,10) , Vec3d(10,-10,0) , Vec3d(2.92893,-10,10) , Vec3d(10,-10,2.92893) ,
+ Vec3d(0,-10,20) , Vec3d(10,10,0) , Vec3d(0,10,10) , Vec3d(0,10,20) , Vec3d(2.92893,10,10) , Vec3d(10,10,2.92893)
+ });
+ facets = std::vector<Vec3crd>({
+ Vec3crd(0,1,2) , Vec3crd(2,1,3) , Vec3crd(4,0,5) , Vec3crd(4,1,0) , Vec3crd(6,4,7) , Vec3crd(7,4,5) , Vec3crd(4,8,1) , Vec3crd(0,2,5) , Vec3crd(5,2,9) , Vec3crd(2,10,9) , Vec3crd(10,3,11) ,
+ Vec3crd(2,3,10) , Vec3crd(9,10,12) , Vec3crd(13,9,12) , Vec3crd(3,1,8) , Vec3crd(11,3,8) , Vec3crd(10,11,8) , Vec3crd(4,10,8) , Vec3crd(6,12,10) , Vec3crd(4,6,10) ,
+ Vec3crd(7,13,12) , Vec3crd(6,7,12) , Vec3crd(7,5,9) , Vec3crd(13,7,9)
+ });
+ break;
+ default:
+ break;
+ }
+ TriangleMesh _mesh;
+ if (vertices.size() == 0) {
+ switch(m) {
+ case TestMesh::cube_20x20x20:
+ _mesh = Slic3r::make_cube(20,20,20);
+ break;
+ case TestMesh::sphere_50mm:
+ _mesh = Slic3r::make_sphere(50, PI / 243.0);
+ break;
+ default:
+ break;
+ }
+ } else {
+ _mesh = TriangleMesh(vertices, facets);
+ }
+
+ _mesh.repair();
+ return _mesh;
+}
+
+static bool verbose_gcode()
+{
+ const char *v = std::getenv("SLIC3R_TESTS_GCODE");
+ if (v == nullptr)
+ return false;
+ std::string s(v);
+ return s == "1" || s == "on" || s == "yes";
+}
+
+std::shared_ptr<Print> init_print(std::initializer_list<TestMesh> meshes, Slic3r::Model& model, const Slic3r::DynamicPrintConfig &config_in, bool comments)
+{
+ DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
+ config.apply(config_in);
+
+ if (verbose_gcode())
+ config.set_key_value("gcode_comments", new ConfigOptionBool(true));
+
+ std::shared_ptr<Print> print(std::make_shared<Slic3r::Print>());
+
+ for (const TestMesh &t : meshes) {
+ ModelObject *object = model.add_object();
+ object->name += std::string(mesh_names.at(t)) + ".stl";
+ object->add_volume(mesh(t));
+ object->add_instance();
+ }
+
+ model.arrange_objects(PrintConfig::min_object_distance(&config));
+ model.center_instances_around_point(Slic3r::Vec2d(100,100));
+ for (ModelObject *mo : model.objects) {
+ mo->ensure_on_bed();
+ print->auto_assign_extruders(mo);
+ }
+
+ print->apply(model, config);
+ print->validate();
+ print->set_status_silent();
+ return print;
+}
+
+std::shared_ptr<Print> init_print(std::initializer_list<TriangleMesh> meshes, Slic3r::Model& model, const DynamicPrintConfig &config_in, bool comments)
+{
+ DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
+ config.apply(config_in);
+
+ if (verbose_gcode())
+ config.set_key_value("gcode_comments", new ConfigOptionBool(true));
+
+ std::shared_ptr<Print> print(std::make_shared<Slic3r::Print>());
+
+ for (const TriangleMesh &t : meshes) {
+ ModelObject *object = model.add_object();
+ object->name += "object.stl";
+ object->add_volume(t);
+ object->add_instance();
+ }
+ model.arrange_objects(PrintConfig::min_object_distance(&config));
+ model.center_instances_around_point(Slic3r::Vec2d(100, 100));
+ for (ModelObject *mo : model.objects) {
+ mo->ensure_on_bed();
+ print->auto_assign_extruders(mo);
+ }
+
+ print->apply(model, config);
+ print->validate();
+ print->set_status_silent();
+ return print;
+}
+
+std::string gcode(std::shared_ptr<Print> _print)
+{
+ boost::filesystem::path temp = boost::filesystem::unique_path();
+ _print->set_status_silent();
+ _print->process();
+ _print->export_gcode(temp.string(), nullptr);
+ std::ifstream t(temp.string());
+ std::string str((std::istreambuf_iterator<char>(t)), std::istreambuf_iterator<char>());
+ boost::nowide::remove(temp.string().c_str());
+ return str;
+}
+
+Slic3r::Model model(const std::string& model_name, TriangleMesh&& _mesh)
+{
+ Slic3r::Model result;
+ ModelObject *object = result.add_object();
+ object->name += model_name + ".stl";
+ object->add_volume(_mesh);
+ object->add_instance();
+ return result;
+}
+
+void add_testmesh_to_model(Slic3r::Model& result, const std::string& model_name, TriangleMesh&& _mesh)
+{
+ ModelObject *object = result.add_object();
+ object->name += model_name + ".stl";
+ object->add_volume(_mesh);
+ object->add_instance();
+}
+
+} } // namespace Slic3r::Test
+
+#include <catch2/catch.hpp>
+
+SCENARIO("init_print functionality") {
+ GIVEN("A default config") {
+ Slic3r::DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
+ std::stringstream gcode;
+ WHEN("init_print is called with a single mesh.") {
+ Slic3r::Model model;
+ auto print = Slic3r::Test::init_print({ Slic3r::Test::TestMesh::cube_20x20x20 }, model, config, true);
+ gcode.clear();
+ THEN("One mesh/printobject is in the resulting Print object.") {
+ REQUIRE(print->objects().size() == 1);
+ }
+ THEN("print->process() doesn't crash.") {
+ REQUIRE_NOTHROW(print->process());
+ }
+ THEN("Export gcode functions outputs text.") {
+ REQUIRE(!Slic3r::Test::gcode(print).empty());
+ }
+ }
+ }
+}
diff --git a/tests/fff_print/test_data.hpp b/tests/fff_print/test_data.hpp
new file mode 100644
index 000000000..db102e8f4
--- /dev/null
+++ b/tests/fff_print/test_data.hpp
@@ -0,0 +1,77 @@
+#ifndef SLIC3R_TEST_DATA_HPP
+#define SLIC3R_TEST_DATA_HPP
+
+#include "libslic3r/Point.hpp"
+#include "libslic3r/TriangleMesh.hpp"
+#include "libslic3r/Geometry.hpp"
+#include "libslic3r/Model.hpp"
+#include "libslic3r/Print.hpp"
+#include "libslic3r/Config.hpp"
+
+#include <unordered_map>
+
+namespace Slic3r { namespace Test {
+
+constexpr double MM_PER_MIN = 60.0;
+
+/// Enumeration of test meshes
+enum class TestMesh {
+ A,
+ L,
+ V,
+ _40x10,
+ cube_20x20x20,
+ sphere_50mm,
+ bridge,
+ bridge_with_hole,
+ cube_with_concave_hole,
+ cube_with_hole,
+ gt2_teeth,
+ ipadstand,
+ overhang,
+ pyramid,
+ sloping_hole,
+ slopy_cube,
+ small_dorito,
+ step,
+ two_hollow_squares
+};
+
+// Neccessary for <c++17
+struct TestMeshHash {
+ std::size_t operator()(TestMesh tm) const {
+ return static_cast<std::size_t>(tm);
+ }
+};
+
+/// Mesh enumeration to name mapping
+extern const std::unordered_map<TestMesh, const char*, TestMeshHash> mesh_names;
+
+/// Port of Slic3r::Test::mesh
+/// Basic cubes/boxes should call TriangleMesh::make_cube() directly and rescale/translate it
+TriangleMesh mesh(TestMesh m);
+
+TriangleMesh mesh(TestMesh m, Vec3d translate, Vec3d scale = Vec3d(1.0, 1.0, 1.0));
+TriangleMesh mesh(TestMesh m, Vec3d translate, double scale = 1.0);
+
+/// Templated function to see if two values are equivalent (+/- epsilon)
+template <typename T>
+bool _equiv(const T& a, const T& b) { return std::abs(a - b) < EPSILON; }
+
+template <typename T>
+bool _equiv(const T& a, const T& b, double epsilon) { return abs(a - b) < epsilon; }
+
+//Slic3r::Model model(const std::string& model_name, TestMesh m, Vec3d translate = Vec3d(0,0,0), Vec3d scale = Vec3d(1.0,1.0,1.0));
+//Slic3r::Model model(const std::string& model_name, TestMesh m, Vec3d translate = Vec3d(0,0,0), double scale = 1.0);
+
+Slic3r::Model model(const std::string& model_name, TriangleMesh&& _mesh);
+
+std::shared_ptr<Print> init_print(std::initializer_list<TestMesh> meshes, Slic3r::Model& model, const Slic3r::DynamicPrintConfig &config_in = Slic3r::DynamicPrintConfig::full_print_config(), bool comments = false);
+std::shared_ptr<Print> init_print(std::initializer_list<TriangleMesh> meshes, Slic3r::Model& model, const Slic3r::DynamicPrintConfig &config_in = Slic3r::DynamicPrintConfig::full_print_config(), bool comments = false);
+
+std::string gcode(std::shared_ptr<Print> print);
+
+} } // namespace Slic3r::Test
+
+
+#endif // SLIC3R_TEST_DATA_HPP
diff --git a/tests/fff_print/test_fill.cpp b/tests/fff_print/test_fill.cpp
new file mode 100644
index 000000000..ec8b44a0d
--- /dev/null
+++ b/tests/fff_print/test_fill.cpp
@@ -0,0 +1,474 @@
+#include <catch2/catch.hpp>
+
+#include <numeric>
+#include <sstream>
+
+#include "libslic3r/ClipperUtils.hpp"
+#include "libslic3r/Fill/Fill.hpp"
+#include "libslic3r/Flow.hpp"
+#include "libslic3r/Geometry.hpp"
+#include "libslic3r/Print.hpp"
+#include "libslic3r/SVG.hpp"
+#include "libslic3r/libslic3r.h"
+
+#include "test_data.hpp"
+
+using namespace Slic3r;
+
+bool test_if_solid_surface_filled(const ExPolygon& expolygon, double flow_spacing, double angle = 0, double density = 1.0);
+
+#if 0
+TEST_CASE("Fill: adjusted solid distance") {
+ int surface_width = 250;
+ int distance = Slic3r::Flow::solid_spacing(surface_width, 47);
+ REQUIRE(distance == Approx(50));
+ REQUIRE(surface_width % distance == 0);
+}
+#endif
+
+TEST_CASE("Fill: Pattern Path Length", "[Fill]") {
+ std::unique_ptr<Slic3r::Fill> filler(Slic3r::Fill::new_from_type("rectilinear"));
+ filler->angle = float(-(PI)/2.0);
+ FillParams fill_params;
+ filler->spacing = 5;
+ fill_params.dont_adjust = true;
+ //fill_params.endpoints_overlap = false;
+ fill_params.density = float(filler->spacing / 50.0);
+
+ auto test = [&filler, &fill_params] (const ExPolygon& poly) -> Slic3r::Polylines {
+ Slic3r::Surface surface(stTop, poly);
+ return filler->fill_surface(&surface, fill_params);
+ };
+
+ SECTION("Square") {
+ Slic3r::Points test_set;
+ test_set.reserve(4);
+ std::vector<Vec2d> points {Vec2d(0,0), Vec2d(100,0), Vec2d(100,100), Vec2d(0,100)};
+ for (size_t i = 0; i < 4; ++i) {
+ std::transform(points.cbegin()+i, points.cend(), std::back_inserter(test_set), [] (const Vec2d& a) -> Point { return Point::new_scale(a.x(), a.y()); } );
+ std::transform(points.cbegin(), points.cbegin()+i, std::back_inserter(test_set), [] (const Vec2d& a) -> Point { return Point::new_scale(a.x(), a.y()); } );
+ Slic3r::Polylines paths = test(Slic3r::ExPolygon(test_set));
+ REQUIRE(paths.size() == 1); // one continuous path
+
+ // TODO: determine what the "Expected length" should be for rectilinear fill of a 100x100 polygon.
+ // This check only checks that it's above scale(3*100 + 2*50) + scaled_epsilon.
+ // ok abs($paths->[0]->length - scale(3*100 + 2*50)) - scaled_epsilon, 'path has expected length';
+ REQUIRE(std::abs(paths[0].length() - static_cast<double>(scale_(3*100 + 2*50))) - SCALED_EPSILON > 0); // path has expected length
+
+ test_set.clear();
+ }
+ }
+ SECTION("Diamond with endpoints on grid") {
+ std::vector<Vec2d> points {Vec2d(0,0), Vec2d(100,0), Vec2d(150,50), Vec2d(100,100), Vec2d(0,100), Vec2d(-50,50)};
+ Slic3r::Points test_set;
+ test_set.reserve(6);
+ std::transform(points.cbegin(), points.cend(), std::back_inserter(test_set), [] (const Vec2d& a) -> Point { return Point::new_scale(a.x(), a.y()); } );
+ Slic3r::Polylines paths = test(Slic3r::ExPolygon(test_set));
+ REQUIRE(paths.size() == 1); // one continuous path
+ }
+
+ SECTION("Square with hole") {
+ std::vector<Vec2d> square {Vec2d(0,0), Vec2d(100,0), Vec2d(100,100), Vec2d(0,100)};
+ std::vector<Vec2d> hole {Vec2d(25,25), Vec2d(75,25), Vec2d(75,75), Vec2d(25,75) };
+ std::reverse(hole.begin(), hole.end());
+
+ Slic3r::Points test_hole;
+ Slic3r::Points test_square;
+
+ std::transform(square.cbegin(), square.cend(), std::back_inserter(test_square), [] (const Vec2d& a) -> Point { return Point::new_scale(a.x(), a.y()); } );
+ std::transform(hole.cbegin(), hole.cend(), std::back_inserter(test_hole), [] (const Vec2d& a) -> Point { return Point::new_scale(a.x(), a.y()); } );
+
+ for (double angle : {-(PI/2.0), -(PI/4.0), -(PI), PI/2.0, PI}) {
+ for (double spacing : {25.0, 5.0, 7.5, 8.5}) {
+ fill_params.density = float(filler->spacing / spacing);
+ filler->angle = float(angle);
+ ExPolygon e(test_square, test_hole);
+ Slic3r::Polylines paths = test(e);
+#if 0
+ {
+ BoundingBox bbox = get_extents(e);
+ SVG svg("c:\\data\\temp\\square_with_holes.svg", bbox);
+ svg.draw(e);
+ svg.draw(paths);
+ svg.Close();
+ }
+#endif
+ REQUIRE((paths.size() >= 1 && paths.size() <= 3));
+ // paths don't cross hole
+ REQUIRE(diff_pl(paths, offset(e, float(SCALED_EPSILON*10))).size() == 0);
+ }
+ }
+ }
+ SECTION("Regression: Missing infill segments in some rare circumstances") {
+ filler->angle = float(PI/4.0);
+ fill_params.dont_adjust = false;
+ filler->spacing = 0.654498;
+ //filler->endpoints_overlap = unscale(359974);
+ fill_params.density = 1;
+ filler->layer_id = 66;
+ filler->z = 20.15;
+
+ Slic3r::Points points {Point(25771516,14142125),Point(14142138,25771515),Point(2512749,14142131),Point(14142125,2512749)};
+ Slic3r::Polylines paths = test(Slic3r::ExPolygon(points));
+ REQUIRE(paths.size() == 1); // one continuous path
+
+ // TODO: determine what the "Expected length" should be for rectilinear fill of a 100x100 polygon.
+ // This check only checks that it's above scale(3*100 + 2*50) + scaled_epsilon.
+ // ok abs($paths->[0]->length - scale(3*100 + 2*50)) - scaled_epsilon, 'path has expected length';
+ REQUIRE(std::abs(paths[0].length() - static_cast<double>(scale_(3*100 + 2*50))) - SCALED_EPSILON > 0); // path has expected length
+ }
+
+ SECTION("Rotated Square") {
+ Slic3r::Points square { Point::new_scale(0,0), Point::new_scale(50,0), Point::new_scale(50,50), Point::new_scale(0,50)};
+ Slic3r::ExPolygon expolygon(square);
+ std::unique_ptr<Slic3r::Fill> filler(Slic3r::Fill::new_from_type("rectilinear"));
+ filler->bounding_box = get_extents(expolygon.contour);
+ filler->angle = 0;
+
+ Surface surface(stTop, expolygon);
+ auto flow = Slic3r::Flow(0.69, 0.4, 0.50);
+
+ FillParams fill_params;
+ fill_params.density = 1.0;
+ filler->spacing = flow.spacing();
+
+ for (auto angle : { 0.0, 45.0}) {
+ surface.expolygon.rotate(angle, Point(0,0));
+ Polylines paths = filler->fill_surface(&surface, fill_params);
+ REQUIRE(paths.size() == 1);
+ }
+ }
+ SECTION("Solid surface fill") {
+ Slic3r::Points points {
+ Point::new_scale(6883102, 9598327.01296997),
+ Point::new_scale(6883102, 20327272.01297),
+ Point::new_scale(3116896, 20327272.01297),
+ Point::new_scale(3116896, 9598327.01296997)
+ };
+ Slic3r::ExPolygon expolygon(points);
+
+ REQUIRE(test_if_solid_surface_filled(expolygon, 0.55) == true);
+ for (size_t i = 0; i <= 20; ++i)
+ {
+ expolygon.scale(1.05);
+ REQUIRE(test_if_solid_surface_filled(expolygon, 0.55) == true);
+ }
+ }
+ SECTION("Solid surface fill") {
+ Slic3r::Points points {
+ Slic3r::Point(59515297,5422499),Slic3r::Point(59531249,5578697),Slic3r::Point(59695801,6123186),
+ Slic3r::Point(59965713,6630228),Slic3r::Point(60328214,7070685),Slic3r::Point(60773285,7434379),
+ Slic3r::Point(61274561,7702115),Slic3r::Point(61819378,7866770),Slic3r::Point(62390306,7924789),
+ Slic3r::Point(62958700,7866744),Slic3r::Point(63503012,7702244),Slic3r::Point(64007365,7434357),
+ Slic3r::Point(64449960,7070398),Slic3r::Point(64809327,6634999),Slic3r::Point(65082143,6123325),
+ Slic3r::Point(65245005,5584454),Slic3r::Point(65266967,5422499),Slic3r::Point(66267307,5422499),
+ Slic3r::Point(66269190,8310081),Slic3r::Point(66275379,17810072),Slic3r::Point(66277259,20697500),
+ Slic3r::Point(65267237,20697500),Slic3r::Point(65245004,20533538),Slic3r::Point(65082082,19994444),
+ Slic3r::Point(64811462,19488579),Slic3r::Point(64450624,19048208),Slic3r::Point(64012101,18686514),
+ Slic3r::Point(63503122,18415781),Slic3r::Point(62959151,18251378),Slic3r::Point(62453416,18198442),
+ Slic3r::Point(62390147,18197355),Slic3r::Point(62200087,18200576),Slic3r::Point(61813519,18252990),
+ Slic3r::Point(61274433,18415918),Slic3r::Point(60768598,18686517),Slic3r::Point(60327567,19047892),
+ Slic3r::Point(59963609,19493297),Slic3r::Point(59695865,19994587),Slic3r::Point(59531222,20539379),
+ Slic3r::Point(59515153,20697500),Slic3r::Point(58502480,20697500),Slic3r::Point(58502480,5422499)
+ };
+ Slic3r::ExPolygon expolygon(points);
+
+ REQUIRE(test_if_solid_surface_filled(expolygon, 0.55) == true);
+ REQUIRE(test_if_solid_surface_filled(expolygon, 0.55, PI/2.0) == true);
+ }
+ SECTION("Solid surface fill") {
+ Slic3r::Points points {
+ Point::new_scale(0,0),Point::new_scale(98,0),Point::new_scale(98,10), Point::new_scale(0,10)
+ };
+ Slic3r::ExPolygon expolygon(points);
+
+ REQUIRE(test_if_solid_surface_filled(expolygon, 0.5, 45.0, 0.99) == true);
+ }
+}
+
+/*
+{
+ my $collection = Slic3r::Polyline::Collection->new(
+ Slic3r::Polyline->new([0,15], [0,18], [0,20]),
+ Slic3r::Polyline->new([0,10], [0,8], [0,5]),
+ );
+ is_deeply
+ [ map $_->[Y], map @$_, @{$collection->chained_path_from(Slic3r::Point->new(0,30), 0)} ],
+ [20, 18, 15, 10, 8, 5],
+ 'chained path';
+}
+
+{
+ my $collection = Slic3r::Polyline::Collection->new(
+ Slic3r::Polyline->new([4,0], [10,0], [15,0]),
+ Slic3r::Polyline->new([10,5], [15,5], [20,5]),
+ );
+ is_deeply
+ [ map $_->[X], map @$_, @{$collection->chained_path_from(Slic3r::Point->new(30,0), 0)} ],
+ [reverse 4, 10, 15, 10, 15, 20],
+ 'chained path';
+}
+
+{
+ my $collection = Slic3r::ExtrusionPath::Collection->new(
+ map Slic3r::ExtrusionPath->new(polyline => $_, role => 0, mm3_per_mm => 1),
+ Slic3r::Polyline->new([0,15], [0,18], [0,20]),
+ Slic3r::Polyline->new([0,10], [0,8], [0,5]),
+ );
+ is_deeply
+ [ map $_->[Y], map @{$_->polyline}, @{$collection->chained_path_from(Slic3r::Point->new(0,30), 0)} ],
+ [20, 18, 15, 10, 8, 5],
+ 'chained path';
+}
+
+{
+ my $collection = Slic3r::ExtrusionPath::Collection->new(
+ map Slic3r::ExtrusionPath->new(polyline => $_, role => 0, mm3_per_mm => 1),
+ Slic3r::Polyline->new([15,0], [10,0], [4,0]),
+ Slic3r::Polyline->new([10,5], [15,5], [20,5]),
+ );
+ is_deeply
+ [ map $_->[X], map @{$_->polyline}, @{$collection->chained_path_from(Slic3r::Point->new(30,0), 0)} ],
+ [reverse 4, 10, 15, 10, 15, 20],
+ 'chained path';
+}
+
+for my $pattern (qw(rectilinear honeycomb hilbertcurve concentric)) {
+ my $config = Slic3r::Config->new_from_defaults;
+ $config->set('fill_pattern', $pattern);
+ $config->set('external_fill_pattern', $pattern);
+ $config->set('perimeters', 1);
+ $config->set('skirts', 0);
+ $config->set('fill_density', 20);
+ $config->set('layer_height', 0.05);
+ $config->set('perimeter_extruder', 1);
+ $config->set('infill_extruder', 2);
+ my $print = Slic3r::Test::init_print('20mm_cube', config => $config, scale => 2);
+ ok my $gcode = Slic3r::Test::gcode($print), "successful $pattern infill generation";
+ my $tool = undef;
+ my @perimeter_points = my @infill_points = ();
+ Slic3r::GCode::Reader->new->parse($gcode, sub {
+ my ($self, $cmd, $args, $info) = @_;
+
+ if ($cmd =~ /^T(\d+)/) {
+ $tool = $1;
+ } elsif ($cmd eq 'G1' && $info->{extruding} && $info->{dist_XY} > 0) {
+ if ($tool == $config->perimeter_extruder-1) {
+ push @perimeter_points, Slic3r::Point->new_scale($args->{X}, $args->{Y});
+ } elsif ($tool == $config->infill_extruder-1) {
+ push @infill_points, Slic3r::Point->new_scale($args->{X}, $args->{Y});
+ }
+ }
+ });
+ my $convex_hull = convex_hull(\@perimeter_points);
+ ok !(defined first { !$convex_hull->contains_point($_) } @infill_points), "infill does not exceed perimeters ($pattern)";
+}
+
+{
+ my $config = Slic3r::Config->new_from_defaults;
+ $config->set('infill_only_where_needed', 1);
+ $config->set('bottom_solid_layers', 0);
+ $config->set('infill_extruder', 2);
+ $config->set('infill_extrusion_width', 0.5);
+ $config->set('fill_density', 40);
+ $config->set('cooling', 0); # for preventing speeds from being altered
+ $config->set('first_layer_speed', '100%'); # for preventing speeds from being altered
+
+ my $test = sub {
+ my $print = Slic3r::Test::init_print('pyramid', config => $config);
+
+ my $tool = undef;
+ my @infill_extrusions = (); # array of polylines
+ Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub {
+ my ($self, $cmd, $args, $info) = @_;
+
+ if ($cmd =~ /^T(\d+)/) {
+ $tool = $1;
+ } elsif ($cmd eq 'G1' && $info->{extruding} && $info->{dist_XY} > 0) {
+ if ($tool == $config->infill_extruder-1) {
+ push @infill_extrusions, Slic3r::Line->new_scale(
+ [ $self->X, $self->Y ],
+ [ $info->{new_X}, $info->{new_Y} ],
+ );
+ }
+ }
+ });
+ return 0 if !@infill_extrusions; # prevent calling convex_hull() with no points
+
+ my $convex_hull = convex_hull([ map $_->pp, map @$_, @infill_extrusions ]);
+ return unscale unscale sum(map $_->area, @{offset([$convex_hull], scale(+$config->infill_extrusion_width/2))});
+ };
+
+ my $tolerance = 5; # mm^2
+
+ $config->set('solid_infill_below_area', 0);
+ ok $test->() < $tolerance,
+ 'no infill is generated when using infill_only_where_needed on a pyramid';
+
+ $config->set('solid_infill_below_area', 70);
+ ok abs($test->() - $config->solid_infill_below_area) < $tolerance,
+ 'infill is only generated under the forced solid shells';
+}
+
+{
+ my $config = Slic3r::Config->new_from_defaults;
+ $config->set('skirts', 0);
+ $config->set('perimeters', 1);
+ $config->set('fill_density', 0);
+ $config->set('top_solid_layers', 0);
+ $config->set('bottom_solid_layers', 0);
+ $config->set('solid_infill_below_area', 20000000);
+ $config->set('solid_infill_every_layers', 2);
+ $config->set('perimeter_speed', 99);
+ $config->set('external_perimeter_speed', 99);
+ $config->set('cooling', 0);
+ $config->set('first_layer_speed', '100%');
+
+ my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
+ my %layers_with_extrusion = ();
+ Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub {
+ my ($self, $cmd, $args, $info) = @_;
+
+ if ($cmd eq 'G1' && $info->{dist_XY} > 0 && $info->{extruding}) {
+ if (($args->{F} // $self->F) != $config->perimeter_speed*60) {
+ $layers_with_extrusion{$self->Z} = ($args->{F} // $self->F);
+ }
+ }
+ });
+
+ ok !%layers_with_extrusion,
+ "solid_infill_below_area and solid_infill_every_layers are ignored when fill_density is 0";
+}
+
+{
+ my $config = Slic3r::Config->new_from_defaults;
+ $config->set('skirts', 0);
+ $config->set('perimeters', 3);
+ $config->set('fill_density', 0);
+ $config->set('layer_height', 0.2);
+ $config->set('first_layer_height', 0.2);
+ $config->set('nozzle_diameter', [0.35]);
+ $config->set('infill_extruder', 2);
+ $config->set('solid_infill_extruder', 2);
+ $config->set('infill_extrusion_width', 0.52);
+ $config->set('solid_infill_extrusion_width', 0.52);
+ $config->set('first_layer_extrusion_width', 0);
+
+ my $print = Slic3r::Test::init_print('A', config => $config);
+ my %infill = (); # Z => [ Line, Line ... ]
+ my $tool = undef;
+ Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub {
+ my ($self, $cmd, $args, $info) = @_;
+
+ if ($cmd =~ /^T(\d+)/) {
+ $tool = $1;
+ } elsif ($cmd eq 'G1' && $info->{extruding} && $info->{dist_XY} > 0) {
+ if ($tool == $config->infill_extruder-1) {
+ my $z = 1 * $self->Z;
+ $infill{$z} ||= [];
+ push @{$infill{$z}}, Slic3r::Line->new_scale(
+ [ $self->X, $self->Y ],
+ [ $info->{new_X}, $info->{new_Y} ],
+ );
+ }
+ }
+ });
+ my $grow_d = scale($config->infill_extrusion_width)/2;
+ my $layer0_infill = union([ map @{$_->grow($grow_d)}, @{ $infill{0.2} } ]);
+ my $layer1_infill = union([ map @{$_->grow($grow_d)}, @{ $infill{0.4} } ]);
+ my $diff = diff($layer0_infill, $layer1_infill);
+ $diff = offset2_ex($diff, -$grow_d, +$grow_d);
+ $diff = [ grep { $_->area > 2*(($grow_d*2)**2) } @$diff ];
+ is scalar(@$diff), 0, 'no missing parts in solid shell when fill_density is 0';
+}
+
+{
+ # GH: #2697
+ my $config = Slic3r::Config->new_from_defaults;
+ $config->set('perimeter_extrusion_width', 0.72);
+ $config->set('top_infill_extrusion_width', 0.1);
+ $config->set('infill_extruder', 2); # in order to distinguish infill
+ $config->set('solid_infill_extruder', 2); # in order to distinguish infill
+
+ my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
+ my %infill = (); # Z => [ Line, Line ... ]
+ my %other = (); # Z => [ Line, Line ... ]
+ my $tool = undef;
+ Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub {
+ my ($self, $cmd, $args, $info) = @_;
+
+ if ($cmd =~ /^T(\d+)/) {
+ $tool = $1;
+ } elsif ($cmd eq 'G1' && $info->{extruding} && $info->{dist_XY} > 0) {
+ my $z = 1 * $self->Z;
+ my $line = Slic3r::Line->new_scale(
+ [ $self->X, $self->Y ],
+ [ $info->{new_X}, $info->{new_Y} ],
+ );
+ if ($tool == $config->infill_extruder-1) {
+ $infill{$z} //= [];
+ push @{$infill{$z}}, $line;
+ } else {
+ $other{$z} //= [];
+ push @{$other{$z}}, $line;
+ }
+ }
+ });
+ my $top_z = max(keys %infill);
+ my $top_infill_grow_d = scale($config->top_infill_extrusion_width)/2;
+ my $top_infill = union([ map @{$_->grow($top_infill_grow_d)}, @{ $infill{$top_z} } ]);
+ my $perimeters_grow_d = scale($config->perimeter_extrusion_width)/2;
+ my $perimeters = union([ map @{$_->grow($perimeters_grow_d)}, @{ $other{$top_z} } ]);
+ my $covered = union_ex([ @$top_infill, @$perimeters ]);
+ my @holes = map @{$_->holes}, @$covered;
+ ok sum(map unscale unscale $_->area*-1, @holes) < 1, 'no gaps between top solid infill and perimeters';
+}
+*/
+
+bool test_if_solid_surface_filled(const ExPolygon& expolygon, double flow_spacing, double angle, double density)
+{
+ std::unique_ptr<Slic3r::Fill> filler(Slic3r::Fill::new_from_type("rectilinear"));
+ filler->bounding_box = get_extents(expolygon.contour);
+ filler->angle = float(angle);
+
+ Flow flow(flow_spacing, 0.4, flow_spacing);
+ filler->spacing = flow.spacing();
+
+ FillParams fill_params;
+ fill_params.density = float(density);
+ fill_params.dont_adjust = false;
+
+ Surface surface(stBottom, expolygon);
+ Slic3r::Polylines paths = filler->fill_surface(&surface, fill_params);
+
+ // check whether any part was left uncovered
+ Polygons grown_paths;
+ grown_paths.reserve(paths.size());
+
+ // figure out what is actually going on here re: data types
+ float line_offset = float(scale_(filler->spacing / 2.0 + EPSILON));
+ std::for_each(paths.begin(), paths.end(), [line_offset, &grown_paths] (const Slic3r::Polyline& p) {
+ polygons_append(grown_paths, offset(p, line_offset));
+ });
+
+ // Shrink the initial expolygon a bit, this simulates the infill / perimeter overlap that we usually apply.
+ ExPolygons uncovered = diff_ex(offset(expolygon, - float(0.2 * scale_(flow_spacing))), grown_paths, true);
+
+ // ignore very small dots
+ const double scaled_flow_spacing = std::pow(scale_(flow_spacing), 2);
+ uncovered.erase(std::remove_if(uncovered.begin(), uncovered.end(), [scaled_flow_spacing](const ExPolygon& poly) { return poly.area() < scaled_flow_spacing; }), uncovered.end());
+
+#if 0
+ if (! uncovered.empty()) {
+ BoundingBox bbox = get_extents(expolygon.contour);
+ bbox.merge(get_extents(uncovered));
+ bbox.merge(get_extents(grown_paths));
+ SVG svg("c:\\data\\temp\\test_if_solid_surface_filled.svg", bbox);
+ svg.draw(expolygon);
+ svg.draw(uncovered, "red");
+ svg.Close();
+ }
+#endif
+
+ return uncovered.empty(); // solid surface is fully filled
+}
diff --git a/tests/fff_print/test_flow.cpp b/tests/fff_print/test_flow.cpp
new file mode 100644
index 000000000..e4398988a
--- /dev/null
+++ b/tests/fff_print/test_flow.cpp
@@ -0,0 +1,203 @@
+#include <catch2/catch.hpp>
+
+#include <numeric>
+#include <sstream>
+
+#include "test_data.hpp" // get access to init_print, etc
+
+#include "libslic3r/Config.hpp"
+#include "libslic3r/Model.hpp"
+#include "libslic3r/Config.hpp"
+#include "libslic3r/GCodeReader.hpp"
+#include "libslic3r/Flow.hpp"
+#include "libslic3r/libslic3r.h"
+
+using namespace Slic3r::Test;
+using namespace Slic3r;
+
+SCENARIO("Extrusion width specifics", "[!mayfail]") {
+ GIVEN("A config with a skirt, brim, some fill density, 3 perimeters, and 1 bottom solid layer and a 20mm cube mesh") {
+ // this is a sharedptr
+ DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
+ config.opt_int("skirts") = 1;
+ config.opt_float("brim_width") = 2.;
+ config.opt_int("perimeters") = 3;
+ config.set_deserialize("fill_density", "40%");
+ config.set_deserialize("first_layer_height", "100%");
+
+ WHEN("first layer width set to 2mm") {
+ Slic3r::Model model;
+ config.set_deserialize("first_layer_extrusion_width", "2");
+ std::shared_ptr<Print> print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config);
+
+ std::vector<double> E_per_mm_bottom;
+ std::string gcode = Test::gcode(print);
+ Slic3r::GCodeReader parser;
+ const double layer_height = config.opt_float("layer_height");
+ parser.parse_buffer(gcode, [&E_per_mm_bottom, layer_height] (Slic3r::GCodeReader& self, const Slic3r::GCodeReader::GCodeLine& line)
+ {
+ if (self.z() == Approx(layer_height).margin(0.01)) { // only consider first layer
+ if (line.extruding(self) && line.dist_XY(self) > 0) {
+ E_per_mm_bottom.emplace_back(line.dist_E(self) / line.dist_XY(self));
+ }
+ }
+ });
+ THEN(" First layer width applies to everything on first layer.") {
+ bool pass = false;
+ double avg_E = std::accumulate(E_per_mm_bottom.cbegin(), E_per_mm_bottom.cend(), 0.0) / static_cast<double>(E_per_mm_bottom.size());
+
+ pass = (std::count_if(E_per_mm_bottom.cbegin(), E_per_mm_bottom.cend(), [avg_E] (const double& v) { return v == Approx(avg_E); }) == 0);
+ REQUIRE(pass == true);
+ REQUIRE(E_per_mm_bottom.size() > 0); // make sure it actually passed because of extrusion
+ }
+ THEN(" First layer width does not apply to upper layer.") {
+ }
+ }
+ }
+}
+// needs gcode export
+SCENARIO(" Bridge flow specifics.", "[!mayfail]") {
+ GIVEN("A default config with no cooling and a fixed bridge speed, flow ratio and an overhang mesh.") {
+ WHEN("bridge_flow_ratio is set to 1.0") {
+ THEN("Output flow is as expected.") {
+ }
+ }
+ WHEN("bridge_flow_ratio is set to 0.5") {
+ THEN("Output flow is as expected.") {
+ }
+ }
+ WHEN("bridge_flow_ratio is set to 2.0") {
+ THEN("Output flow is as expected.") {
+ }
+ }
+ }
+ GIVEN("A default config with no cooling and a fixed bridge speed, flow ratio, fixed extrusion width of 0.4mm and an overhang mesh.") {
+ WHEN("bridge_flow_ratio is set to 1.0") {
+ THEN("Output flow is as expected.") {
+ }
+ }
+ WHEN("bridge_flow_ratio is set to 0.5") {
+ THEN("Output flow is as expected.") {
+ }
+ }
+ WHEN("bridge_flow_ratio is set to 2.0") {
+ THEN("Output flow is as expected.") {
+ }
+ }
+ }
+}
+
+/// Test the expected behavior for auto-width,
+/// spacing, etc
+SCENARIO("Flow: Flow math for non-bridges", "[!mayfail]") {
+ GIVEN("Nozzle Diameter of 0.4, a desired width of 1mm and layer height of 0.5") {
+ ConfigOptionFloatOrPercent width(1.0, false);
+ float spacing {0.4};
+ float nozzle_diameter {0.4};
+ float bridge_flow {1.0};
+ float layer_height {0.5};
+
+ // Spacing for non-bridges is has some overlap
+ THEN("External perimeter flow has spacing fixed to 1.1*nozzle_diameter") {
+ auto flow = Flow::new_from_config_width(frExternalPerimeter, ConfigOptionFloatOrPercent(0, false), nozzle_diameter, layer_height, 0.0f);
+ REQUIRE(flow.spacing() == Approx((1.1*nozzle_diameter) - layer_height * (1.0 - PI / 4.0)));
+ }
+
+ THEN("Internal perimeter flow has spacing of 1.05 (minimum)") {
+ auto flow = Flow::new_from_config_width(frPerimeter, ConfigOptionFloatOrPercent(0, false), nozzle_diameter, layer_height, 0.0f);
+ REQUIRE(flow.spacing() == Approx((1.05*nozzle_diameter) - layer_height * (1.0 - PI / 4.0)));
+ }
+ THEN("Spacing for supplied width is 0.8927f") {
+ auto flow = Flow::new_from_config_width(frExternalPerimeter, width, nozzle_diameter, layer_height, 0.0f);
+ REQUIRE(flow.spacing() == Approx(width.value - layer_height * (1.0 - PI / 4.0)));
+ flow = Flow::new_from_config_width(frPerimeter, width, nozzle_diameter, layer_height, 0.0f);
+ REQUIRE(flow.spacing() == Approx(width.value - layer_height * (1.0 - PI / 4.0)));
+ }
+ }
+ /// Check the min/max
+ GIVEN("Nozzle Diameter of 0.25") {
+ float spacing {0.4};
+ float nozzle_diameter {0.25};
+ float bridge_flow {0.0};
+ float layer_height {0.5};
+ WHEN("layer height is set to 0.2") {
+ layer_height = 0.15f;
+ THEN("Max width is set.") {
+ auto flow = Flow::new_from_config_width(frPerimeter, ConfigOptionFloatOrPercent(0, false), nozzle_diameter, layer_height, 0.0f);
+ REQUIRE(flow.width == Approx(1.4*nozzle_diameter));
+ }
+ }
+ WHEN("Layer height is set to 0.2") {
+ layer_height = 0.3f;
+ THEN("Min width is set.") {
+ auto flow = Flow::new_from_config_width(frPerimeter, ConfigOptionFloatOrPercent(0, false), nozzle_diameter, layer_height, 0.0f);
+ REQUIRE(flow.width == Approx(1.05*nozzle_diameter));
+ }
+ }
+ }
+
+
+#if 0
+ /// Check for an edge case in the maths where the spacing could be 0; original
+ /// math is 0.99. Slic3r issue #4654
+ GIVEN("Input spacing of 0.414159 and a total width of 2") {
+ double in_spacing = 0.414159;
+ double total_width = 2.0;
+ auto flow = Flow::new_from_spacing(1.0, 0.4, 0.3, false);
+ WHEN("solid_spacing() is called") {
+ double result = flow.solid_spacing(total_width, in_spacing);
+ THEN("Yielded spacing is greater than 0") {
+ REQUIRE(result > 0);
+ }
+ }
+ }
+#endif
+
+}
+
+/// Spacing, width calculation for bridge extrusions
+SCENARIO("Flow: Flow math for bridges", "[!mayfail]") {
+ GIVEN("Nozzle Diameter of 0.4, a desired width of 1mm and layer height of 0.5") {
+ auto width = ConfigOptionFloatOrPercent(1.0, false);
+ double spacing = 0.4;
+ double nozzle_diameter = 0.4;
+ double bridge_flow = 1.0;
+ double layer_height = 0.5;
+ WHEN("Flow role is frExternalPerimeter") {
+ auto flow = Flow::new_from_config_width(frExternalPerimeter, width, nozzle_diameter, layer_height, bridge_flow);
+ THEN("Bridge width is same as nozzle diameter") {
+ REQUIRE(flow.width == Approx(nozzle_diameter));
+ }
+ THEN("Bridge spacing is same as nozzle diameter + BRIDGE_EXTRA_SPACING") {
+ REQUIRE(flow.spacing() == Approx(nozzle_diameter + BRIDGE_EXTRA_SPACING));
+ }
+ }
+ WHEN("Flow role is frInfill") {
+ auto flow = Flow::new_from_config_width(frInfill, width, nozzle_diameter, layer_height, bridge_flow);
+ THEN("Bridge width is same as nozzle diameter") {
+ REQUIRE(flow.width == Approx(nozzle_diameter));
+ }
+ THEN("Bridge spacing is same as nozzle diameter + BRIDGE_EXTRA_SPACING") {
+ REQUIRE(flow.spacing() == Approx(nozzle_diameter + BRIDGE_EXTRA_SPACING));
+ }
+ }
+ WHEN("Flow role is frPerimeter") {
+ auto flow = Flow::new_from_config_width(frPerimeter, width, nozzle_diameter, layer_height, bridge_flow);
+ THEN("Bridge width is same as nozzle diameter") {
+ REQUIRE(flow.width == Approx(nozzle_diameter));
+ }
+ THEN("Bridge spacing is same as nozzle diameter + BRIDGE_EXTRA_SPACING") {
+ REQUIRE(flow.spacing() == Approx(nozzle_diameter + BRIDGE_EXTRA_SPACING));
+ }
+ }
+ WHEN("Flow role is frSupportMaterial") {
+ auto flow = Flow::new_from_config_width(frSupportMaterial, width, nozzle_diameter, layer_height, bridge_flow);
+ THEN("Bridge width is same as nozzle diameter") {
+ REQUIRE(flow.width == Approx(nozzle_diameter));
+ }
+ THEN("Bridge spacing is same as nozzle diameter + BRIDGE_EXTRA_SPACING") {
+ REQUIRE(flow.spacing() == Approx(nozzle_diameter + BRIDGE_EXTRA_SPACING));
+ }
+ }
+ }
+}
diff --git a/tests/fff_print/test_gcodewriter.cpp b/tests/fff_print/test_gcodewriter.cpp
new file mode 100644
index 000000000..4f1c550eb
--- /dev/null
+++ b/tests/fff_print/test_gcodewriter.cpp
@@ -0,0 +1,124 @@
+#include <catch2/catch.hpp>
+
+#include <memory>
+
+#include "libslic3r/GCodeWriter.hpp"
+
+using namespace Slic3r;
+
+SCENARIO("lift() and unlift() behavior with large values of Z", "[!shouldfail]") {
+ GIVEN("A config from a file and a single extruder.") {
+ GCodeWriter writer;
+ GCodeConfig &config = writer.config;
+ config.load(std::string(TEST_DATA_DIR) + "/fff_print_tests/test_gcodewriter/config_lift_unlift.ini");
+
+ std::vector<unsigned int> extruder_ids {0};
+ writer.set_extruders(extruder_ids);
+ writer.set_extruder(0);
+
+ WHEN("Z is set to 9007199254740992") {
+ double trouble_Z = 9007199254740992;
+ writer.travel_to_z(trouble_Z);
+ AND_WHEN("GcodeWriter::Lift() is called") {
+ REQUIRE(writer.lift().size() > 0);
+ AND_WHEN("Z is moved post-lift to the same delta as the config Z lift") {
+ REQUIRE(writer.travel_to_z(trouble_Z + config.retract_lift.values[0]).size() == 0);
+ AND_WHEN("GCodeWriter::Unlift() is called") {
+ REQUIRE(writer.unlift().size() == 0); // we're the same height so no additional move happens.
+ THEN("GCodeWriter::Lift() emits gcode.") {
+ REQUIRE(writer.lift().size() > 0);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+SCENARIO("lift() is not ignored after unlift() at normal values of Z") {
+ GIVEN("A config from a file and a single extruder.") {
+ GCodeWriter writer;
+ GCodeConfig &config = writer.config;
+ config.load(std::string(TEST_DATA_DIR) + "/fff_print_tests/test_gcodewriter/config_lift_unlift.ini");
+
+ std::vector<unsigned int> extruder_ids {0};
+ writer.set_extruders(extruder_ids);
+ writer.set_extruder(0);
+
+ WHEN("Z is set to 203") {
+ double trouble_Z = 203;
+ writer.travel_to_z(trouble_Z);
+ AND_WHEN("GcodeWriter::Lift() is called") {
+ REQUIRE(writer.lift().size() > 0);
+ AND_WHEN("Z is moved post-lift to the same delta as the config Z lift") {
+ REQUIRE(writer.travel_to_z(trouble_Z + config.retract_lift.values[0]).size() == 0);
+ AND_WHEN("GCodeWriter::Unlift() is called") {
+ REQUIRE(writer.unlift().size() == 0); // we're the same height so no additional move happens.
+ THEN("GCodeWriter::Lift() emits gcode.") {
+ REQUIRE(writer.lift().size() > 0);
+ }
+ }
+ }
+ }
+ }
+ WHEN("Z is set to 500003") {
+ double trouble_Z = 500003;
+ writer.travel_to_z(trouble_Z);
+ AND_WHEN("GcodeWriter::Lift() is called") {
+ REQUIRE(writer.lift().size() > 0);
+ AND_WHEN("Z is moved post-lift to the same delta as the config Z lift") {
+ REQUIRE(writer.travel_to_z(trouble_Z + config.retract_lift.values[0]).size() == 0);
+ AND_WHEN("GCodeWriter::Unlift() is called") {
+ REQUIRE(writer.unlift().size() == 0); // we're the same height so no additional move happens.
+ THEN("GCodeWriter::Lift() emits gcode.") {
+ REQUIRE(writer.lift().size() > 0);
+ }
+ }
+ }
+ }
+ }
+ WHEN("Z is set to 10.3") {
+ double trouble_Z = 10.3;
+ writer.travel_to_z(trouble_Z);
+ AND_WHEN("GcodeWriter::Lift() is called") {
+ REQUIRE(writer.lift().size() > 0);
+ AND_WHEN("Z is moved post-lift to the same delta as the config Z lift") {
+ REQUIRE(writer.travel_to_z(trouble_Z + config.retract_lift.values[0]).size() == 0);
+ AND_WHEN("GCodeWriter::Unlift() is called") {
+ REQUIRE(writer.unlift().size() == 0); // we're the same height so no additional move happens.
+ THEN("GCodeWriter::Lift() emits gcode.") {
+ REQUIRE(writer.lift().size() > 0);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+SCENARIO("set_speed emits values with fixed-point output.") {
+
+ GIVEN("GCodeWriter instance") {
+ GCodeWriter writer;
+ WHEN("set_speed is called to set speed to 99999.123") {
+ THEN("Output string is G1 F99999.123") {
+ REQUIRE_THAT(writer.set_speed(99999.123), Catch::Equals("G1 F99999.123\n"));
+ }
+ }
+ WHEN("set_speed is called to set speed to 1") {
+ THEN("Output string is G1 F1.000") {
+ REQUIRE_THAT(writer.set_speed(1.0), Catch::Equals("G1 F1.000\n"));
+ }
+ }
+ WHEN("set_speed is called to set speed to 203.200022") {
+ THEN("Output string is G1 F203.200") {
+ REQUIRE_THAT(writer.set_speed(203.200022), Catch::Equals("G1 F203.200\n"));
+ }
+ }
+ WHEN("set_speed is called to set speed to 203.200522") {
+ THEN("Output string is G1 F203.201") {
+ REQUIRE_THAT(writer.set_speed(203.200522), Catch::Equals("G1 F203.201\n"));
+ }
+ }
+ }
+}
diff --git a/tests/fff_print/test_model.cpp b/tests/fff_print/test_model.cpp
new file mode 100644
index 000000000..88966d774
--- /dev/null
+++ b/tests/fff_print/test_model.cpp
@@ -0,0 +1,61 @@
+#include <catch2/catch.hpp>
+
+#include "libslic3r/libslic3r.h"
+#include "libslic3r/Model.hpp"
+
+#include <boost/nowide/cstdio.hpp>
+#include <boost/filesystem.hpp>
+
+#include "test_data.hpp"
+
+using namespace Slic3r;
+using namespace Slic3r::Test;
+
+SCENARIO("Model construction", "[Model]") {
+ GIVEN("A Slic3r Model") {
+ Slic3r::Model model;
+ Slic3r::TriangleMesh sample_mesh = Slic3r::make_cube(20,20,20);
+ Slic3r::DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
+ Slic3r::Print print;
+
+ WHEN("Model object is added") {
+ Slic3r::ModelObject *model_object = model.add_object();
+ THEN("Model object list == 1") {
+ REQUIRE(model.objects.size() == 1);
+ }
+ model_object->add_volume(sample_mesh);
+ THEN("Model volume list == 1") {
+ REQUIRE(model_object->volumes.size() == 1);
+ }
+ THEN("Model volume is a part") {
+ REQUIRE(model_object->volumes.front()->is_model_part());
+ }
+ THEN("Mesh is equivalent to input mesh.") {
+ REQUIRE(! sample_mesh.its.vertices.empty());
+ const std::vector<Vec3f>& mesh_vertices = model_object->volumes.front()->mesh().its.vertices;
+ Vec3f mesh_offset = model_object->volumes.front()->source.mesh_offset.cast<float>();
+ for (size_t i = 0; i < sample_mesh.its.vertices.size(); ++ i) {
+ const Vec3f &p1 = sample_mesh.its.vertices[i];
+ const Vec3f p2 = mesh_vertices[i] + mesh_offset;
+ REQUIRE((p2 - p1).norm() < EPSILON);
+ }
+ }
+ Slic3r::ModelInstance *model_instance = model_object->add_instance();
+ model.arrange_objects(PrintConfig::min_object_distance(&config));
+ model.center_instances_around_point(Slic3r::Vec2d(100, 100));
+ model_object->ensure_on_bed();
+ print.auto_assign_extruders(model_object);
+ THEN("Print works?") {
+ print.set_status_silent();
+ print.apply(model, config);
+ print.process();
+ boost::filesystem::path temp = boost::filesystem::unique_path();
+ print.export_gcode(temp.string(), nullptr);
+ REQUIRE(boost::filesystem::exists(temp));
+ REQUIRE(boost::filesystem::is_regular_file(temp));
+ REQUIRE(boost::filesystem::file_size(temp) > 0);
+ boost::nowide::remove(temp.string().c_str());
+ }
+ }
+ }
+}
diff --git a/tests/fff_print/test_print.cpp b/tests/fff_print/test_print.cpp
new file mode 100644
index 000000000..aae4731a9
--- /dev/null
+++ b/tests/fff_print/test_print.cpp
@@ -0,0 +1,144 @@
+#include <catch2/catch.hpp>
+
+#include "libslic3r/libslic3r.h"
+#include "libslic3r/Print.hpp"
+
+#include "test_data.hpp"
+
+using namespace Slic3r;
+using namespace Slic3r::Test;
+
+SCENARIO("PrintObject: Perimeter generation", "[PrintObject]") {
+ GIVEN("20mm cube and default config") {
+ Slic3r::DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
+ TestMesh m = TestMesh::cube_20x20x20;
+ Slic3r::Model model;
+ size_t event_counter = 0;
+ std::string stage;
+ int value = 0;
+ auto callback = [&event_counter, &stage, &value] (int a, const char* b) { stage = std::string(b); ++ event_counter; value = a; };
+ config.set_deserialize("fill_density", "0");
+
+ WHEN("make_perimeters() is called") {
+ std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({m}, model, config);
+ print->process();
+ const PrintObject& object = *(print->objects().at(0));
+ THEN("67 layers exist in the model") {
+ REQUIRE(object.layers().size() == 66);
+ }
+ THEN("Every layer in region 0 has 1 island of perimeters") {
+ for (const Layer *layer : object.layers()) {
+ REQUIRE(layer->regions().front()->perimeters.entities.size() == 1);
+ }
+ }
+ THEN("Every layer in region 0 has 3 paths in its perimeters list.") {
+ for (const Layer *layer : object.layers()) {
+ REQUIRE(layer->regions().front()->perimeters.items_count() == 3);
+ }
+ }
+ }
+ }
+}
+
+SCENARIO("Print: Skirt generation", "[Print]") {
+ GIVEN("20mm cube and default config") {
+ Slic3r::DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
+ TestMesh m = TestMesh::cube_20x20x20;
+ Slic3r::Model model;
+ std::string stage;
+ int value = 0;
+ config.opt_int("skirt_height") = 1;
+ config.opt_float("skirt_distance") = 1.f;
+ WHEN("Skirts is set to 2 loops") {
+ config.opt_int("skirts") = 2;
+ std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({m}, model, config);
+ print->process();
+ THEN("Skirt Extrusion collection has 2 loops in it") {
+ REQUIRE(print->skirt().items_count() == 2);
+ REQUIRE(print->skirt().flatten().entities.size() == 2);
+ }
+ }
+ }
+}
+
+void test_is_solid_infill(std::shared_ptr<Slic3r::Print> p, size_t obj_id, size_t layer_id ) {
+ const PrintObject &obj = *(p->objects().at(obj_id));
+ const Layer &layer = *(obj.get_layer((int)layer_id));
+
+ // iterate over all of the regions in the layer
+ for (const LayerRegion *reg : layer.regions()) {
+ // for each region, iterate over the fill surfaces
+ for (const Surface& s : reg->fill_surfaces.surfaces) {
+ CHECK(s.is_solid());
+ }
+ }
+}
+
+SCENARIO("Print: Changing number of solid surfaces does not cause all surfaces to become internal.", "[Print]") {
+ GIVEN("sliced 20mm cube and config with top_solid_surfaces = 2 and bottom_solid_surfaces = 1") {
+ Slic3r::DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
+ TestMesh m = TestMesh::cube_20x20x20;
+ config.opt_int("top_solid_layers") = 2;
+ config.opt_int("bottom_solid_layers") = 1;
+ config.opt_float("layer_height") = 0.5; // get a known number of layers
+ config.set_deserialize("first_layer_height", "0.5");
+ Slic3r::Model model;
+ std::string stage;
+ std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({m}, model, config);
+ print->process();
+ // Precondition: Ensure that the model has 2 solid top layers (39, 38)
+ // and one solid bottom layer (0).
+ test_is_solid_infill(print, 0, 0); // should be solid
+ test_is_solid_infill(print, 0, 39); // should be solid
+ test_is_solid_infill(print, 0, 38); // should be solid
+ WHEN("Model is re-sliced with top_solid_layers == 3") {
+ config.opt_int("top_solid_layers") = 3;
+ print->apply(model, config);
+ print->process();
+ THEN("Print object does not have 0 solid bottom layers.") {
+ test_is_solid_infill(print, 0, 0);
+ }
+ AND_THEN("Print object has 3 top solid layers") {
+ test_is_solid_infill(print, 0, 39);
+ test_is_solid_infill(print, 0, 38);
+ test_is_solid_infill(print, 0, 37);
+ }
+ }
+ }
+}
+
+SCENARIO("Print: Brim generation", "[Print]") {
+ GIVEN("20mm cube and default config, 1mm first layer width") {
+ Slic3r::DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
+ TestMesh m = TestMesh::cube_20x20x20;
+ Slic3r::Model model;
+ std::string stage;
+ int value = 0;
+ config.set_deserialize("first_layer_extrusion_width", "1");
+ WHEN("Brim is set to 3mm") {
+ config.opt_float("brim_width") = 3;
+ std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({m}, model, config);
+ print->process();
+ THEN("Brim Extrusion collection has 3 loops in it") {
+ REQUIRE(print->brim().items_count() == 3);
+ }
+ }
+ WHEN("Brim is set to 6mm") {
+ config.opt_float("brim_width") = 6;
+ std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({m}, model, config);
+ print->process();
+ THEN("Brim Extrusion collection has 6 loops in it") {
+ REQUIRE(print->brim().items_count() == 6);
+ }
+ }
+ WHEN("Brim is set to 6mm, extrusion width 0.5mm") {
+ config.opt_float("brim_width") = 6;
+ config.set_deserialize("first_layer_extrusion_width", "0.5");
+ std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({m}, model, config);
+ print->process();
+ THEN("Brim Extrusion collection has 12 loops in it") {
+ REQUIRE(print->brim().items_count() == 14);
+ }
+ }
+ }
+}
diff --git a/tests/fff_print/test_printgcode.cpp b/tests/fff_print/test_printgcode.cpp
new file mode 100644
index 000000000..376392d04
--- /dev/null
+++ b/tests/fff_print/test_printgcode.cpp
@@ -0,0 +1,274 @@
+#include <catch2/catch.hpp>
+
+#include "libslic3r/libslic3r.h"
+#include "libslic3r/GCodeReader.hpp"
+
+#include "test_data.hpp"
+
+#include <algorithm>
+#include <regex>
+
+using namespace Slic3r;
+using namespace Slic3r::Test;
+
+std::regex perimeters_regex("G1 X[-0-9.]* Y[-0-9.]* E[-0-9.]* ; perimeter");
+std::regex infill_regex("G1 X[-0-9.]* Y[-0-9.]* E[-0-9.]* ; infill");
+std::regex skirt_regex("G1 X[-0-9.]* Y[-0-9.]* E[-0-9.]* ; skirt");
+
+SCENARIO( "PrintGCode basic functionality", "[PrintGCode]") {
+ GIVEN("A default configuration and a print test object") {
+ Slic3r::DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
+
+ WHEN("the output is executed with no support material") {
+ config.set_deserialize("layer_height", "0.2");
+ config.set_deserialize("first_layer_height", "0.2");
+ config.set_deserialize("first_layer_extrusion_width", "0");
+ config.set_deserialize("gcode_comments", "1");
+ config.set_deserialize("start_gcode", "");
+ Slic3r::Model model;
+ std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config);
+ std::string gcode = Slic3r::Test::gcode(print);
+ THEN("Some text output is generated.") {
+ REQUIRE(gcode.size() > 0);
+ }
+ THEN("Exported text contains slic3r version") {
+ REQUIRE(gcode.find(SLIC3R_VERSION) != std::string::npos);
+ }
+ //THEN("Exported text contains git commit id") {
+ // REQUIRE(gcode.find("; Git Commit") != std::string::npos);
+ // REQUIRE(gcode.find(SLIC3R_BUILD_ID) != std::string::npos);
+ //}
+ THEN("Exported text contains extrusion statistics.") {
+ REQUIRE(gcode.find("; external perimeters extrusion width") != std::string::npos);
+ REQUIRE(gcode.find("; perimeters extrusion width") != std::string::npos);
+ REQUIRE(gcode.find("; infill extrusion width") != std::string::npos);
+ REQUIRE(gcode.find("; solid infill extrusion width") != std::string::npos);
+ REQUIRE(gcode.find("; top infill extrusion width") != std::string::npos);
+ REQUIRE(gcode.find("; support material extrusion width") == std::string::npos);
+ REQUIRE(gcode.find("; first layer extrusion width") == std::string::npos);
+ }
+ THEN("Exported text does not contain cooling markers (they were consumed)") {
+ REQUIRE(gcode.find(";_EXTRUDE_SET_SPEED") == std::string::npos);
+ }
+
+ THEN("GCode preamble is emitted.") {
+ REQUIRE(gcode.find("G21 ; set units to millimeters") != std::string::npos);
+ }
+
+ THEN("Config options emitted for print config, default region config, default object config") {
+ REQUIRE(gcode.find("; first_layer_temperature") != std::string::npos);
+ REQUIRE(gcode.find("; layer_height") != std::string::npos);
+ REQUIRE(gcode.find("; fill_density") != std::string::npos);
+ }
+ THEN("Infill is emitted.") {
+ std::smatch has_match;
+ REQUIRE(std::regex_search(gcode, has_match, infill_regex));
+ }
+ THEN("Perimeters are emitted.") {
+ std::smatch has_match;
+ REQUIRE(std::regex_search(gcode, has_match, perimeters_regex));
+ }
+ THEN("Skirt is emitted.") {
+ std::smatch has_match;
+ REQUIRE(std::regex_search(gcode, has_match, skirt_regex));
+ }
+ THEN("final Z height is 20mm") {
+ double final_z = 0.0;
+ GCodeReader reader;
+ reader.apply_config(print->config());
+ reader.parse_buffer(gcode, [&final_z] (GCodeReader& self, const GCodeReader::GCodeLine& line) {
+ final_z = std::max<double>(final_z, static_cast<double>(self.z())); // record the highest Z point we reach
+ });
+ REQUIRE(final_z == Approx(20.));
+ }
+ }
+ WHEN("output is executed with complete objects and two differently-sized meshes") {
+ Slic3r::Model model;
+ config.set_deserialize("first_layer_extrusion_width", "0");
+ config.set_deserialize("first_layer_height", "0.3");
+ config.set_deserialize("layer_height", "0.2");
+ config.set_deserialize("support_material", "0");
+ config.set_deserialize("raft_layers", "0");
+ config.set_deserialize("complete_objects", "1");
+ config.set_deserialize("gcode_comments", "1");
+ config.set_deserialize("between_objects_gcode", "; between-object-gcode");
+ std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({TestMesh::cube_20x20x20,TestMesh::cube_20x20x20}, model, config);
+ std::string gcode = Slic3r::Test::gcode(print);
+ THEN("Some text output is generated.") {
+ REQUIRE(gcode.size() > 0);
+ }
+ THEN("Infill is emitted.") {
+ std::smatch has_match;
+ REQUIRE(std::regex_search(gcode, has_match, infill_regex));
+ }
+ THEN("Perimeters are emitted.") {
+ std::smatch has_match;
+ REQUIRE(std::regex_search(gcode, has_match, perimeters_regex));
+ }
+ THEN("Skirt is emitted.") {
+ std::smatch has_match;
+ REQUIRE(std::regex_search(gcode, has_match, skirt_regex));
+ }
+ THEN("Between-object-gcode is emitted.") {
+ REQUIRE(gcode.find("; between-object-gcode") != std::string::npos);
+ }
+ THEN("final Z height is 20.1mm") {
+ double final_z = 0.0;
+ GCodeReader reader;
+ reader.apply_config(print->config());
+ reader.parse_buffer(gcode, [&final_z] (GCodeReader& self, const GCodeReader::GCodeLine& line) {
+ final_z = std::max(final_z, static_cast<double>(self.z())); // record the highest Z point we reach
+ });
+ REQUIRE(final_z == Approx(20.1));
+ }
+ THEN("Z height resets on object change") {
+ double final_z = 0.0;
+ bool reset = false;
+ GCodeReader reader;
+ reader.apply_config(print->config());
+ reader.parse_buffer(gcode, [&final_z, &reset] (GCodeReader& self, const GCodeReader::GCodeLine& line) {
+ if (final_z > 0 && std::abs(self.z() - 0.3) < 0.01 ) { // saw higher Z before this, now it's lower
+ reset = true;
+ } else {
+ final_z = std::max(final_z, static_cast<double>(self.z())); // record the highest Z point we reach
+ }
+ });
+ REQUIRE(reset == true);
+ }
+ THEN("Shorter object is printed before taller object.") {
+ double final_z = 0.0;
+ bool reset = false;
+ GCodeReader reader;
+ reader.apply_config(print->config());
+ reader.parse_buffer(gcode, [&final_z, &reset] (GCodeReader& self, const GCodeReader::GCodeLine& line) {
+ if (final_z > 0 && std::abs(self.z() - 0.3) < 0.01 ) {
+ reset = (final_z > 20.0);
+ } else {
+ final_z = std::max(final_z, static_cast<double>(self.z())); // record the highest Z point we reach
+ }
+ });
+ REQUIRE(reset == true);
+ }
+ }
+ WHEN("the output is executed with support material") {
+ Slic3r::Model model;
+ config.set_deserialize("first_layer_extrusion_width", "0");
+ config.set_deserialize("support_material", "1");
+ config.set_deserialize("raft_layers", "3");
+ config.set_deserialize("gcode_comments", "1");
+ std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config);
+ std::string gcode = Slic3r::Test::gcode(print);
+ THEN("Some text output is generated.") {
+ REQUIRE(gcode.size() > 0);
+ }
+ THEN("Exported text contains extrusion statistics.") {
+ REQUIRE(gcode.find("; external perimeters extrusion width") != std::string::npos);
+ REQUIRE(gcode.find("; perimeters extrusion width") != std::string::npos);
+ REQUIRE(gcode.find("; infill extrusion width") != std::string::npos);
+ REQUIRE(gcode.find("; solid infill extrusion width") != std::string::npos);
+ REQUIRE(gcode.find("; top infill extrusion width") != std::string::npos);
+ REQUIRE(gcode.find("; support material extrusion width") != std::string::npos);
+ REQUIRE(gcode.find("; first layer extrusion width") == std::string::npos);
+ }
+ THEN("Raft is emitted.") {
+ REQUIRE(gcode.find("; raft") != std::string::npos);
+ }
+ }
+ WHEN("the output is executed with a separate first layer extrusion width") {
+ Slic3r::Model model;
+ config.set_deserialize("first_layer_extrusion_width", "0.5");
+ std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config);
+ std::string gcode = Slic3r::Test::gcode(print);
+ THEN("Some text output is generated.") {
+ REQUIRE(gcode.size() > 0);
+ }
+ THEN("Exported text contains extrusion statistics.") {
+ REQUIRE(gcode.find("; external perimeters extrusion width") != std::string::npos);
+ REQUIRE(gcode.find("; perimeters extrusion width") != std::string::npos);
+ REQUIRE(gcode.find("; infill extrusion width") != std::string::npos);
+ REQUIRE(gcode.find("; solid infill extrusion width") != std::string::npos);
+ REQUIRE(gcode.find("; top infill extrusion width") != std::string::npos);
+ REQUIRE(gcode.find("; support material extrusion width") == std::string::npos);
+ REQUIRE(gcode.find("; first layer extrusion width") != std::string::npos);
+ }
+ }
+ WHEN("Cooling is enabled and the fan is disabled.") {
+ config.set_deserialize("cooling", "1");
+ config.set_deserialize("disable_fan_first_layers", "5");
+ Slic3r::Model model;
+ std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config);
+ std::string gcode = Slic3r::Test::gcode(print);
+ THEN("GCode to disable fan is emitted."){
+ REQUIRE(gcode.find("M107") != std::string::npos);
+ }
+ }
+ WHEN("end_gcode exists with layer_num and layer_z") {
+ config.set_deserialize("end_gcode", "; Layer_num [layer_num]\n; Layer_z [layer_z]");
+ config.set_deserialize("layer_height", "0.1");
+ config.set_deserialize("first_layer_height", "0.1");
+
+ Slic3r::Model model;
+ std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config);
+ std::string gcode = Slic3r::Test::gcode(print);
+ THEN("layer_num and layer_z are processed in the end gcode") {
+ REQUIRE(gcode.find("; Layer_num 199") != std::string::npos);
+ REQUIRE(gcode.find("; Layer_z 20") != std::string::npos);
+ }
+ }
+ WHEN("current_extruder exists in start_gcode") {
+ config.set_deserialize("start_gcode", "; Extruder [current_extruder]");
+ {
+ Slic3r::Model model;
+ std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config);
+ std::string gcode = Slic3r::Test::gcode(print);
+ THEN("current_extruder is processed in the start gcode and set for first extruder") {
+ REQUIRE(gcode.find("; Extruder 0") != std::string::npos);
+ }
+ }
+ config.set_num_extruders(4);
+ config.set_deserialize("infill_extruder", "2");
+ config.set_deserialize("solid_infill_extruder", "2");
+ config.set_deserialize("perimeter_extruder", "2");
+ config.set_deserialize("support_material_extruder", "2");
+ config.set_deserialize("support_material_interface_extruder", "2");
+ {
+ Slic3r::Model model;
+ std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config);
+ std::string gcode = Slic3r::Test::gcode(print);
+ THEN("current_extruder is processed in the start gcode and set for second extruder") {
+ REQUIRE(gcode.find("; Extruder 1") != std::string::npos);
+ }
+ }
+ }
+
+ WHEN("layer_num represents the layer's index from z=0") {
+ config.set_deserialize("complete_objects", "1");
+ config.set_deserialize("gcode_comments", "1");
+ config.set_deserialize("layer_gcode", ";Layer:[layer_num] ([layer_z] mm)");
+ config.set_deserialize("layer_height", "1.0");
+ config.set_deserialize("first_layer_height", "1.0");
+
+ Slic3r::Model model;
+ std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({TestMesh::cube_20x20x20,TestMesh::cube_20x20x20}, model, config);
+ std::string gcode = Slic3r::Test::gcode(print);
+ // End of the 1st object.
+ size_t pos = gcode.find(";Layer:19 ");
+ THEN("First and second object last layer is emitted") {
+ // First object
+ REQUIRE(pos != std::string::npos);
+ pos += 10;
+ REQUIRE(pos < gcode.size());
+ double z = 0;
+ REQUIRE((sscanf(gcode.data() + pos, "(%lf mm)", &z) == 1));
+ REQUIRE(z == Approx(20.));
+ // Second object
+ pos = gcode.find(";Layer:39 ", pos);
+ REQUIRE(pos != std::string::npos);
+ pos += 10;
+ REQUIRE(pos < gcode.size());
+ REQUIRE((sscanf(gcode.data() + pos, "(%lf mm)", &z) == 1));
+ REQUIRE(z == Approx(20.));
+ }
+ }
+ }
+}
diff --git a/tests/fff_print/test_printobject.cpp b/tests/fff_print/test_printobject.cpp
new file mode 100644
index 000000000..27f3aff54
--- /dev/null
+++ b/tests/fff_print/test_printobject.cpp
@@ -0,0 +1,86 @@
+#include <catch2/catch.hpp>
+
+#include "libslic3r/libslic3r.h"
+#include "libslic3r/Print.hpp"
+
+#include "test_data.hpp"
+
+using namespace Slic3r;
+using namespace Slic3r::Test;
+
+SCENARIO("PrintObject: object layer heights", "[PrintObject]") {
+ GIVEN("20mm cube and default initial config, initial layer height of 2mm") {
+ Slic3r::DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
+ TestMesh m = TestMesh::cube_20x20x20;
+ Slic3r::Model model;
+
+ config.set_deserialize("first_layer_height", "2");
+
+ WHEN("generate_object_layers() is called for 2mm layer heights and nozzle diameter of 3mm") {
+ config.opt_float("nozzle_diameter", 0) = 3;
+ config.opt_float("layer_height") = 2.0;
+ std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({m}, model, config);
+ print->process();
+ const std::vector<Slic3r::Layer*> &layers = print->objects().front()->layers();
+ THEN("The output vector has 10 entries") {
+ REQUIRE(layers.size() == 10);
+ }
+ AND_THEN("Each layer is approximately 2mm above the previous Z") {
+ coordf_t last = 0.0;
+ for (size_t i = 0; i < layers.size(); ++ i) {
+ REQUIRE((layers[i]->print_z - last) == Approx(2.0));
+ last = layers[i]->print_z;
+ }
+ }
+ }
+ WHEN("generate_object_layers() is called for 10mm layer heights and nozzle diameter of 11mm") {
+ config.opt_float("nozzle_diameter", 0) = 11;
+ config.opt_float("layer_height") = 10;
+ std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({m}, model, config);
+ print->process();
+ const std::vector<Slic3r::Layer*> &layers = print->objects().front()->layers();
+ THEN("The output vector has 3 entries") {
+ REQUIRE(layers.size() == 3);
+ }
+ AND_THEN("Layer 0 is at 2mm") {
+ REQUIRE(layers.front()->print_z == Approx(2.0));
+ }
+ AND_THEN("Layer 1 is at 12mm") {
+ REQUIRE(layers[1]->print_z == Approx(12.0));
+ }
+ }
+ WHEN("generate_object_layers() is called for 15mm layer heights and nozzle diameter of 16mm") {
+ config.opt_float("nozzle_diameter", 0) = 16;
+ config.opt_float("layer_height") = 15.0;
+ std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({m}, model, config);
+ print->process();
+ const std::vector<Slic3r::Layer*> &layers = print->objects().front()->layers();
+ THEN("The output vector has 2 entries") {
+ REQUIRE(layers.size() == 2);
+ }
+ AND_THEN("Layer 0 is at 2mm") {
+ REQUIRE(layers[0]->print_z == Approx(2.0));
+ }
+ AND_THEN("Layer 1 is at 17mm") {
+ REQUIRE(layers[1]->print_z == Approx(17.0));
+ }
+ }
+#if 0
+ WHEN("generate_object_layers() is called for 15mm layer heights and nozzle diameter of 5mm") {
+ config.opt_float("nozzle_diameter", 0) = 5;
+ config.opt_float("layer_height") = 15.0;
+ std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({m}, model, config);
+ print->process();
+ const std::vector<Slic3r::Layer*> &layers = print->objects().front()->layers();
+ THEN("The layer height is limited to 5mm.") {
+ CHECK(layers.size() == 5);
+ coordf_t last = 2.0;
+ for (size_t i = 1; i < layers.size(); i++) {
+ REQUIRE((layers[i]->print_z - last) == Approx(5.0));
+ last = layers[i]->print_z;
+ }
+ }
+ }
+#endif
+ }
+}
diff --git a/tests/fff_print/test_skirt_brim.cpp b/tests/fff_print/test_skirt_brim.cpp
new file mode 100644
index 000000000..d5b36dfda
--- /dev/null
+++ b/tests/fff_print/test_skirt_brim.cpp
@@ -0,0 +1,263 @@
+#include <catch2/catch.hpp>
+
+#include "libslic3r/GCodeReader.hpp"
+#include "libslic3r/Config.hpp"
+#include "libslic3r/Geometry.hpp"
+
+#include <boost/algorithm/string.hpp>
+
+#include "test_data.hpp" // get access to init_print, etc
+
+using namespace Slic3r::Test;
+using namespace Slic3r;
+
+/// Helper method to find the tool used for the brim (always the first extrusion)
+int get_brim_tool(std::string &gcode, Slic3r::GCodeReader& parser) {
+ int brim_tool = -1;
+ int tool = -1;
+
+ parser.parse_buffer(gcode, [&tool, &brim_tool] (Slic3r::GCodeReader& self, const Slic3r::GCodeReader::GCodeLine& line)
+ {
+ // if the command is a T command, set the the current tool
+ if (boost::starts_with(line.cmd(), "T")) {
+ tool = atoi(line.cmd().data() + 1);
+ } else if (line.cmd() == "G1" && line.extruding(self) && line.dist_XY(self) > 0 && brim_tool < 0) {
+ brim_tool = tool;
+ }
+ });
+
+ return brim_tool;
+}
+
+TEST_CASE("Skirt height is honored") {
+ DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
+ config.opt_int("skirts") = 1;
+ config.opt_int("skirt_height") = 5;
+ config.opt_int("perimeters") = 0;
+ config.opt_float("support_material_speed") = 99;
+
+ // avoid altering speeds unexpectedly
+ config.set_deserialize("cooling", "0");
+ config.set_deserialize("first_layer_speed", "100%");
+ double support_speed = config.opt<Slic3r::ConfigOptionFloat>("support_material_speed")->value * MM_PER_MIN;
+
+ std::map<double, bool> layers_with_skirt;
+ std::string gcode;
+ GCodeReader parser;
+ Slic3r::Model model;
+
+ SECTION("printing a single object") {
+ auto print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config);
+ gcode = Slic3r::Test::gcode(print);
+ }
+
+ SECTION("printing multiple objects") {
+ auto print = Slic3r::Test::init_print({TestMesh::cube_20x20x20, TestMesh::cube_20x20x20}, model, config);
+ gcode = Slic3r::Test::gcode(print);
+ }
+ parser.parse_buffer(gcode, [&layers_with_skirt, &support_speed] (Slic3r::GCodeReader& self, const Slic3r::GCodeReader::GCodeLine& line)
+ {
+ if (line.extruding(self) && self.f() == Approx(support_speed)) {
+ layers_with_skirt[self.z()] = 1;
+ }
+ });
+
+ REQUIRE(layers_with_skirt.size() == (size_t)config.opt_int("skirt_height"));
+}
+
+SCENARIO("Original Slic3r Skirt/Brim tests", "[!mayfail]") {
+ Slic3r::GCodeReader parser;
+ Slic3r::Model model;
+ std::string gcode;
+ GIVEN("A default configuration") {
+ DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
+ config.set_num_extruders(4);
+ config.opt_float("support_material_speed") = 99;
+ config.set_deserialize("first_layer_height", "0.3");
+ config.set_deserialize("gcode_comments", "1");
+
+ // avoid altering speeds unexpectedly
+ config.set_deserialize("cooling", "0");
+ config.set_deserialize("first_layer_speed", "100%");
+ // remove noise from top/solid layers
+ config.opt_int("top_solid_layers") = 0;
+ config.opt_int("bottom_solid_layers") = 1;
+
+ WHEN("Brim width is set to 5") {
+ config.opt_int("perimeters") = 0;
+ config.opt_int("skirts") = 0;
+ config.opt_float("brim_width") = 5;
+ THEN("Brim is generated") {
+ auto print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config);
+ gcode = Slic3r::Test::gcode(print);
+ bool brim_generated = false;
+ double support_speed = config.opt<Slic3r::ConfigOptionFloat>("support_material_speed")->value * MM_PER_MIN;
+ parser.parse_buffer(gcode, [&brim_generated, support_speed] (Slic3r::GCodeReader& self, const Slic3r::GCodeReader::GCodeLine& line)
+ {
+ if (self.z() == Approx(0.3) || line.new_Z(self) == Approx(0.3)) {
+ if (line.extruding(self) && self.f() == Approx(support_speed)) {
+ brim_generated = true;
+ }
+ }
+ });
+ REQUIRE(brim_generated);
+ }
+ }
+
+ WHEN("Skirt area is smaller than the brim") {
+ config.opt_int("skirts") = 1;
+ config.opt_float("brim_width") = 10;
+ auto print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config);
+ THEN("Gcode generates") {
+ REQUIRE(! Slic3r::Test::gcode(print).empty());
+ }
+ }
+
+ WHEN("Skirt height is 0 and skirts > 0") {
+ config.opt_int("skirts") = 2;
+ config.opt_int("skirt_height") = 0;
+
+ auto print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config);
+ THEN("Gcode generates") {
+ REQUIRE(! Slic3r::Test::gcode(print).empty());
+ }
+ }
+
+ WHEN("Perimeter extruder = 2 and support extruders = 3") {
+ config.opt_int("skirts") = 0;
+ config.opt_float("brim_width") = 5;
+ config.opt_int("perimeter_extruder") = 2;
+ config.opt_int("support_material_extruder") = 3;
+ THEN("Brim is printed with the extruder used for the perimeters of first object") {
+ auto print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config);
+ gcode = Slic3r::Test::gcode(print);
+ int tool = get_brim_tool(gcode, parser);
+ REQUIRE(tool == config.opt_int("perimeter_extruder") - 1);
+ }
+ }
+ WHEN("Perimeter extruder = 2, support extruders = 3, raft is enabled") {
+ config.opt_int("skirts") = 0;
+ config.opt_float("brim_width") = 5;
+ config.opt_int("perimeter_extruder") = 2;
+ config.opt_int("support_material_extruder") = 3;
+ config.opt_int("raft_layers") = 1;
+ THEN("brim is printed with same extruder as skirt") {
+ auto print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config);
+ gcode = Slic3r::Test::gcode(print);
+ int tool = get_brim_tool(gcode, parser);
+ REQUIRE(tool == config.opt_int("support_material_extruder") - 1);
+ }
+ }
+ WHEN("brim width to 1 with layer_width of 0.5") {
+ config.opt_int("skirts") = 0;
+ config.set_deserialize("first_layer_extrusion_width", "0.5");
+ config.opt_float("brim_width") = 1;
+
+ THEN("2 brim lines") {
+ Slic3r::Model model;
+ auto print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config);
+ print->process();
+ REQUIRE(print->brim().entities.size() == 2);
+ }
+ }
+
+#if 0
+ WHEN("brim ears on a square") {
+ config.opt_int("skirts") = 0);
+ config.set_deserialize("first_layer_extrusion_width", "0.5");
+ config.opt_float("brim_width") = 1;
+ config.set("brim_ears", true);
+ config.set("brim_ears_max_angle", 91);
+
+ Slic3r::Model model;
+ auto print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config);
+ print->process();
+
+ THEN("Four brim ears") {
+ REQUIRE(print->brim.size() == 4);
+ }
+ }
+
+ WHEN("brim ears on a square but with a too small max angle") {
+ config.set("skirts", 0);
+ config.set("first_layer_extrusion_width", 0.5);
+ config.set("brim_width", 1);
+ config.set("brim_ears", true);
+ config.set("brim_ears_max_angle", 89);
+
+ THEN("no brim") {
+ Slic3r::Model model;
+ auto print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config);
+ print->process();
+ REQUIRE(print->brim.size() == 0);
+ }
+ }
+#endif
+
+ WHEN("Object is plated with overhang support and a brim") {
+ config.opt_float("layer_height") = 0.4;
+ config.set_deserialize("first_layer_height", "0.4");
+ config.opt_int("skirts") = 1;
+ config.opt_float("skirt_distance") = 0;
+ config.opt_float("support_material_speed") = 99;
+ config.opt_int("perimeter_extruder") = 1;
+ config.opt_int("support_material_extruder") = 2;
+ config.opt_int("infill_extruder") = 3; // ensure that a tool command gets emitted.
+ config.set_deserialize("cooling", "0"); // to prevent speeds to be altered
+ config.set_deserialize("first_layer_speed", "100%"); // to prevent speeds to be altered
+
+ Slic3r::Model model;
+ auto print = Slic3r::Test::init_print({TestMesh::overhang}, model, config);
+ print->process();
+
+ // config.set("support_material", true); // to prevent speeds to be altered
+
+ THEN("skirt length is large enough to contain object with support") {
+ CHECK(config.opt_bool("support_material")); // test is not valid if support material is off
+ double skirt_length = 0.0;
+ Points extrusion_points;
+ int tool = -1;
+
+ auto print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config);
+ std::string gcode = Slic3r::Test::gcode(print);
+
+ double support_speed = config.opt<ConfigOptionFloat>("support_material_speed")->value * MM_PER_MIN;
+ parser.parse_buffer(gcode, [config, &extrusion_points, &tool, &skirt_length, support_speed] (Slic3r::GCodeReader& self, const Slic3r::GCodeReader::GCodeLine& line)
+ {
+ // std::cerr << line.cmd() << "\n";
+ if (boost::starts_with(line.cmd(), "T")) {
+ tool = atoi(line.cmd().data() + 1);
+ } else if (self.z() == Approx(config.opt<ConfigOptionFloat>("first_layer_height")->value)) {
+ // on first layer
+ if (line.extruding(self) && line.dist_XY(self) > 0) {
+ float speed = ( self.f() > 0 ? self.f() : line.new_F(self));
+ // std::cerr << "Tool " << tool << "\n";
+ if (speed == Approx(support_speed) && tool == config.opt_int("perimeter_extruder") - 1) {
+ // Skirt uses first material extruder, support material speed.
+ skirt_length += line.dist_XY(self);
+ } else {
+ extrusion_points.push_back(Slic3r::Point::new_scale(line.new_X(self), line.new_Y(self)));
+ }
+ }
+ }
+
+ if (self.z() == Approx(0.3) || line.new_Z(self) == Approx(0.3)) {
+ if (line.extruding(self) && self.f() == Approx(support_speed)) {
+ }
+ }
+ });
+ Slic3r::Polygon convex_hull = Slic3r::Geometry::convex_hull(extrusion_points);
+ double hull_perimeter = unscale<double>(convex_hull.split_at_first_point().length());
+ REQUIRE(skirt_length > hull_perimeter);
+ }
+ }
+ WHEN("Large minimum skirt length is used.") {
+ config.opt_float("min_skirt_length") = 20;
+ Slic3r::Model model;
+ auto print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config);
+ THEN("Gcode generation doesn't crash") {
+ REQUIRE(! Slic3r::Test::gcode(print).empty());
+ }
+ }
+ }
+}
diff --git a/tests/fff_print/test_trianglemesh.cpp b/tests/fff_print/test_trianglemesh.cpp
new file mode 100644
index 000000000..77c522538
--- /dev/null
+++ b/tests/fff_print/test_trianglemesh.cpp
@@ -0,0 +1,433 @@
+#include <catch2/catch.hpp>
+
+#include "libslic3r/TriangleMesh.hpp"
+#include "libslic3r/Point.hpp"
+#include "libslic3r/Config.hpp"
+#include "libslic3r/Model.hpp"
+#include "libslic3r/libslic3r.h"
+
+#include <algorithm>
+#include <future>
+#include <chrono>
+
+//#include "test_options.hpp"
+#include "test_data.hpp"
+
+using namespace Slic3r;
+using namespace std;
+
+SCENARIO( "TriangleMesh: Basic mesh statistics") {
+ GIVEN( "A 20mm cube, built from constexpr std::array" ) {
+ std::vector<Vec3d> vertices { Vec3d(20,20,0), Vec3d(20,0,0), Vec3d(0,0,0), Vec3d(0,20,0), Vec3d(20,20,20), Vec3d(0,20,20), Vec3d(0,0,20), Vec3d(20,0,20) };
+ std::vector<Vec3crd> facets { Vec3crd(0,1,2), Vec3crd(0,2,3), Vec3crd(4,5,6), Vec3crd(4,6,7), Vec3crd(0,4,7), Vec3crd(0,7,1), Vec3crd(1,7,6), Vec3crd(1,6,2), Vec3crd(2,6,5), Vec3crd(2,5,3), Vec3crd(4,0,3), Vec3crd(4,3,5) };
+ TriangleMesh cube(vertices, facets);
+ cube.repair();
+
+ THEN( "Volume is appropriate for 20mm square cube.") {
+ REQUIRE(abs(cube.volume() - 20.0*20.0*20.0) < 1e-2);
+ }
+
+ THEN( "Vertices array matches input.") {
+ for (size_t i = 0U; i < cube.its.vertices.size(); i++) {
+ REQUIRE(cube.its.vertices.at(i) == vertices.at(i).cast<float>());
+ }
+ for (size_t i = 0U; i < vertices.size(); i++) {
+ REQUIRE(vertices.at(i).cast<float>() == cube.its.vertices.at(i));
+ }
+ }
+ THEN( "Vertex count matches vertex array size.") {
+ REQUIRE(cube.facets_count() == facets.size());
+ }
+
+ THEN( "Facet array matches input.") {
+ for (size_t i = 0U; i < cube.its.indices.size(); i++) {
+ REQUIRE(cube.its.indices.at(i) == facets.at(i));
+ }
+
+ for (size_t i = 0U; i < facets.size(); i++) {
+ REQUIRE(facets.at(i) == cube.its.indices.at(i));
+ }
+ }
+ THEN( "Facet count matches facet array size.") {
+ REQUIRE(cube.facets_count() == facets.size());
+ }
+
+#if 0
+ THEN( "Number of normals is equal to the number of facets.") {
+ REQUIRE(cube.normals().size() == facets.size());
+ }
+#endif
+
+ THEN( "center() returns the center of the object.") {
+ REQUIRE(cube.center() == Vec3d(10.0,10.0,10.0));
+ }
+
+ THEN( "Size of cube is (20,20,20)") {
+ REQUIRE(cube.size() == Vec3d(20,20,20));
+ }
+
+ }
+ GIVEN( "A 20mm cube with one corner on the origin") {
+ const std::vector<Vec3d> vertices { Vec3d(20,20,0), Vec3d(20,0,0), Vec3d(0,0,0), Vec3d(0,20,0), Vec3d(20,20,20), Vec3d(0,20,20), Vec3d(0,0,20), Vec3d(20,0,20) };
+ const std::vector<Vec3crd> facets { Vec3crd(0,1,2), Vec3crd(0,2,3), Vec3crd(4,5,6), Vec3crd(4,6,7), Vec3crd(0,4,7), Vec3crd(0,7,1), Vec3crd(1,7,6), Vec3crd(1,6,2), Vec3crd(2,6,5), Vec3crd(2,5,3), Vec3crd(4,0,3), Vec3crd(4,3,5) };
+
+ TriangleMesh cube(vertices, facets);
+ cube.repair();
+
+ THEN( "Volume is appropriate for 20mm square cube.") {
+ REQUIRE(abs(cube.volume() - 20.0*20.0*20.0) < 1e-2);
+ }
+
+ THEN( "Vertices array matches input.") {
+ for (size_t i = 0U; i < cube.its.vertices.size(); i++) {
+ REQUIRE(cube.its.vertices.at(i) == vertices.at(i).cast<float>());
+ }
+ for (size_t i = 0U; i < vertices.size(); i++) {
+ REQUIRE(vertices.at(i).cast<float>() == cube.its.vertices.at(i));
+ }
+ }
+ THEN( "Vertex count matches vertex array size.") {
+ REQUIRE(cube.facets_count() == facets.size());
+ }
+
+ THEN( "Facet array matches input.") {
+ for (size_t i = 0U; i < cube.its.indices.size(); i++) {
+ REQUIRE(cube.its.indices.at(i) == facets.at(i));
+ }
+
+ for (size_t i = 0U; i < facets.size(); i++) {
+ REQUIRE(facets.at(i) == cube.its.indices.at(i));
+ }
+ }
+ THEN( "Facet count matches facet array size.") {
+ REQUIRE(cube.facets_count() == facets.size());
+ }
+
+#if 0
+ THEN( "Number of normals is equal to the number of facets.") {
+ REQUIRE(cube.normals().size() == facets.size());
+ }
+#endif
+
+ THEN( "center() returns the center of the object.") {
+ REQUIRE(cube.center() == Vec3d(10.0,10.0,10.0));
+ }
+
+ THEN( "Size of cube is (20,20,20)") {
+ REQUIRE(cube.size() == Vec3d(20,20,20));
+ }
+ }
+}
+
+SCENARIO( "TriangleMesh: Transformation functions affect mesh as expected.") {
+ GIVEN( "A 20mm cube with one corner on the origin") {
+ const std::vector<Vec3d> vertices { Vec3d(20,20,0), Vec3d(20,0,0), Vec3d(0,0,0), Vec3d(0,20,0), Vec3d(20,20,20), Vec3d(0,20,20), Vec3d(0,0,20), Vec3d(20,0,20) };
+ const std::vector<Vec3crd> facets { Vec3crd(0,1,2), Vec3crd(0,2,3), Vec3crd(4,5,6), Vec3crd(4,6,7), Vec3crd(0,4,7), Vec3crd(0,7,1), Vec3crd(1,7,6), Vec3crd(1,6,2), Vec3crd(2,6,5), Vec3crd(2,5,3), Vec3crd(4,0,3), Vec3crd(4,3,5) };
+ TriangleMesh cube(vertices, facets);
+ cube.repair();
+
+ WHEN( "The cube is scaled 200% uniformly") {
+ cube.scale(2.0);
+ THEN( "The volume is equivalent to 40x40x40 (all dimensions increased by 200%") {
+ REQUIRE(abs(cube.volume() - 40.0*40.0*40.0) < 1e-2);
+ }
+ }
+ WHEN( "The resulting cube is scaled 200% in the X direction") {
+ cube.scale(Vec3d(2.0, 1, 1));
+ THEN( "The volume is doubled.") {
+ REQUIRE(abs(cube.volume() - 2*20.0*20.0*20.0) < 1e-2);
+ }
+ THEN( "The X coordinate size is 200%.") {
+ REQUIRE(cube.its.vertices.at(0).x() == 40.0);
+ }
+ }
+
+ WHEN( "The cube is scaled 25% in the X direction") {
+ cube.scale(Vec3d(0.25, 1, 1));
+ THEN( "The volume is 25% of the previous volume.") {
+ REQUIRE(abs(cube.volume() - 0.25*20.0*20.0*20.0) < 1e-2);
+ }
+ THEN( "The X coordinate size is 25% from previous.") {
+ REQUIRE(cube.its.vertices.at(0).x() == 5.0);
+ }
+ }
+
+ WHEN( "The cube is rotated 45 degrees.") {
+ cube.rotate_z(float(M_PI / 4.));
+ THEN( "The X component of the size is sqrt(2)*20") {
+ REQUIRE(abs(cube.size().x() - sqrt(2.0)*20) < 1e-2);
+ }
+ }
+
+ WHEN( "The cube is translated (5, 10, 0) units with a Vec3f ") {
+ cube.translate(Vec3f(5.0, 10.0, 0.0));
+ THEN( "The first vertex is located at 25, 30, 0") {
+ REQUIRE(cube.its.vertices.at(0) == Vec3f(25.0, 30.0, 0.0));
+ }
+ }
+
+ WHEN( "The cube is translated (5, 10, 0) units with 3 doubles") {
+ cube.translate(5.0, 10.0, 0.0);
+ THEN( "The first vertex is located at 25, 30, 0") {
+ REQUIRE(cube.its.vertices.at(0) == Vec3f(25.0, 30.0, 0.0));
+ }
+ }
+ WHEN( "The cube is translated (5, 10, 0) units and then aligned to origin") {
+ cube.translate(5.0, 10.0, 0.0);
+ cube.align_to_origin();
+ THEN( "The third vertex is located at 0,0,0") {
+ REQUIRE(cube.its.vertices.at(2) == Vec3f(0.0, 0.0, 0.0));
+ }
+ }
+ }
+}
+
+SCENARIO( "TriangleMesh: slice behavior.") {
+ GIVEN( "A 20mm cube with one corner on the origin") {
+ const std::vector<Vec3d> vertices { Vec3d(20,20,0), Vec3d(20,0,0), Vec3d(0,0,0), Vec3d(0,20,0), Vec3d(20,20,20), Vec3d(0,20,20), Vec3d(0,0,20), Vec3d(20,0,20) };
+ const std::vector<Vec3crd> facets { Vec3crd(0,1,2), Vec3crd(0,2,3), Vec3crd(4,5,6), Vec3crd(4,6,7), Vec3crd(0,4,7), Vec3crd(0,7,1), Vec3crd(1,7,6), Vec3crd(1,6,2), Vec3crd(2,6,5), Vec3crd(2,5,3), Vec3crd(4,0,3), Vec3crd(4,3,5) };
+ TriangleMesh cube(vertices, facets);
+ cube.repair();
+
+ WHEN("Cube is sliced with z = [0+EPSILON,2,4,8,6,8,10,12,14,16,18,20]") {
+ std::vector<double> z { 0+EPSILON,2,4,8,6,8,10,12,14,16,18,20 };
+ std::vector<ExPolygons> result = cube.slice(z);
+ THEN( "The correct number of polygons are returned per layer.") {
+ for (size_t i = 0U; i < z.size(); i++) {
+ REQUIRE(result.at(i).size() == 1);
+ }
+ }
+ THEN( "The area of the returned polygons is correct.") {
+ for (size_t i = 0U; i < z.size(); i++) {
+ REQUIRE(result.at(i).at(0).area() == 20.0*20/(std::pow(SCALING_FACTOR,2)));
+ }
+ }
+ }
+ }
+ GIVEN( "A STL with an irregular shape.") {
+ const std::vector<Vec3d> vertices {Vec3d(0,0,0),Vec3d(0,0,20),Vec3d(0,5,0),Vec3d(0,5,20),Vec3d(50,0,0),Vec3d(50,0,20),Vec3d(15,5,0),Vec3d(35,5,0),Vec3d(15,20,0),Vec3d(50,5,0),Vec3d(35,20,0),Vec3d(15,5,10),Vec3d(50,5,20),Vec3d(35,5,10),Vec3d(35,20,10),Vec3d(15,20,10)};
+ const std::vector<Vec3crd> facets {Vec3crd(0,1,2),Vec3crd(2,1,3),Vec3crd(1,0,4),Vec3crd(5,1,4),Vec3crd(0,2,4),Vec3crd(4,2,6),Vec3crd(7,6,8),Vec3crd(4,6,7),Vec3crd(9,4,7),Vec3crd(7,8,10),Vec3crd(2,3,6),Vec3crd(11,3,12),Vec3crd(7,12,9),Vec3crd(13,12,7),Vec3crd(6,3,11),Vec3crd(11,12,13),Vec3crd(3,1,5),Vec3crd(12,3,5),Vec3crd(5,4,9),Vec3crd(12,5,9),Vec3crd(13,7,10),Vec3crd(14,13,10),Vec3crd(8,15,10),Vec3crd(10,15,14),Vec3crd(6,11,8),Vec3crd(8,11,15),Vec3crd(15,11,13),Vec3crd(14,15,13)};
+
+ TriangleMesh cube(vertices, facets);
+ cube.repair();
+ WHEN(" a top tangent plane is sliced") {
+ std::vector<ExPolygons> slices = cube.slice({5.0, 10.0});
+ THEN( "its area is included") {
+ REQUIRE(slices.at(0).at(0).area() > 0);
+ REQUIRE(slices.at(1).at(0).area() > 0);
+ }
+ }
+ WHEN(" a model that has been transformed is sliced") {
+ cube.mirror_z();
+ std::vector<ExPolygons> slices = cube.slice({-5.0, -10.0});
+ THEN( "it is sliced properly (mirrored bottom plane area is included)") {
+ REQUIRE(slices.at(0).at(0).area() > 0);
+ REQUIRE(slices.at(1).at(0).area() > 0);
+ }
+ }
+ }
+}
+
+SCENARIO( "make_xxx functions produce meshes.") {
+ GIVEN("make_cube() function") {
+ WHEN("make_cube() is called with arguments 20,20,20") {
+ TriangleMesh cube = make_cube(20,20,20);
+ THEN("The resulting mesh has one and only one vertex at 0,0,0") {
+ const std::vector<Vec3f> &verts = cube.its.vertices;
+ REQUIRE(std::count_if(verts.begin(), verts.end(), [](const Vec3f& t) { return t.x() == 0 && t.y() == 0 && t.z() == 0; } ) == 1);
+ }
+ THEN("The mesh volume is 20*20*20") {
+ REQUIRE(abs(cube.volume() - 20.0*20.0*20.0) < 1e-2);
+ }
+ THEN("The resulting mesh is in the repaired state.") {
+ REQUIRE(cube.repaired == true);
+ }
+ THEN("There are 12 facets.") {
+ REQUIRE(cube.its.indices.size() == 12);
+ }
+ }
+ }
+ GIVEN("make_cylinder() function") {
+ WHEN("make_cylinder() is called with arguments 10,10, PI / 3") {
+ TriangleMesh cyl = make_cylinder(10, 10, PI / 243.0);
+ double angle = (2*PI / floor(2*PI / (PI / 243.0)));
+ THEN("The resulting mesh has one and only one vertex at 0,0,0") {
+ const std::vector<Vec3f> &verts = cyl.its.vertices;
+ REQUIRE(std::count_if(verts.begin(), verts.end(), [](const Vec3f& t) { return t.x() == 0 && t.y() == 0 && t.z() == 0; } ) == 1);
+ }
+ THEN("The resulting mesh has one and only one vertex at 0,0,10") {
+ const std::vector<Vec3f> &verts = cyl.its.vertices;
+ REQUIRE(std::count_if(verts.begin(), verts.end(), [](const Vec3f& t) { return t.x() == 0 && t.y() == 0 && t.z() == 10; } ) == 1);
+ }
+ THEN("Resulting mesh has 2 + (2*PI/angle * 2) vertices.") {
+ REQUIRE(cyl.its.vertices.size() == (2 + ((2*PI/angle)*2)));
+ }
+ THEN("Resulting mesh has 2*PI/angle * 4 facets") {
+ REQUIRE(cyl.its.indices.size() == (2*PI/angle)*4);
+ }
+ THEN("The resulting mesh is in the repaired state.") {
+ REQUIRE(cyl.repaired == true);
+ }
+ THEN( "The mesh volume is approximately 10pi * 10^2") {
+ REQUIRE(abs(cyl.volume() - (10.0 * M_PI * std::pow(10,2))) < 1);
+ }
+ }
+ }
+
+ GIVEN("make_sphere() function") {
+ WHEN("make_sphere() is called with arguments 10, PI / 3") {
+ TriangleMesh sph = make_sphere(10, PI / 243.0);
+ double angle = (2.0*PI / floor(2.0*PI / (PI / 243.0)));
+ THEN("Resulting mesh has one point at 0,0,-10 and one at 0,0,10") {
+ const std::vector<stl_vertex> &verts = sph.its.vertices;
+ REQUIRE(std::count_if(verts.begin(), verts.end(), [](const Vec3f& t) { return is_approx(t, Vec3f(0.f, 0.f, 10.f)); } ) == 1);
+ REQUIRE(std::count_if(verts.begin(), verts.end(), [](const Vec3f& t) { return is_approx(t, Vec3f(0.f, 0.f, -10.f)); } ) == 1);
+ }
+ THEN("The resulting mesh is in the repaired state.") {
+ REQUIRE(sph.repaired == true);
+ }
+ THEN( "The mesh volume is approximately 4/3 * pi * 10^3") {
+ REQUIRE(abs(sph.volume() - (4.0/3.0 * M_PI * std::pow(10,3))) < 1); // 1% tolerance?
+ }
+ }
+ }
+}
+
+SCENARIO( "TriangleMesh: split functionality.") {
+ GIVEN( "A 20mm cube with one corner on the origin") {
+ const std::vector<Vec3d> vertices { Vec3d(20,20,0), Vec3d(20,0,0), Vec3d(0,0,0), Vec3d(0,20,0), Vec3d(20,20,20), Vec3d(0,20,20), Vec3d(0,0,20), Vec3d(20,0,20) };
+ const std::vector<Vec3crd> facets { Vec3crd(0,1,2), Vec3crd(0,2,3), Vec3crd(4,5,6), Vec3crd(4,6,7), Vec3crd(0,4,7), Vec3crd(0,7,1), Vec3crd(1,7,6), Vec3crd(1,6,2), Vec3crd(2,6,5), Vec3crd(2,5,3), Vec3crd(4,0,3), Vec3crd(4,3,5) };
+
+ TriangleMesh cube(vertices, facets);
+ cube.repair();
+ WHEN( "The mesh is split into its component parts.") {
+ std::vector<TriangleMesh*> meshes = cube.split();
+ THEN(" The bounding box statistics are propagated to the split copies") {
+ REQUIRE(meshes.size() == 1);
+ REQUIRE((meshes.at(0)->bounding_box() == cube.bounding_box()));
+ }
+ }
+ }
+ GIVEN( "Two 20mm cubes, each with one corner on the origin, merged into a single TriangleMesh") {
+ const std::vector<Vec3d> vertices { Vec3d(20,20,0), Vec3d(20,0,0), Vec3d(0,0,0), Vec3d(0,20,0), Vec3d(20,20,20), Vec3d(0,20,20), Vec3d(0,0,20), Vec3d(20,0,20) };
+ const std::vector<Vec3crd> facets { Vec3crd(0,1,2), Vec3crd(0,2,3), Vec3crd(4,5,6), Vec3crd(4,6,7), Vec3crd(0,4,7), Vec3crd(0,7,1), Vec3crd(1,7,6), Vec3crd(1,6,2), Vec3crd(2,6,5), Vec3crd(2,5,3), Vec3crd(4,0,3), Vec3crd(4,3,5) };
+
+ TriangleMesh cube(vertices, facets);
+ cube.repair();
+ TriangleMesh cube2(vertices, facets);
+ cube2.repair();
+
+ cube.merge(cube2);
+ cube.repair();
+ WHEN( "The combined mesh is split") {
+ std::vector<TriangleMesh*> meshes = cube.split();
+ THEN( "Two meshes are in the output vector.") {
+ REQUIRE(meshes.size() == 2);
+ }
+ }
+ }
+}
+
+SCENARIO( "TriangleMesh: Mesh merge functions") {
+ GIVEN( "Two 20mm cubes, each with one corner on the origin") {
+ const std::vector<Vec3d> vertices { Vec3d(20,20,0), Vec3d(20,0,0), Vec3d(0,0,0), Vec3d(0,20,0), Vec3d(20,20,20), Vec3d(0,20,20), Vec3d(0,0,20), Vec3d(20,0,20) };
+ const std::vector<Vec3crd> facets { Vec3crd(0,1,2), Vec3crd(0,2,3), Vec3crd(4,5,6), Vec3crd(4,6,7), Vec3crd(0,4,7), Vec3crd(0,7,1), Vec3crd(1,7,6), Vec3crd(1,6,2), Vec3crd(2,6,5), Vec3crd(2,5,3), Vec3crd(4,0,3), Vec3crd(4,3,5) };
+
+ TriangleMesh cube(vertices, facets);
+ cube.repair();
+ TriangleMesh cube2(vertices, facets);
+ cube2.repair();
+
+ WHEN( "The two meshes are merged") {
+ cube.merge(cube2);
+ cube.repair();
+ THEN( "There are twice as many facets in the merged mesh as the original.") {
+ REQUIRE(cube.stl.stats.number_of_facets == 2 * cube2.stl.stats.number_of_facets);
+ }
+ }
+ }
+}
+
+SCENARIO( "TriangleMeshSlicer: Cut behavior.") {
+ GIVEN( "A 20mm cube with one corner on the origin") {
+ const std::vector<Vec3d> vertices { Vec3d(20,20,0), Vec3d(20,0,0), Vec3d(0,0,0), Vec3d(0,20,0), Vec3d(20,20,20), Vec3d(0,20,20), Vec3d(0,0,20), Vec3d(20,0,20) };
+ const std::vector<Vec3crd> facets { Vec3crd(0,1,2), Vec3crd(0,2,3), Vec3crd(4,5,6), Vec3crd(4,6,7), Vec3crd(0,4,7), Vec3crd(0,7,1), Vec3crd(1,7,6), Vec3crd(1,6,2), Vec3crd(2,6,5), Vec3crd(2,5,3), Vec3crd(4,0,3), Vec3crd(4,3,5) };
+
+ TriangleMesh cube(vertices, facets);
+ cube.repair();
+ WHEN( "Object is cut at the bottom") {
+ TriangleMesh upper {};
+ TriangleMesh lower {};
+ TriangleMeshSlicer slicer(&cube);
+ slicer.cut(0, &upper, &lower);
+ THEN("Upper mesh has all facets except those belonging to the slicing plane.") {
+ REQUIRE(upper.facets_count() == 12);
+ }
+ THEN("Lower mesh has no facets.") {
+ REQUIRE(lower.facets_count() == 0);
+ }
+ }
+ WHEN( "Object is cut at the center") {
+ TriangleMesh upper {};
+ TriangleMesh lower {};
+ TriangleMeshSlicer slicer(&cube);
+ slicer.cut(10, &upper, &lower);
+ THEN("Upper mesh has 2 external horizontal facets, 3 facets on each side, and 6 facets on the triangulated side (2 + 12 + 6).") {
+ REQUIRE(upper.facets_count() == 2+12+6);
+ }
+ THEN("Lower mesh has 2 external horizontal facets, 3 facets on each side, and 6 facets on the triangulated side (2 + 12 + 6).") {
+ REQUIRE(lower.facets_count() == 2+12+6);
+ }
+ }
+ }
+}
+#ifdef TEST_PERFORMANCE
+TEST_CASE("Regression test for issue #4486 - files take forever to slice") {
+ TriangleMesh mesh;
+ DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
+ mesh.ReadSTLFile(std::string(testfile_dir) + "test_trianglemesh/4486/100_000.stl");
+ mesh.repair();
+
+ config.set("layer_height", 500);
+ config.set("first_layer_height", 250);
+ config.set("nozzle_diameter", 500);
+
+ Slic3r::Model model;
+ auto print = Slic3r::Test::init_print({mesh}, model, config);
+
+ print->status_cb = [] (int ln, const std::string& msg) { Slic3r::Log::info("Print") << ln << " " << msg << "\n";};
+
+ std::future<void> fut = std::async([&print] () { print->process(); });
+ std::chrono::milliseconds span {120000};
+ bool timedout {false};
+ if(fut.wait_for(span) == std::future_status::timeout) {
+ timedout = true;
+ }
+ REQUIRE(timedout == false);
+
+}
+#endif // TEST_PERFORMANCE
+
+#ifdef BUILD_PROFILE
+TEST_CASE("Profile test for issue #4486 - files take forever to slice") {
+ TriangleMesh mesh;
+ DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
+ mesh.ReadSTLFile(std::string(testfile_dir) + "test_trianglemesh/4486/10_000.stl");
+ mesh.repair();
+
+ config.set("layer_height", 500);
+ config.set("first_layer_height", 250);
+ config.set("nozzle_diameter", 500);
+ config.set("fill_density", "5%");
+
+ Slic3r::Model model;
+ auto print = Slic3r::Test::init_print({mesh}, model, config);
+
+ print->status_cb = [] (int ln, const std::string& msg) { Slic3r::Log::info("Print") << ln << " " << msg << "\n";};
+
+ print->process();
+
+ REQUIRE(true);
+
+}
+#endif //BUILD_PROFILE
diff --git a/tests/libnest2d/CMakeLists.txt b/tests/libnest2d/CMakeLists.txt
index ee38280b5..91a2e7852 100644
--- a/tests/libnest2d/CMakeLists.txt
+++ b/tests/libnest2d/CMakeLists.txt
@@ -1,6 +1,7 @@
get_filename_component(_TEST_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
add_executable(${_TEST_NAME}_tests ${_TEST_NAME}_tests_main.cpp printer_parts.cpp printer_parts.hpp)
-target_link_libraries(${_TEST_NAME}_tests test_common libslic3r ${Boost_LIBRARIES} ${TBB_LIBRARIES} ${Boost_LIBRARIES})
+target_link_libraries(${_TEST_NAME}_tests test_common libnest2d )
+set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER "tests")
# catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ")
add_test(${_TEST_NAME}_tests ${_TEST_NAME}_tests "--durations yes")
diff --git a/tests/libnest2d/libnest2d_tests_main.cpp b/tests/libnest2d/libnest2d_tests_main.cpp
index 1741bb8d6..252bea47f 100644
--- a/tests/libnest2d/libnest2d_tests_main.cpp
+++ b/tests/libnest2d/libnest2d_tests_main.cpp
@@ -3,7 +3,8 @@
#include <fstream>
-#include <libnest2d.h>
+
+#include <libnest2d/libnest2d.hpp>
#include "printer_parts.hpp"
//#include <libnest2d/geometry_traits_nfp.hpp>
#include "../tools/svgtools.hpp"
@@ -225,11 +226,11 @@ TEST_CASE("Area", "[Geometry]") {
using namespace libnest2d;
RectangleItem rect(10, 10);
-
+
REQUIRE(rect.area() == Approx(100));
RectangleItem rect2 = {100, 100};
-
+
REQUIRE(rect2.area() == Approx(10000));
Item item = {
@@ -371,7 +372,7 @@ TEST_CASE("ArrangeRectanglesTight", "[Nesting]")
REQUIRE(getX(bin.center()) == 105);
REQUIRE(getY(bin.center()) == 125);
- _Nester<BottomLeftPlacer, DJDHeuristic> arrange(bin);
+ _Nester<BottomLeftPlacer, FirstFitSelection> arrange(bin);
arrange.execute(rects.begin(), rects.end());
@@ -439,7 +440,7 @@ TEST_CASE("ArrangeRectanglesLoose", "[Nesting]")
Coord min_obj_distance = 5;
- _Nester<BottomLeftPlacer, DJDHeuristic> arrange(bin, min_obj_distance);
+ _Nester<BottomLeftPlacer, FirstFitSelection> arrange(bin, min_obj_distance);
arrange.execute(rects.begin(), rects.end());
@@ -447,7 +448,7 @@ TEST_CASE("ArrangeRectanglesLoose", "[Nesting]")
[](const Item &i1, const Item &i2) {
return i1.binId() < i2.binId();
});
-
+
auto groups = size_t(max_group == rects.end() ? 0 : max_group->binId() + 1);
REQUIRE(groups == 1u);
@@ -615,7 +616,7 @@ TEST_CASE("EmptyItemShouldBeUntouched", "[Nesting]") {
items.emplace_back(Item{0, 200, 0}); // Emplace zero area item
size_t bins = libnest2d::nest(items, bin);
-
+
REQUIRE(bins == 0u);
for (auto &itm : items) REQUIRE(itm.binId() == BIN_ID_UNSET);
}
@@ -627,57 +628,57 @@ TEST_CASE("LargeItemShouldBeUntouched", "[Nesting]") {
items.emplace_back(RectangleItem{250000001, 210000001}); // Emplace large item
size_t bins = libnest2d::nest(items, bin);
-
+
REQUIRE(bins == 0u);
REQUIRE(items.front().binId() == BIN_ID_UNSET);
}
TEST_CASE("Items can be preloaded", "[Nesting]") {
auto bin = Box({0, 0}, {250000000, 210000000}); // dummy bin
-
+
std::vector<Item> items;
items.reserve(2);
-
+
NestConfig<> cfg;
cfg.placer_config.alignment = NestConfig<>::Placement::Alignment::DONT_ALIGN;
-
+
items.emplace_back(RectangleItem{10000000, 10000000});
Item &fixed_rect = items.back();
fixed_rect.translate(bin.center());
-
+
items.emplace_back(RectangleItem{20000000, 20000000});
Item &movable_rect = items.back();
movable_rect.translate(bin.center());
-
+
SECTION("Preloaded Item should be untouched") {
fixed_rect.markAsFixedInBin(0);
-
+
size_t bins = libnest2d::nest(items, bin, 0, cfg);
-
+
REQUIRE(bins == 1);
-
+
REQUIRE(fixed_rect.binId() == 0);
REQUIRE(fixed_rect.translation().X == bin.center().X);
REQUIRE(fixed_rect.translation().Y == bin.center().Y);
-
+
REQUIRE(movable_rect.binId() == 0);
REQUIRE(movable_rect.translation().X != bin.center().X);
- REQUIRE(movable_rect.translation().Y != bin.center().Y);
+ REQUIRE(movable_rect.translation().Y != bin.center().Y);
}
-
+
SECTION("Preloaded Item should not affect free bins") {
fixed_rect.markAsFixedInBin(1);
-
+
size_t bins = libnest2d::nest(items, bin, 0, cfg);
-
+
REQUIRE(bins == 2);
-
+
REQUIRE(fixed_rect.binId() == 1);
REQUIRE(fixed_rect.translation().X == bin.center().X);
REQUIRE(fixed_rect.translation().Y == bin.center().Y);
-
+
REQUIRE(movable_rect.binId() == 0);
-
+
auto bb = movable_rect.boundingBox();
REQUIRE(bb.center().X == bin.center().X);
REQUIRE(bb.center().Y == bin.center().Y);
@@ -1013,7 +1014,7 @@ TEST_CASE("mergePileWithPolygon", "[Geometry]") {
REQUIRE(result.size() == 1);
RectangleItem ref(45, 15);
-
+
REQUIRE(shapelike::area(result.front()) == Approx(ref.area()));
}
diff --git a/tests/libslic3r/CMakeLists.txt b/tests/libslic3r/CMakeLists.txt
new file mode 100644
index 000000000..61d4667b7
--- /dev/null
+++ b/tests/libslic3r/CMakeLists.txt
@@ -0,0 +1,12 @@
+get_filename_component(_TEST_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
+add_executable(${_TEST_NAME}_tests
+ ${_TEST_NAME}_tests.cpp
+ test_3mf.cpp
+ test_geometry.cpp
+ test_polygon.cpp
+ )
+target_link_libraries(${_TEST_NAME}_tests test_common libslic3r)
+set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER "tests")
+
+# catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ")
+add_test(${_TEST_NAME}_tests ${_TEST_NAME}_tests "--durations yes")
diff --git a/tests/libslic3r/libslic3r_tests.cpp b/tests/libslic3r/libslic3r_tests.cpp
new file mode 100644
index 000000000..907304f57
--- /dev/null
+++ b/tests/libslic3r/libslic3r_tests.cpp
@@ -0,0 +1,15 @@
+#define CATCH_CONFIG_MAIN
+#include <catch2/catch.hpp>
+
+#include "libslic3r/libslic3r.h"
+
+namespace {
+
+TEST_CASE("sort_remove_duplicates", "[utils]") {
+ std::vector<int> data_src = { 3, 0, 2, 1, 15, 3, 5, 6, 3, 1, 0 };
+ std::vector<int> data_dst = { 0, 1, 2, 3, 5, 6, 15 };
+ Slic3r::sort_remove_duplicates(data_src);
+ REQUIRE(data_src == data_dst);
+}
+
+}
diff --git a/tests/libslic3r/test_3mf.cpp b/tests/libslic3r/test_3mf.cpp
new file mode 100644
index 000000000..0cde0b0dc
--- /dev/null
+++ b/tests/libslic3r/test_3mf.cpp
@@ -0,0 +1,20 @@
+#include <catch2/catch.hpp>
+
+#include "libslic3r/Model.hpp"
+#include "libslic3r/Format/3mf.hpp"
+
+using namespace Slic3r;
+
+SCENARIO("Reading 3mf file") {
+ GIVEN("umlauts in the path of the file") {
+ Slic3r::Model model;
+ WHEN("3mf model is read") {
+ std::string path = std::string(TEST_DATA_DIR) + "/test_3mf/Geräte/Büchse.3mf";
+ DynamicPrintConfig config;
+ bool ret = Slic3r::load_3mf(path.c_str(), &config, &model, false);
+ THEN("load should succeed") {
+ REQUIRE(ret);
+ }
+ }
+ }
+}
diff --git a/tests/libslic3r/test_geometry.cpp b/tests/libslic3r/test_geometry.cpp
new file mode 100644
index 000000000..fce6a476c
--- /dev/null
+++ b/tests/libslic3r/test_geometry.cpp
@@ -0,0 +1,375 @@
+#include <catch2/catch.hpp>
+
+#include "libslic3r/Point.hpp"
+#include "libslic3r/BoundingBox.hpp"
+#include "libslic3r/Polygon.hpp"
+#include "libslic3r/Polyline.hpp"
+#include "libslic3r/Line.hpp"
+#include "libslic3r/Geometry.hpp"
+#include "libslic3r/ClipperUtils.hpp"
+#include "libslic3r/ShortestPath.hpp"
+
+using namespace Slic3r;
+
+TEST_CASE("Polygon::contains works properly", ""){
+ // this test was failing on Windows (GH #1950)
+ Slic3r::Polygon polygon(std::vector<Point>({
+ Point(207802834,-57084522),
+ Point(196528149,-37556190),
+ Point(173626821,-25420928),
+ Point(171285751,-21366123),
+ Point(118673592,-21366123),
+ Point(116332562,-25420928),
+ Point(93431208,-37556191),
+ Point(82156517,-57084523),
+ Point(129714478,-84542120),
+ Point(160244873,-84542120)
+ }));
+ Point point(95706562, -57294774);
+ REQUIRE(polygon.contains(point));
+}
+
+SCENARIO("Intersections of line segments"){
+ GIVEN("Integer coordinates"){
+ Line line1(Point(5,15),Point(30,15));
+ Line line2(Point(10,20), Point(10,10));
+ THEN("The intersection is valid"){
+ Point point;
+ line1.intersection(line2,&point);
+ REQUIRE(Point(10,15) == point);
+ }
+ }
+
+ GIVEN("Scaled coordinates"){
+ Line line1(Point(73.6310778185108 / 0.00001, 371.74239268924 / 0.00001), Point(73.6310778185108 / 0.00001, 501.74239268924 / 0.00001));
+ Line line2(Point(75/0.00001, 437.9853/0.00001), Point(62.7484/0.00001, 440.4223/0.00001));
+ THEN("There is still an intersection"){
+ Point point;
+ REQUIRE(line1.intersection(line2,&point));
+ }
+ }
+}
+
+/*
+Tests for unused methods still written in perl
+{
+ my $polygon = Slic3r::Polygon->new(
+ [45919000, 515273900], [14726100, 461246400], [14726100, 348753500], [33988700, 315389800],
+ [43749700, 343843000], [45422300, 352251500], [52362100, 362637800], [62748400, 369577600],
+ [75000000, 372014700], [87251500, 369577600], [97637800, 362637800], [104577600, 352251500],
+ [107014700, 340000000], [104577600, 327748400], [97637800, 317362100], [87251500, 310422300],
+ [82789200, 309534700], [69846100, 294726100], [254081000, 294726100], [285273900, 348753500],
+ [285273900, 461246400], [254081000, 515273900],
+ );
+
+ # this points belongs to $polyline
+ # note: it's actually a vertex, while we should better check an intermediate point
+ my $point = Slic3r::Point->new(104577600, 327748400);
+
+ local $Slic3r::Geometry::epsilon = 1E-5;
+ is_deeply Slic3r::Geometry::polygon_segment_having_point($polygon, $point)->pp,
+ [ [107014700, 340000000], [104577600, 327748400] ],
+ 'polygon_segment_having_point';
+}
+{
+ auto point = Point(736310778.185108, 5017423926.8924);
+ auto line = Line(Point((long int) 627484000, (long int) 3695776000), Point((long int) 750000000, (long int)3720147000));
+ //is Slic3r::Geometry::point_in_segment($point, $line), 0, 'point_in_segment';
+}
+
+// Possible to delete
+{
+ //my $p1 = [10, 10];
+ //my $p2 = [10, 20];
+ //my $p3 = [10, 30];
+ //my $p4 = [20, 20];
+ //my $p5 = [0, 20];
+
+ THEN("Points in a line give the correct angles"){
+ //is Slic3r::Geometry::angle3points($p2, $p3, $p1), PI(), 'angle3points';
+ //is Slic3r::Geometry::angle3points($p2, $p1, $p3), PI(), 'angle3points';
+ }
+ THEN("Left turns give the correct angle"){
+ //is Slic3r::Geometry::angle3points($p2, $p4, $p3), PI()/2, 'angle3points';
+ //is Slic3r::Geometry::angle3points($p2, $p1, $p4), PI()/2, 'angle3points';
+ }
+ THEN("Right turns give the correct angle"){
+ //is Slic3r::Geometry::angle3points($p2, $p3, $p4), PI()/2*3, 'angle3points';
+ //is Slic3r::Geometry::angle3points($p2, $p1, $p5), PI()/2*3, 'angle3points';
+ }
+ //my $p1 = [30, 30];
+ //my $p2 = [20, 20];
+ //my $p3 = [10, 10];
+ //my $p4 = [30, 10];
+
+ //is Slic3r::Geometry::angle3points($p2, $p1, $p3), PI(), 'angle3points';
+ //is Slic3r::Geometry::angle3points($p2, $p1, $p4), PI()/2*3, 'angle3points';
+ //is Slic3r::Geometry::angle3points($p2, $p1, $p1), 2*PI(), 'angle3points';
+}
+
+SCENARIO("polygon_is_convex works"){
+ GIVEN("A square of dimension 10"){
+ //my $cw_square = [ [0,0], [0,10], [10,10], [10,0] ];
+ THEN("It is not convex clockwise"){
+ //is polygon_is_convex($cw_square), 0, 'cw square is not convex';
+ }
+ THEN("It is convex counter-clockwise"){
+ //is polygon_is_convex([ reverse @$cw_square ]), 1, 'ccw square is convex';
+ }
+
+ }
+ GIVEN("A concave polygon"){
+ //my $convex1 = [ [0,0], [10,0], [10,10], [0,10], [0,6], [4,6], [4,4], [0,4] ];
+ THEN("It is concave"){
+ //is polygon_is_convex($convex1), 0, 'concave polygon';
+ }
+ }
+}*/
+
+
+TEST_CASE("Creating a polyline generates the obvious lines"){
+ Slic3r::Polyline polyline;
+ polyline.points = std::vector<Point>({Point(0, 0), Point(10, 0), Point(20, 0)});
+ REQUIRE(polyline.lines().at(0).a == Point(0,0));
+ REQUIRE(polyline.lines().at(0).b == Point(10,0));
+ REQUIRE(polyline.lines().at(1).a == Point(10,0));
+ REQUIRE(polyline.lines().at(1).b == Point(20,0));
+}
+
+TEST_CASE("Splitting a Polygon generates a polyline correctly"){
+ Slic3r::Polygon polygon(std::vector<Point>({Point(0, 0), Point(10, 0), Point(5, 5)}));
+ Slic3r::Polyline split = polygon.split_at_index(1);
+ REQUIRE(split.points[0]==Point(10,0));
+ REQUIRE(split.points[1]==Point(5,5));
+ REQUIRE(split.points[2]==Point(0,0));
+ REQUIRE(split.points[3]==Point(10,0));
+}
+
+
+TEST_CASE("Bounding boxes are scaled appropriately"){
+ BoundingBox bb(std::vector<Point>({Point(0, 1), Point(10, 2), Point(20, 2)}));
+ bb.scale(2);
+ REQUIRE(bb.min == Point(0,2));
+ REQUIRE(bb.max == Point(40,4));
+}
+
+
+TEST_CASE("Offseting a line generates a polygon correctly"){
+ Slic3r::Polyline tmp = { Point(10,10), Point(20,10) };
+ Slic3r::Polygon area = offset(tmp,5).at(0);
+ REQUIRE(area.area() == Slic3r::Polygon(std::vector<Point>({Point(10,5),Point(20,5),Point(20,15),Point(10,15)})).area());
+}
+
+SCENARIO("Circle Fit, TaubinFit with Newton's method") {
+ GIVEN("A vector of Vec2ds arranged in a half-circle with approximately the same distance R from some point") {
+ Vec2d expected_center(-6, 0);
+ Vec2ds sample {Vec2d(6.0, 0), Vec2d(5.1961524, 3), Vec2d(3 ,5.1961524), Vec2d(0, 6.0), Vec2d(3, 5.1961524), Vec2d(-5.1961524, 3), Vec2d(-6.0, 0)};
+ std::transform(sample.begin(), sample.end(), sample.begin(), [expected_center] (const Vec2d& a) { return a + expected_center;});
+
+ WHEN("Circle fit is called on the entire array") {
+ Vec2d result_center(0,0);
+ result_center = Geometry::circle_taubin_newton(sample);
+ THEN("A center point of -6,0 is returned.") {
+ REQUIRE(is_approx(result_center, expected_center));
+ }
+ }
+ WHEN("Circle fit is called on the first four points") {
+ Vec2d result_center(0,0);
+ result_center = Geometry::circle_taubin_newton(sample.cbegin(), sample.cbegin()+4);
+ THEN("A center point of -6,0 is returned.") {
+ REQUIRE(is_approx(result_center, expected_center));
+ }
+ }
+ WHEN("Circle fit is called on the middle four points") {
+ Vec2d result_center(0,0);
+ result_center = Geometry::circle_taubin_newton(sample.cbegin()+2, sample.cbegin()+6);
+ THEN("A center point of -6,0 is returned.") {
+ REQUIRE(is_approx(result_center, expected_center));
+ }
+ }
+ }
+ GIVEN("A vector of Vec2ds arranged in a half-circle with approximately the same distance R from some point") {
+ Vec2d expected_center(-3, 9);
+ Vec2ds sample {Vec2d(6.0, 0), Vec2d(5.1961524, 3), Vec2d(3 ,5.1961524),
+ Vec2d(0, 6.0),
+ Vec2d(3, 5.1961524), Vec2d(-5.1961524, 3), Vec2d(-6.0, 0)};
+
+ std::transform(sample.begin(), sample.end(), sample.begin(), [expected_center] (const Vec2d& a) { return a + expected_center;});
+
+
+ WHEN("Circle fit is called on the entire array") {
+ Vec2d result_center(0,0);
+ result_center = Geometry::circle_taubin_newton(sample);
+ THEN("A center point of 3,9 is returned.") {
+ REQUIRE(is_approx(result_center, expected_center));
+ }
+ }
+ WHEN("Circle fit is called on the first four points") {
+ Vec2d result_center(0,0);
+ result_center = Geometry::circle_taubin_newton(sample.cbegin(), sample.cbegin()+4);
+ THEN("A center point of 3,9 is returned.") {
+ REQUIRE(is_approx(result_center, expected_center));
+ }
+ }
+ WHEN("Circle fit is called on the middle four points") {
+ Vec2d result_center(0,0);
+ result_center = Geometry::circle_taubin_newton(sample.cbegin()+2, sample.cbegin()+6);
+ THEN("A center point of 3,9 is returned.") {
+ REQUIRE(is_approx(result_center, expected_center));
+ }
+ }
+ }
+ GIVEN("A vector of Points arranged in a half-circle with approximately the same distance R from some point") {
+ Point expected_center { Point::new_scale(-3, 9)};
+ Points sample {Point::new_scale(6.0, 0), Point::new_scale(5.1961524, 3), Point::new_scale(3 ,5.1961524),
+ Point::new_scale(0, 6.0),
+ Point::new_scale(3, 5.1961524), Point::new_scale(-5.1961524, 3), Point::new_scale(-6.0, 0)};
+
+ std::transform(sample.begin(), sample.end(), sample.begin(), [expected_center] (const Point& a) { return a + expected_center;});
+
+
+ WHEN("Circle fit is called on the entire array") {
+ Point result_center(0,0);
+ result_center = Geometry::circle_taubin_newton(sample);
+ THEN("A center point of scaled 3,9 is returned.") {
+ REQUIRE(is_approx(result_center, expected_center));
+ }
+ }
+ WHEN("Circle fit is called on the first four points") {
+ Point result_center(0,0);
+ result_center = Geometry::circle_taubin_newton(sample.cbegin(), sample.cbegin()+4);
+ THEN("A center point of scaled 3,9 is returned.") {
+ REQUIRE(is_approx(result_center, expected_center));
+ }
+ }
+ WHEN("Circle fit is called on the middle four points") {
+ Point result_center(0,0);
+ result_center = Geometry::circle_taubin_newton(sample.cbegin()+2, sample.cbegin()+6);
+ THEN("A center point of scaled 3,9 is returned.") {
+ REQUIRE(is_approx(result_center, expected_center));
+ }
+ }
+ }
+}
+
+TEST_CASE("Chained path working correctly"){
+ // if chained_path() works correctly, these points should be joined with no diagonal paths
+ // (thus 26 units long)
+ std::vector<Point> points = {Point(26,26),Point(52,26),Point(0,26),Point(26,52),Point(26,0),Point(0,52),Point(52,52),Point(52,0)};
+ std::vector<Points::size_type> indices = chain_points(points);
+ for (Points::size_type i = 0; i + 1 < indices.size(); ++ i) {
+ double dist = (points.at(indices.at(i)).cast<double>() - points.at(indices.at(i+1)).cast<double>()).norm();
+ REQUIRE(std::abs(dist-26) <= EPSILON);
+ }
+}
+
+SCENARIO("Line distances"){
+ GIVEN("A line"){
+ Line line(Point(0, 0), Point(20, 0));
+ THEN("Points on the line segment have 0 distance"){
+ REQUIRE(line.distance_to(Point(0, 0)) == 0);
+ REQUIRE(line.distance_to(Point(20, 0)) == 0);
+ REQUIRE(line.distance_to(Point(10, 0)) == 0);
+
+ }
+ THEN("Points off the line have the appropriate distance"){
+ REQUIRE(line.distance_to(Point(10, 10)) == 10);
+ REQUIRE(line.distance_to(Point(50, 0)) == 30);
+ }
+ }
+}
+
+SCENARIO("Polygon convex/concave detection"){
+ GIVEN(("A Square with dimension 100")){
+ auto square = Slic3r::Polygon /*new_scale*/(std::vector<Point>({
+ Point(100,100),
+ Point(200,100),
+ Point(200,200),
+ Point(100,200)}));
+ THEN("It has 4 convex points counterclockwise"){
+ REQUIRE(square.concave_points(PI*4/3).size() == 0);
+ REQUIRE(square.convex_points(PI*2/3).size() == 4);
+ }
+ THEN("It has 4 concave points clockwise"){
+ square.make_clockwise();
+ REQUIRE(square.concave_points(PI*4/3).size() == 4);
+ REQUIRE(square.convex_points(PI*2/3).size() == 0);
+ }
+ }
+ GIVEN("A Square with an extra colinearvertex"){
+ auto square = Slic3r::Polygon /*new_scale*/(std::vector<Point>({
+ Point(150,100),
+ Point(200,100),
+ Point(200,200),
+ Point(100,200),
+ Point(100,100)}));
+ THEN("It has 4 convex points counterclockwise"){
+ REQUIRE(square.concave_points(PI*4/3).size() == 0);
+ REQUIRE(square.convex_points(PI*2/3).size() == 4);
+ }
+ }
+ GIVEN("A Square with an extra collinear vertex in different order"){
+ auto square = Slic3r::Polygon /*new_scale*/(std::vector<Point>({
+ Point(200,200),
+ Point(100,200),
+ Point(100,100),
+ Point(150,100),
+ Point(200,100)}));
+ THEN("It has 4 convex points counterclockwise"){
+ REQUIRE(square.concave_points(PI*4/3).size() == 0);
+ REQUIRE(square.convex_points(PI*2/3).size() == 4);
+ }
+ }
+
+ GIVEN("A triangle"){
+ auto triangle = Slic3r::Polygon(std::vector<Point>({
+ Point(16000170,26257364),
+ Point(714223,461012),
+ Point(31286371,461008)
+ }));
+ THEN("it has three convex vertices"){
+ REQUIRE(triangle.concave_points(PI*4/3).size() == 0);
+ REQUIRE(triangle.convex_points(PI*2/3).size() == 3);
+ }
+ }
+
+ GIVEN("A triangle with an extra collinear point"){
+ auto triangle = Slic3r::Polygon(std::vector<Point>({
+ Point(16000170,26257364),
+ Point(714223,461012),
+ Point(20000000,461012),
+ Point(31286371,461012)
+ }));
+ THEN("it has three convex vertices"){
+ REQUIRE(triangle.concave_points(PI*4/3).size() == 0);
+ REQUIRE(triangle.convex_points(PI*2/3).size() == 3);
+ }
+ }
+ GIVEN("A polygon with concave vertices with angles of specifically 4/3pi"){
+ // Two concave vertices of this polygon have angle = PI*4/3, so this test fails
+ // if epsilon is not used.
+ auto polygon = Slic3r::Polygon(std::vector<Point>({
+ Point(60246458,14802768),Point(64477191,12360001),
+ Point(63727343,11060995),Point(64086449,10853608),
+ Point(66393722,14850069),Point(66034704,15057334),
+ Point(65284646,13758387),Point(61053864,16200839),
+ Point(69200258,30310849),Point(62172547,42483120),
+ Point(61137680,41850279),Point(67799985,30310848),
+ Point(51399866,1905506),Point(38092663,1905506),
+ Point(38092663,692699),Point(52100125,692699)
+ }));
+ THEN("the correct number of points are detected"){
+ REQUIRE(polygon.concave_points(PI*4/3).size() == 6);
+ REQUIRE(polygon.convex_points(PI*2/3).size() == 10);
+ }
+ }
+}
+
+TEST_CASE("Triangle Simplification does not result in less than 3 points"){
+ auto triangle = Slic3r::Polygon(std::vector<Point>({
+ Point(16000170,26257364), Point(714223,461012), Point(31286371,461008)
+ }));
+ REQUIRE(triangle.simplify(250000).at(0).points.size() == 3);
+}
+
+
diff --git a/tests/libslic3r/test_polygon.cpp b/tests/libslic3r/test_polygon.cpp
new file mode 100644
index 000000000..8e9975843
--- /dev/null
+++ b/tests/libslic3r/test_polygon.cpp
@@ -0,0 +1,44 @@
+#include <catch2/catch.hpp>
+
+#include "libslic3r/Point.hpp"
+#include "libslic3r/Polygon.hpp"
+
+using namespace Slic3r;
+
+// This test currently only covers remove_collinear_points.
+// All remaining tests are to be ported from xs/t/06_polygon.t
+
+Slic3r::Points collinear_circle({
+ Slic3r::Point::new_scale(0, 0), // 3 collinear points at beginning
+ Slic3r::Point::new_scale(10, 0),
+ Slic3r::Point::new_scale(20, 0),
+ Slic3r::Point::new_scale(30, 10),
+ Slic3r::Point::new_scale(40, 20), // 2 collinear points
+ Slic3r::Point::new_scale(40, 30),
+ Slic3r::Point::new_scale(30, 40), // 3 collinear points
+ Slic3r::Point::new_scale(20, 40),
+ Slic3r::Point::new_scale(10, 40),
+ Slic3r::Point::new_scale(-10, 20),
+ Slic3r::Point::new_scale(-20, 10),
+ Slic3r::Point::new_scale(-20, 0), // 3 collinear points at end
+ Slic3r::Point::new_scale(-10, 0),
+ Slic3r::Point::new_scale(-5, 0)
+});
+
+SCENARIO("Remove collinear points from Polygon") {
+ GIVEN("Polygon with collinear points"){
+ Slic3r::Polygon p(collinear_circle);
+ WHEN("collinear points are removed") {
+ remove_collinear(p);
+ THEN("Leading collinear points are removed") {
+ REQUIRE(p.points.front() == Slic3r::Point::new_scale(20, 0));
+ }
+ THEN("Trailing collinear points are removed") {
+ REQUIRE(p.points.back() == Slic3r::Point::new_scale(-20, 0));
+ }
+ THEN("Number of remaining points is correct") {
+ REQUIRE(p.points.size() == 7);
+ }
+ }
+ }
+}
diff --git a/tests/sla_print/CMakeLists.txt b/tests/sla_print/CMakeLists.txt
index 687096ee4..d0b51a01d 100644
--- a/tests/sla_print/CMakeLists.txt
+++ b/tests/sla_print/CMakeLists.txt
@@ -1,6 +1,7 @@
get_filename_component(_TEST_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
add_executable(${_TEST_NAME}_tests ${_TEST_NAME}_tests.cpp)
-target_link_libraries(${_TEST_NAME}_tests test_common libslic3r ${Boost_LIBRARIES} ${TBB_LIBRARIES} ${Boost_LIBRARIES})
+target_link_libraries(${_TEST_NAME}_tests test_common libslic3r)
+set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER "tests")
-#catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ")
+# catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ")
add_test(${_TEST_NAME}_tests ${_TEST_NAME}_tests "--durations yes")
diff --git a/tests/timeutils/CMakeLists.txt b/tests/timeutils/CMakeLists.txt
index c4b7c2029..b67ce85f1 100644
--- a/tests/timeutils/CMakeLists.txt
+++ b/tests/timeutils/CMakeLists.txt
@@ -1,6 +1,11 @@
get_filename_component(_TEST_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
-add_executable(${_TEST_NAME}_tests ${_TEST_NAME}_tests_main.cpp)
-target_link_libraries(${_TEST_NAME}_tests test_common libslic3r ${Boost_LIBRARIES} ${TBB_LIBRARIES} ${Boost_LIBRARIES})
+add_executable(${_TEST_NAME}_tests
+ ${_TEST_NAME}_tests_main.cpp
+ ${PROJECT_SOURCE_DIR}/src/libslic3r/Time.cpp
+ ${PROJECT_SOURCE_DIR}/src/libslic3r/Time.hpp
+ )
+target_link_libraries(${_TEST_NAME}_tests test_common)
+set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER "tests")
# catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ")
add_test(${_TEST_NAME}_tests ${_TEST_NAME}_tests "--durations yes")
diff --git a/xs/xsp/Config.xsp b/xs/xsp/Config.xsp
index f5e6ffb05..435dda5a2 100644
--- a/xs/xsp/Config.xsp
+++ b/xs/xsp/Config.xsp
@@ -8,7 +8,8 @@
%name{Slic3r::Config} class DynamicPrintConfig {
DynamicPrintConfig();
~DynamicPrintConfig();
- static DynamicPrintConfig* new_from_defaults();
+ static DynamicPrintConfig* new_from_defaults()
+ %code{% RETVAL = DynamicPrintConfig::new_from_defaults_keys(FullPrintConfig::defaults().keys()); %};
static DynamicPrintConfig* new_from_defaults_keys(std::vector<std::string> keys);
DynamicPrintConfig* clone() %code{% RETVAL = new DynamicPrintConfig(*THIS); %};
DynamicPrintConfig* clone_only(std::vector<std::string> keys)