diff --git a/.gitignore b/.gitignore index 1612499f6..1d68b940c 100644 --- a/.gitignore +++ b/.gitignore @@ -12,4 +12,4 @@ xs/assertlib* .init_bundle.ini local-lib build* -deps/deps-build +deps/deps-* diff --git a/doc/How to build - Windows.md b/doc/How to build - Windows.md index 020b28958..80fc946be 100644 --- a/doc/How to build - Windows.md +++ b/doc/How to build - Windows.md @@ -100,3 +100,8 @@ The dependency build will by default build _both_ the _Release_ and _Debug_ vari You can disable building of the debug variant by passing the `-DDEP_DEBUG=OFF` option to CMake, this will only produce a _Release_ build. Refer to the CMake scripts inside the `deps` directory to see which dependencies are built in what versions and how this is done. + +### building tests + +You must use vs 2015 or 2017, and convert the slic3r_test project to your version. Also, you have to add the "CRT SDK" to your windows 2017 installation (can be done via the VS intaller). + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d15fee470..36e8d22fc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -13,7 +13,7 @@ add_subdirectory(qhull) add_subdirectory(Shiny) add_subdirectory(semver) add_subdirectory(imgui) -#add_subdirectory(test) +add_subdirectory(test) # Adding libnest2d project for bin packing... set(LIBNEST2D_UNITTESTS ON CACHE BOOL "Force generating unittests for libnest2d") @@ -94,6 +94,7 @@ if (MSVC) else () add_executable(slic3r slic3r.cpp slic3r.hpp) endif () +add_library(slic3r_lib STATIC slic3r.cpp slic3r.hpp) if (NOT MSVC) if(SLIC3R_GUI) set_target_properties(slic3r PROPERTIES OUTPUT_NAME "slic3r-gui") @@ -103,31 +104,38 @@ if (NOT MSVC) endif () target_link_libraries(slic3r libslic3r) +target_link_libraries(slic3r_lib libslic3r) if (APPLE) # add_compile_options(-stdlib=libc++) # add_definitions(-DBOOST_THREAD_DONT_USE_CHRONO -DBOOST_NO_CXX11_RVALUE_REFERENCES -DBOOST_THREAD_USES_MOVE) # -liconv: boost links to libiconv by default target_link_libraries(slic3r "-liconv -framework IOKit" "-framework CoreFoundation" -lc++) + target_link_libraries(slic3r_lib "-liconv -framework IOKit" "-framework CoreFoundation" -lc++) elseif (MSVC) # Manifest is provided through slic3r.rc, don't generate your own. set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /MANIFEST:NO") else () target_link_libraries(slic3r ${CMAKE_DL_LIBS} -lstdc++) + target_link_libraries(slic3r_lib ${CMAKE_DL_LIBS} -lstdc++) endif () # Add the Slic3r GUI library, libcurl, OpenGL and GLU libraries. if (SLIC3R_GUI) target_link_libraries(slic3r libslic3r_gui ${wxWidgets_LIBRARIES}) + target_link_libraries(slic3r_lib libslic3r_gui ${wxWidgets_LIBRARIES}) # Configure libcurl & OpenSSL find_package(CURL REQUIRED) target_include_directories(slic3r PRIVATE ${CURL_INCLUDE_DIRS}) target_link_libraries(slic3r CURL::libcurl) + target_include_directories(slic3r_lib PRIVATE ${CURL_INCLUDE_DIRS}) + target_link_libraries(slic3r_lib CURL::libcurl) if (SLIC3R_STATIC) if (NOT APPLE) # libcurl is always linked dynamically to the system libcurl on OSX. # On other systems, libcurl is linked statically if SLIC3R_STATIC is set. target_compile_definitions(slic3r PRIVATE CURL_STATICLIB) + target_compile_definitions(slic3r_lib PRIVATE CURL_STATICLIB) endif() if (CMAKE_SYSTEM_NAME STREQUAL "Linux") # As of now, our build system produces a statically linked libcurl, @@ -137,17 +145,23 @@ if (SLIC3R_GUI) message("OpenSSL libraries: ${OPENSSL_LIBRARIES}") target_include_directories(slic3r PRIVATE ${OPENSSL_INCLUDE_DIR}) target_link_libraries(slic3r ${OPENSSL_LIBRARIES}) + target_include_directories(slic3r_lib PRIVATE ${OPENSSL_INCLUDE_DIR}) + target_link_libraries(slic3r_lib ${OPENSSL_LIBRARIES}) endif() endif() if (MSVC) target_link_libraries(slic3r user32.lib Setupapi.lib OpenGL32.Lib GlU32.Lib) + target_link_libraries(slic3r_lib user32.lib Setupapi.lib OpenGL32.Lib GlU32.Lib) elseif (MINGW) target_link_libraries(slic3r -lopengl32) + target_link_libraries(slic3r_lib -lopengl32) elseif (APPLE) target_link_libraries(slic3r "-framework OpenGL") + target_link_libraries(slic3r_lib "-framework OpenGL") else () target_link_libraries(slic3r -ldl -lGL -lGLU) + target_link_libraries(slic3r_lib -ldl -lGL -lGLU) endif () endif () @@ -164,10 +178,6 @@ if (MSVC) add_dependencies(slic3r_app_console slic3r) set_target_properties(slic3r_app_console PROPERTIES OUTPUT_NAME "slic3r-console") -# add_executable(slic3r_test slic3r.cpp ${CMAKE_CURRENT_BINARY_DIR}/slic3r.rc) -# target_compile_definitions(slic3r_test PRIVATE -DSLIC3R_WRAPPER_CONSOLE) -# add_dependencies(slic3r_test slic3r) -# set_target_properties(slic3r_test PROPERTIES OUTPUT_NAME "slic3r-test") endif () # Link the resources dir to where Slic3r GUI expects it @@ -215,7 +225,6 @@ if (WIN32) if (MSVC) install(TARGETS slic3r_app_gui RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}") install(TARGETS slic3r_app_console RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}") -# install(TARGETS slic3r_test RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}") endif () else () install(TARGETS slic3r RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") diff --git a/src/avrdude/avrdude-slic3r.conf.h b/src/avrdude/avrdude-slic3r.conf.h new file mode 100644 index 000000000..7cc901336 --- /dev/null +++ b/src/avrdude/avrdude-slic3r.conf.h @@ -0,0 +1,1188 @@ +/* WARN: This file is auto-generated from `avrdude-slic3r.conf` */ +unsigned char avrdude_slic3r_conf[] = { + 0x0a, 0x23, 0x0a, 0x23, 0x20, 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, + 0x20, 0x61, 0x20, 0x62, 0x61, 0x73, 0x69, 0x63, 0x20, 0x6d, 0x69, 0x6e, + 0x69, 0x6d, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x20, + 0x66, 0x69, 0x6c, 0x65, 0x20, 0x65, 0x6d, 0x62, 0x65, 0x64, 0x64, 0x65, + 0x64, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, + 0x76, 0x72, 0x64, 0x75, 0x64, 0x65, 0x2d, 0x73, 0x6c, 0x69, 0x63, 0x33, + 0x72, 0x20, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x0a, 0x23, 0x20, 0x73, + 0x6f, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x69, 0x74, 0x20, 0x63, 0x61, + 0x6e, 0x20, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x69, 0x6e, 0x20, 0x61, 0x20, + 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x6c, 0x6f, 0x6e, 0x65, 0x20, 0x6d, + 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x0a, 0x23, 0x0a, 0x23, 0x20, 0x4f, + 0x6e, 0x6c, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x69, 0x74, 0x73, + 0x20, 0x75, 0x73, 0x65, 0x66, 0x75, 0x6c, 0x20, 0x66, 0x6f, 0x72, 0x20, + 0x50, 0x72, 0x75, 0x73, 0x61, 0x33, 0x44, 0x20, 0x64, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x73, 0x20, 0x77, 0x65, 0x72, 0x65, 0x20, 0x63, 0x6f, 0x70, + 0x69, 0x65, 0x64, 0x20, 0x6f, 0x76, 0x65, 0x72, 0x20, 0x66, 0x72, 0x6f, + 0x6d, 0x20, 0x61, 0x76, 0x72, 0x64, 0x75, 0x64, 0x65, 0x2e, 0x63, 0x6f, + 0x6e, 0x66, 0x0a, 0x23, 0x20, 0x49, 0x66, 0x20, 0x6e, 0x65, 0x65, 0x64, + 0x65, 0x64, 0x2c, 0x20, 0x6d, 0x6f, 0x72, 0x65, 0x20, 0x63, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, + 0x61, 0x6e, 0x20, 0x73, 0x74, 0x69, 0x6c, 0x6c, 0x20, 0x62, 0x65, 0x20, + 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x20, + 0x61, 0x76, 0x72, 0x64, 0x75, 0x64, 0x65, 0x2d, 0x73, 0x6c, 0x69, 0x63, + 0x33, 0x72, 0x0a, 0x23, 0x20, 0x76, 0x69, 0x61, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x2d, 0x43, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2d, + 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x2e, + 0x0a, 0x23, 0x0a, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, + 0x6d, 0x65, 0x72, 0x0a, 0x20, 0x20, 0x69, 0x64, 0x20, 0x20, 0x20, 0x20, + 0x3d, 0x20, 0x22, 0x77, 0x69, 0x72, 0x69, 0x6e, 0x67, 0x22, 0x3b, 0x0a, + 0x20, 0x20, 0x64, 0x65, 0x73, 0x63, 0x20, 0x20, 0x3d, 0x20, 0x22, 0x57, + 0x69, 0x72, 0x69, 0x6e, 0x67, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x74, 0x79, + 0x70, 0x65, 0x20, 0x20, 0x3d, 0x20, 0x22, 0x77, 0x69, 0x72, 0x69, 0x6e, + 0x67, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x20, 0x3d, 0x20, + 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x3b, 0x0a, 0x3b, 0x0a, 0x0a, 0x70, + 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x0a, 0x20, 0x20, + 0x69, 0x64, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x22, 0x61, 0x72, 0x64, + 0x75, 0x69, 0x6e, 0x6f, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x64, 0x65, 0x73, + 0x63, 0x20, 0x20, 0x3d, 0x20, 0x22, 0x41, 0x72, 0x64, 0x75, 0x69, 0x6e, + 0x6f, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x20, + 0x3d, 0x20, 0x22, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x22, 0x3b, + 0x0a, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x20, 0x3d, 0x20, 0x73, 0x65, 0x72, + 0x69, 0x61, 0x6c, 0x3b, 0x0a, 0x3b, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x67, + 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x0a, 0x20, 0x20, 0x69, 0x64, 0x20, + 0x20, 0x20, 0x20, 0x3d, 0x20, 0x22, 0x61, 0x76, 0x72, 0x31, 0x30, 0x39, + 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x64, 0x65, 0x73, 0x63, 0x20, 0x20, 0x3d, + 0x20, 0x22, 0x41, 0x74, 0x6d, 0x65, 0x6c, 0x20, 0x41, 0x70, 0x70, 0x4e, + 0x6f, 0x74, 0x65, 0x20, 0x41, 0x56, 0x52, 0x31, 0x30, 0x39, 0x20, 0x42, + 0x6f, 0x6f, 0x74, 0x20, 0x4c, 0x6f, 0x61, 0x64, 0x65, 0x72, 0x22, 0x3b, + 0x0a, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x20, 0x3d, 0x20, 0x22, + 0x62, 0x75, 0x74, 0x74, 0x65, 0x72, 0x66, 0x6c, 0x79, 0x22, 0x3b, 0x0a, + 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x20, 0x3d, 0x20, 0x73, 0x65, 0x72, 0x69, + 0x61, 0x6c, 0x3b, 0x0a, 0x3b, 0x0a, 0x0a, 0x0a, 0x23, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x23, 0x20, + 0x41, 0x54, 0x6d, 0x65, 0x67, 0x61, 0x32, 0x35, 0x36, 0x30, 0x0a, 0x23, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x74, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, + 0x64, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x22, 0x6d, 0x32, 0x35, 0x36, 0x30, + 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x65, 0x73, 0x63, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x3d, 0x20, 0x22, 0x41, 0x54, 0x6d, 0x65, 0x67, 0x61, 0x32, 0x35, 0x36, + 0x30, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x69, 0x67, 0x6e, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x3d, 0x20, 0x30, 0x78, 0x31, 0x65, 0x20, 0x30, 0x78, 0x39, 0x38, + 0x20, 0x30, 0x78, 0x30, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x68, + 0x61, 0x73, 0x5f, 0x6a, 0x74, 0x61, 0x67, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x79, 0x65, 0x73, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x73, 0x74, 0x6b, 0x35, 0x30, 0x30, 0x5f, 0x64, 0x65, + 0x76, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x30, 0x78, + 0x42, 0x32, 0x3b, 0x0a, 0x23, 0x20, 0x20, 0x20, 0x20, 0x61, 0x76, 0x72, + 0x39, 0x31, 0x30, 0x5f, 0x64, 0x65, 0x76, 0x63, 0x6f, 0x64, 0x65, 0x20, + 0x20, 0x20, 0x3d, 0x20, 0x30, 0x78, 0x34, 0x33, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x63, 0x68, 0x69, 0x70, 0x5f, 0x65, 0x72, 0x61, 0x73, 0x65, + 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x20, 0x3d, 0x20, 0x39, 0x30, 0x30, + 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x67, 0x65, 0x6c, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x3d, 0x20, 0x30, 0x78, 0x44, 0x37, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x62, 0x73, 0x32, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x30, 0x78, 0x41, 0x30, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x65, 0x74, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, + 0x64, 0x65, 0x64, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x70, 0x67, 0x6d, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, + 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x22, 0x31, + 0x20, 0x30, 0x20, 0x31, 0x20, 0x30, 0x20, 0x20, 0x31, 0x20, 0x31, 0x20, + 0x30, 0x20, 0x30, 0x20, 0x20, 0x20, 0x20, 0x30, 0x20, 0x31, 0x20, 0x30, + 0x20, 0x31, 0x20, 0x20, 0x30, 0x20, 0x30, 0x20, 0x31, 0x20, 0x31, 0x22, + 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x22, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x20, 0x78, + 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x20, 0x20, 0x20, 0x78, 0x20, + 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, + 0x20, 0x78, 0x22, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x68, + 0x69, 0x70, 0x5f, 0x65, 0x72, 0x61, 0x73, 0x65, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x3d, 0x20, 0x22, 0x31, 0x20, 0x30, 0x20, 0x31, 0x20, + 0x30, 0x20, 0x20, 0x31, 0x20, 0x31, 0x20, 0x30, 0x20, 0x30, 0x20, 0x20, + 0x20, 0x20, 0x31, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x20, 0x30, + 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x78, 0x20, 0x78, + 0x20, 0x78, 0x20, 0x78, 0x20, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, + 0x78, 0x20, 0x20, 0x20, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, + 0x20, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, 0x22, 0x3b, 0x0a, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, + 0x20, 0x20, 0x20, 0x3d, 0x20, 0x32, 0x30, 0x30, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x73, 0x74, 0x61, 0x62, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x20, + 0x20, 0x20, 0x3d, 0x20, 0x31, 0x30, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x63, 0x6d, 0x64, 0x65, 0x78, 0x65, 0x64, 0x65, 0x6c, 0x61, 0x79, + 0x20, 0x20, 0x20, 0x3d, 0x20, 0x32, 0x35, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x6c, 0x6f, 0x6f, 0x70, 0x73, 0x20, + 0x20, 0x20, 0x20, 0x3d, 0x20, 0x33, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x62, 0x79, 0x74, 0x65, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x20, 0x20, + 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x6f, + 0x6c, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x20, 0x20, 0x3d, 0x20, + 0x33, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x6f, 0x6c, 0x6c, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x30, 0x78, 0x35, + 0x33, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x65, 0x64, 0x65, + 0x6c, 0x61, 0x79, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x31, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x70, 0x6f, 0x73, 0x74, 0x64, 0x65, 0x6c, 0x61, + 0x79, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x70, 0x6f, 0x6c, 0x6c, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x20, + 0x20, 0x20, 0x20, 0x3d, 0x20, 0x31, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x70, 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x73, + 0x74, 0x61, 0x63, 0x6b, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x78, 0x30, 0x45, 0x2c, + 0x20, 0x30, 0x78, 0x31, 0x45, 0x2c, 0x20, 0x30, 0x78, 0x30, 0x46, 0x2c, + 0x20, 0x30, 0x78, 0x31, 0x46, 0x2c, 0x20, 0x30, 0x78, 0x32, 0x45, 0x2c, + 0x20, 0x30, 0x78, 0x33, 0x45, 0x2c, 0x20, 0x30, 0x78, 0x32, 0x46, 0x2c, + 0x20, 0x30, 0x78, 0x33, 0x46, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x30, 0x78, 0x34, 0x45, 0x2c, 0x20, 0x30, 0x78, 0x35, + 0x45, 0x2c, 0x20, 0x30, 0x78, 0x34, 0x46, 0x2c, 0x20, 0x30, 0x78, 0x35, + 0x46, 0x2c, 0x20, 0x30, 0x78, 0x36, 0x45, 0x2c, 0x20, 0x30, 0x78, 0x37, + 0x45, 0x2c, 0x20, 0x30, 0x78, 0x36, 0x46, 0x2c, 0x20, 0x30, 0x78, 0x37, + 0x46, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, + 0x78, 0x36, 0x36, 0x2c, 0x20, 0x30, 0x78, 0x37, 0x36, 0x2c, 0x20, 0x30, + 0x78, 0x36, 0x37, 0x2c, 0x20, 0x30, 0x78, 0x37, 0x37, 0x2c, 0x20, 0x30, + 0x78, 0x36, 0x41, 0x2c, 0x20, 0x30, 0x78, 0x37, 0x41, 0x2c, 0x20, 0x30, + 0x78, 0x36, 0x42, 0x2c, 0x20, 0x30, 0x78, 0x37, 0x42, 0x2c, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x78, 0x42, 0x45, 0x2c, + 0x20, 0x30, 0x78, 0x46, 0x44, 0x2c, 0x20, 0x30, 0x78, 0x30, 0x30, 0x2c, + 0x20, 0x30, 0x78, 0x30, 0x31, 0x2c, 0x20, 0x30, 0x78, 0x30, 0x30, 0x2c, + 0x20, 0x30, 0x78, 0x30, 0x30, 0x2c, 0x20, 0x30, 0x78, 0x30, 0x30, 0x2c, + 0x20, 0x30, 0x78, 0x30, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x68, + 0x76, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x74, 0x61, 0x62, 0x64, 0x65, + 0x6c, 0x61, 0x79, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x31, 0x30, 0x30, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x6d, 0x6f, + 0x64, 0x65, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, + 0x61, 0x74, 0x63, 0x68, 0x63, 0x79, 0x63, 0x6c, 0x65, 0x73, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x35, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x74, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x76, 0x74, + 0x67, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x3d, 0x20, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x6f, 0x77, + 0x65, 0x72, 0x6f, 0x66, 0x66, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x31, 0x35, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x65, 0x74, 0x64, 0x65, 0x6c, 0x61, + 0x79, 0x6d, 0x73, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, + 0x20, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x65, + 0x74, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x75, 0x73, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x68, 0x76, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x74, 0x61, 0x62, + 0x64, 0x65, 0x6c, 0x61, 0x79, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x31, + 0x35, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x68, 0x69, 0x70, 0x65, + 0x72, 0x61, 0x73, 0x65, 0x70, 0x75, 0x6c, 0x73, 0x65, 0x77, 0x69, 0x64, + 0x74, 0x68, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x63, 0x68, 0x69, 0x70, 0x65, 0x72, 0x61, 0x73, 0x65, 0x70, 0x6f, 0x6c, + 0x6c, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x20, 0x3d, 0x20, 0x31, + 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, + 0x61, 0x6d, 0x66, 0x75, 0x73, 0x65, 0x70, 0x75, 0x6c, 0x73, 0x65, 0x77, + 0x69, 0x64, 0x74, 0x68, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x66, 0x75, 0x73, + 0x65, 0x70, 0x6f, 0x6c, 0x6c, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, + 0x20, 0x3d, 0x20, 0x35, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, + 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6c, 0x6f, 0x63, 0x6b, 0x70, 0x75, 0x6c, + 0x73, 0x65, 0x77, 0x69, 0x64, 0x74, 0x68, 0x20, 0x3d, 0x20, 0x30, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, + 0x6c, 0x6f, 0x63, 0x6b, 0x70, 0x6f, 0x6c, 0x6c, 0x74, 0x69, 0x6d, 0x65, + 0x6f, 0x75, 0x74, 0x20, 0x3d, 0x20, 0x35, 0x3b, 0x0a, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x64, 0x72, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, + 0x30, 0x78, 0x33, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x70, + 0x6d, 0x63, 0x72, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x30, 0x78, 0x35, 0x37, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x61, 0x6d, 0x70, 0x7a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x3d, 0x20, 0x30, 0x78, 0x33, 0x62, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x66, 0x75, 0x6c, 0x6c, 0x70, + 0x61, 0x67, 0x65, 0x62, 0x69, 0x74, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, + 0x20, 0x3d, 0x20, 0x6e, 0x6f, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x6f, 0x63, 0x64, 0x72, 0x65, 0x76, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x34, 0x3b, + 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, + 0x20, 0x22, 0x65, 0x65, 0x70, 0x72, 0x6f, 0x6d, 0x22, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x67, 0x65, 0x64, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, + 0x6e, 0x6f, 0x3b, 0x20, 0x2f, 0x2a, 0x20, 0x6c, 0x65, 0x61, 0x76, 0x65, + 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x22, 0x6e, 0x6f, 0x22, 0x20, 0x2a, + 0x2f, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, + 0x67, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x3d, 0x20, 0x38, 0x3b, 0x20, 0x20, 0x2f, 0x2a, 0x20, 0x66, + 0x6f, 0x72, 0x20, 0x70, 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x20, + 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x69, 0x6e, 0x67, 0x20, + 0x2a, 0x2f, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, + 0x69, 0x7a, 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x3d, 0x20, 0x34, 0x30, 0x39, 0x36, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x6e, 0x5f, 0x77, + 0x72, 0x69, 0x74, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x20, 0x3d, + 0x20, 0x39, 0x30, 0x30, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x6d, 0x61, 0x78, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, + 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x20, 0x3d, 0x20, 0x39, 0x30, 0x30, + 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, + 0x65, 0x61, 0x64, 0x62, 0x61, 0x63, 0x6b, 0x5f, 0x70, 0x31, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x3d, 0x20, 0x30, 0x78, 0x30, 0x30, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x61, 0x64, 0x62, + 0x61, 0x63, 0x6b, 0x5f, 0x70, 0x32, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, + 0x20, 0x30, 0x78, 0x30, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x72, 0x65, 0x61, 0x64, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x22, 0x20, 0x20, + 0x31, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x31, 0x20, 0x20, 0x20, + 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, + 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x22, 0x2c, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x22, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, + 0x78, 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, 0x20, 0x61, 0x31, 0x31, + 0x20, 0x61, 0x31, 0x30, 0x20, 0x20, 0x61, 0x39, 0x20, 0x20, 0x61, 0x38, + 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x20, 0x61, 0x37, 0x20, 0x20, 0x61, + 0x36, 0x20, 0x20, 0x61, 0x35, 0x20, 0x20, 0x61, 0x34, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x61, 0x33, 0x20, 0x20, 0x61, 0x32, 0x20, 0x20, 0x61, 0x31, + 0x20, 0x20, 0x61, 0x30, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x20, 0x20, + 0x6f, 0x20, 0x20, 0x20, 0x6f, 0x20, 0x20, 0x20, 0x6f, 0x20, 0x20, 0x20, + 0x6f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x20, 0x20, 0x20, 0x6f, + 0x20, 0x20, 0x20, 0x6f, 0x20, 0x20, 0x20, 0x6f, 0x22, 0x3b, 0x0a, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x77, 0x72, 0x69, 0x74, + 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x3d, 0x20, 0x22, 0x20, 0x20, 0x31, 0x20, 0x20, 0x20, 0x31, 0x20, 0x20, + 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x30, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, + 0x30, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x20, 0x20, 0x78, 0x20, 0x20, + 0x20, 0x78, 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, + 0x20, 0x20, 0x61, 0x31, 0x31, 0x20, 0x61, 0x31, 0x30, 0x20, 0x20, 0x61, + 0x39, 0x20, 0x20, 0x61, 0x38, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x20, + 0x61, 0x37, 0x20, 0x20, 0x61, 0x36, 0x20, 0x20, 0x61, 0x35, 0x20, 0x20, + 0x61, 0x34, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x33, 0x20, 0x20, 0x61, + 0x32, 0x20, 0x20, 0x61, 0x31, 0x20, 0x20, 0x61, 0x30, 0x22, 0x2c, 0x20, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x22, 0x20, 0x20, 0x69, 0x20, 0x20, 0x20, 0x69, 0x20, + 0x20, 0x20, 0x69, 0x20, 0x20, 0x20, 0x69, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x69, 0x20, 0x20, 0x20, 0x69, 0x20, 0x20, 0x20, 0x69, 0x20, 0x20, + 0x20, 0x69, 0x22, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x6c, 0x6f, 0x61, 0x64, + 0x70, 0x61, 0x67, 0x65, 0x5f, 0x6c, 0x6f, 0x20, 0x3d, 0x20, 0x22, 0x20, + 0x20, 0x31, 0x20, 0x20, 0x20, 0x31, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, + 0x20, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, + 0x30, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x31, 0x22, 0x2c, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x20, 0x20, 0x30, + 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x20, + 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x22, 0x2c, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x20, 0x20, 0x30, 0x20, 0x20, + 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x61, 0x32, 0x20, 0x20, 0x61, + 0x31, 0x20, 0x20, 0x61, 0x30, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x22, 0x20, 0x20, 0x69, 0x20, 0x20, 0x20, 0x69, + 0x20, 0x20, 0x20, 0x69, 0x20, 0x20, 0x20, 0x69, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x20, 0x20, 0x20, 0x69, 0x20, 0x20, 0x20, 0x69, 0x20, + 0x20, 0x20, 0x69, 0x22, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x77, 0x72, 0x69, + 0x74, 0x65, 0x70, 0x61, 0x67, 0x65, 0x20, 0x3d, 0x20, 0x22, 0x20, 0x20, + 0x31, 0x20, 0x20, 0x20, 0x31, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, + 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, + 0x20, 0x20, 0x20, 0x31, 0x20, 0x20, 0x20, 0x30, 0x22, 0x2c, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x20, 0x20, 0x30, 0x20, + 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, 0x78, 0x20, + 0x20, 0x20, 0x20, 0x61, 0x31, 0x31, 0x20, 0x61, 0x31, 0x30, 0x20, 0x20, + 0x61, 0x39, 0x20, 0x20, 0x61, 0x38, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x20, 0x61, 0x37, 0x20, 0x20, 0x61, + 0x36, 0x20, 0x20, 0x61, 0x35, 0x20, 0x20, 0x61, 0x34, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x61, 0x33, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, + 0x20, 0x20, 0x20, 0x30, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x22, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, 0x78, 0x20, + 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x78, 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, + 0x20, 0x78, 0x22, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x6d, 0x6f, 0x64, 0x65, + 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x30, 0x78, 0x34, 0x31, 0x3b, 0x0a, + 0x20, 0x20, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x20, 0x20, 0x20, 0x3d, 0x20, + 0x31, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, + 0x69, 0x7a, 0x65, 0x20, 0x3d, 0x20, 0x38, 0x3b, 0x0a, 0x20, 0x20, 0x72, + 0x65, 0x61, 0x64, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x20, 0x3d, 0x20, 0x32, + 0x35, 0x36, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3b, 0x0a, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x20, + 0x22, 0x66, 0x6c, 0x61, 0x73, 0x68, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x67, 0x65, 0x64, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x79, 0x65, + 0x73, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, + 0x69, 0x7a, 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x3d, 0x20, 0x32, 0x36, 0x32, 0x31, 0x34, 0x34, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x67, + 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x3d, 0x20, 0x32, 0x35, 0x36, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x6e, 0x75, 0x6d, 0x5f, 0x70, 0x61, 0x67, 0x65, + 0x73, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x31, 0x30, + 0x32, 0x34, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x6d, 0x69, 0x6e, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x64, 0x65, + 0x6c, 0x61, 0x79, 0x20, 0x3d, 0x20, 0x34, 0x35, 0x30, 0x30, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x78, 0x5f, + 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x20, + 0x3d, 0x20, 0x34, 0x35, 0x30, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x61, 0x64, 0x62, 0x61, 0x63, 0x6b, + 0x5f, 0x70, 0x31, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x30, 0x78, + 0x30, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x61, 0x64, 0x62, 0x61, 0x63, 0x6b, 0x5f, 0x70, 0x32, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x30, 0x78, 0x30, 0x30, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x61, 0x64, + 0x5f, 0x6c, 0x6f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x3d, 0x20, 0x22, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, + 0x20, 0x31, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x30, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, + 0x30, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x61, 0x31, 0x35, 0x20, 0x61, + 0x31, 0x34, 0x20, 0x61, 0x31, 0x33, 0x20, 0x61, 0x31, 0x32, 0x20, 0x20, + 0x20, 0x20, 0x61, 0x31, 0x31, 0x20, 0x61, 0x31, 0x30, 0x20, 0x20, 0x61, + 0x39, 0x20, 0x20, 0x61, 0x38, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x20, + 0x61, 0x37, 0x20, 0x20, 0x61, 0x36, 0x20, 0x20, 0x61, 0x35, 0x20, 0x20, + 0x61, 0x34, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x33, 0x20, 0x20, 0x61, + 0x32, 0x20, 0x20, 0x61, 0x31, 0x20, 0x20, 0x61, 0x30, 0x22, 0x2c, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x22, 0x20, 0x20, 0x6f, 0x20, 0x20, 0x20, 0x6f, 0x20, 0x20, + 0x20, 0x6f, 0x20, 0x20, 0x20, 0x6f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x6f, 0x20, 0x20, 0x20, 0x6f, 0x20, 0x20, 0x20, 0x6f, 0x20, 0x20, 0x20, + 0x6f, 0x22, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x68, 0x69, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x22, 0x20, 0x20, 0x30, 0x20, + 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x31, 0x20, 0x20, 0x20, 0x30, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x31, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, + 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, + 0x61, 0x31, 0x35, 0x20, 0x61, 0x31, 0x34, 0x20, 0x61, 0x31, 0x33, 0x20, + 0x61, 0x31, 0x32, 0x20, 0x20, 0x20, 0x20, 0x61, 0x31, 0x31, 0x20, 0x61, + 0x31, 0x30, 0x20, 0x20, 0x61, 0x39, 0x20, 0x20, 0x61, 0x38, 0x22, 0x2c, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x22, 0x20, 0x61, 0x37, 0x20, 0x20, 0x61, 0x36, 0x20, + 0x20, 0x61, 0x35, 0x20, 0x20, 0x61, 0x34, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x61, 0x33, 0x20, 0x20, 0x61, 0x32, 0x20, 0x20, 0x61, 0x31, 0x20, 0x20, + 0x61, 0x30, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x20, 0x20, 0x6f, 0x20, + 0x20, 0x20, 0x6f, 0x20, 0x20, 0x20, 0x6f, 0x20, 0x20, 0x20, 0x6f, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x20, 0x20, 0x20, 0x6f, 0x20, 0x20, + 0x20, 0x6f, 0x20, 0x20, 0x20, 0x6f, 0x22, 0x3b, 0x0a, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x61, + 0x67, 0x65, 0x5f, 0x6c, 0x6f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, + 0x22, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x31, 0x20, 0x20, 0x20, 0x30, + 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x20, + 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x22, + 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x22, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, 0x78, + 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, 0x78, 0x20, + 0x20, 0x20, 0x78, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x20, 0x20, 0x78, + 0x20, 0x20, 0x61, 0x36, 0x20, 0x20, 0x61, 0x35, 0x20, 0x20, 0x61, 0x34, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x33, 0x20, 0x20, 0x61, 0x32, 0x20, + 0x20, 0x61, 0x31, 0x20, 0x20, 0x61, 0x30, 0x22, 0x2c, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x22, 0x20, 0x20, 0x69, 0x20, 0x20, 0x20, 0x69, 0x20, 0x20, 0x20, 0x69, + 0x20, 0x20, 0x20, 0x69, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x20, + 0x20, 0x20, 0x69, 0x20, 0x20, 0x20, 0x69, 0x20, 0x20, 0x20, 0x69, 0x22, + 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, + 0x6f, 0x61, 0x64, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x68, 0x69, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x3d, 0x20, 0x22, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, + 0x31, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x31, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, + 0x20, 0x20, 0x20, 0x30, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x20, 0x20, + 0x78, 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, + 0x78, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, 0x78, + 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, 0x78, 0x22, 0x2c, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x22, 0x20, 0x20, 0x78, 0x20, 0x20, 0x61, 0x36, 0x20, 0x20, 0x61, + 0x35, 0x20, 0x20, 0x61, 0x34, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x33, + 0x20, 0x20, 0x61, 0x32, 0x20, 0x20, 0x61, 0x31, 0x20, 0x20, 0x61, 0x30, + 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x20, 0x20, 0x69, 0x20, 0x20, 0x20, + 0x69, 0x20, 0x20, 0x20, 0x69, 0x20, 0x20, 0x20, 0x69, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x69, 0x20, 0x20, 0x20, 0x69, 0x20, 0x20, 0x20, 0x69, + 0x20, 0x20, 0x20, 0x69, 0x22, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x77, 0x72, 0x69, 0x74, 0x65, 0x70, 0x61, 0x67, + 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x22, 0x20, + 0x20, 0x30, 0x20, 0x20, 0x20, 0x31, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, + 0x20, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x31, 0x20, 0x20, 0x20, + 0x31, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x22, 0x2c, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x22, 0x61, 0x31, 0x35, 0x20, 0x61, 0x31, 0x34, 0x20, 0x61, + 0x31, 0x33, 0x20, 0x61, 0x31, 0x32, 0x20, 0x20, 0x20, 0x20, 0x61, 0x31, + 0x31, 0x20, 0x61, 0x31, 0x30, 0x20, 0x20, 0x61, 0x39, 0x20, 0x20, 0x61, + 0x38, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x20, 0x61, 0x37, 0x20, 0x20, + 0x20, 0x78, 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, + 0x78, 0x20, 0x20, 0x20, 0x78, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x20, + 0x20, 0x78, 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, + 0x20, 0x78, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, + 0x78, 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, 0x78, 0x22, 0x3b, 0x0a, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x61, + 0x64, 0x5f, 0x65, 0x78, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x20, 0x20, + 0x20, 0x3d, 0x20, 0x22, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x31, 0x20, + 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x31, 0x20, 0x20, 0x20, 0x31, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, + 0x20, 0x31, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x20, 0x20, 0x30, 0x20, + 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, + 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, + 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x20, + 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, + 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x20, 0x61, 0x31, 0x36, 0x22, 0x2c, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x22, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x20, + 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, + 0x20, 0x30, 0x22, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x6d, 0x6f, 0x64, 0x65, + 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x30, 0x78, 0x34, 0x31, 0x3b, 0x0a, + 0x20, 0x20, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x20, 0x20, 0x20, 0x3d, 0x20, + 0x31, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, + 0x69, 0x7a, 0x65, 0x20, 0x3d, 0x20, 0x32, 0x35, 0x36, 0x3b, 0x0a, 0x20, + 0x20, 0x72, 0x65, 0x61, 0x64, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x20, 0x3d, + 0x20, 0x32, 0x35, 0x36, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x72, + 0x79, 0x20, 0x22, 0x6c, 0x66, 0x75, 0x73, 0x65, 0x22, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, + 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x77, + 0x72, 0x69, 0x74, 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x3d, 0x20, 0x22, 0x31, 0x20, 0x30, 0x20, 0x31, 0x20, + 0x30, 0x20, 0x20, 0x31, 0x20, 0x31, 0x20, 0x30, 0x20, 0x30, 0x20, 0x20, + 0x31, 0x20, 0x30, 0x20, 0x31, 0x20, 0x30, 0x20, 0x20, 0x30, 0x20, 0x30, + 0x20, 0x30, 0x20, 0x30, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x78, 0x20, + 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, + 0x20, 0x78, 0x20, 0x20, 0x69, 0x20, 0x69, 0x20, 0x69, 0x20, 0x69, 0x20, + 0x20, 0x69, 0x20, 0x69, 0x20, 0x69, 0x20, 0x69, 0x22, 0x3b, 0x0a, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x61, 0x64, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x3d, 0x20, 0x22, 0x30, 0x20, 0x31, 0x20, 0x30, 0x20, 0x31, 0x20, 0x20, + 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x20, 0x30, 0x20, 0x30, + 0x20, 0x30, 0x20, 0x30, 0x20, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, + 0x30, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x78, 0x20, 0x78, 0x20, 0x78, + 0x20, 0x78, 0x20, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, + 0x20, 0x6f, 0x20, 0x6f, 0x20, 0x6f, 0x20, 0x6f, 0x20, 0x20, 0x6f, 0x20, + 0x6f, 0x20, 0x6f, 0x20, 0x6f, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x6e, 0x5f, 0x77, 0x72, 0x69, 0x74, + 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x20, 0x3d, 0x20, 0x39, 0x30, + 0x30, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x6d, 0x61, 0x78, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x64, 0x65, + 0x6c, 0x61, 0x79, 0x20, 0x3d, 0x20, 0x39, 0x30, 0x30, 0x30, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x20, 0x22, 0x68, 0x66, 0x75, + 0x73, 0x65, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x73, 0x69, 0x7a, 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x77, 0x72, 0x69, 0x74, 0x65, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x22, + 0x31, 0x20, 0x30, 0x20, 0x31, 0x20, 0x30, 0x20, 0x20, 0x31, 0x20, 0x31, + 0x20, 0x30, 0x20, 0x30, 0x20, 0x20, 0x31, 0x20, 0x30, 0x20, 0x31, 0x20, + 0x30, 0x20, 0x20, 0x31, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x22, 0x2c, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x22, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, + 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x20, 0x69, 0x20, + 0x69, 0x20, 0x69, 0x20, 0x69, 0x20, 0x20, 0x69, 0x20, 0x69, 0x20, 0x69, + 0x20, 0x69, 0x22, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x61, 0x64, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x22, 0x30, 0x20, 0x31, + 0x20, 0x30, 0x20, 0x31, 0x20, 0x20, 0x31, 0x20, 0x30, 0x20, 0x30, 0x20, + 0x30, 0x20, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x20, + 0x31, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x22, 0x2c, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x22, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x20, 0x78, 0x20, + 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x20, 0x6f, 0x20, 0x6f, 0x20, 0x6f, + 0x20, 0x6f, 0x20, 0x20, 0x6f, 0x20, 0x6f, 0x20, 0x6f, 0x20, 0x6f, 0x22, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, + 0x6e, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x61, + 0x79, 0x20, 0x3d, 0x20, 0x39, 0x30, 0x30, 0x30, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x78, 0x5f, 0x77, 0x72, + 0x69, 0x74, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x20, 0x3d, 0x20, + 0x39, 0x30, 0x30, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x72, + 0x79, 0x20, 0x22, 0x65, 0x66, 0x75, 0x73, 0x65, 0x22, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, + 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x77, + 0x72, 0x69, 0x74, 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x3d, 0x20, 0x22, 0x31, 0x20, 0x30, 0x20, 0x31, 0x20, + 0x30, 0x20, 0x20, 0x31, 0x20, 0x31, 0x20, 0x30, 0x20, 0x30, 0x20, 0x20, + 0x31, 0x20, 0x30, 0x20, 0x31, 0x20, 0x30, 0x20, 0x20, 0x30, 0x20, 0x31, + 0x20, 0x30, 0x20, 0x30, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x78, 0x20, + 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, + 0x20, 0x78, 0x20, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, + 0x20, 0x78, 0x20, 0x69, 0x20, 0x69, 0x20, 0x69, 0x22, 0x3b, 0x0a, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x61, 0x64, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x3d, 0x20, 0x22, 0x30, 0x20, 0x31, 0x20, 0x30, 0x20, 0x31, 0x20, 0x20, + 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x20, 0x30, 0x20, 0x30, + 0x20, 0x30, 0x20, 0x30, 0x20, 0x20, 0x31, 0x20, 0x30, 0x20, 0x30, 0x20, + 0x30, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x78, 0x20, 0x78, 0x20, 0x78, + 0x20, 0x78, 0x20, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, + 0x20, 0x6f, 0x20, 0x6f, 0x20, 0x6f, 0x20, 0x6f, 0x20, 0x20, 0x6f, 0x20, + 0x6f, 0x20, 0x6f, 0x20, 0x6f, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x6e, 0x5f, 0x77, 0x72, 0x69, 0x74, + 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x20, 0x3d, 0x20, 0x39, 0x30, + 0x30, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x6d, 0x61, 0x78, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x64, 0x65, + 0x6c, 0x61, 0x79, 0x20, 0x3d, 0x20, 0x39, 0x30, 0x30, 0x30, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x20, 0x22, 0x6c, 0x6f, 0x63, + 0x6b, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, + 0x69, 0x7a, 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x3d, 0x20, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x61, 0x64, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x22, 0x30, + 0x20, 0x31, 0x20, 0x30, 0x20, 0x31, 0x20, 0x20, 0x31, 0x20, 0x30, 0x20, + 0x30, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, + 0x30, 0x20, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x22, 0x2c, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x22, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, + 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x20, 0x20, 0x78, + 0x20, 0x78, 0x20, 0x6f, 0x20, 0x6f, 0x20, 0x20, 0x6f, 0x20, 0x6f, 0x20, + 0x6f, 0x20, 0x6f, 0x22, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x77, 0x72, 0x69, 0x74, 0x65, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x22, 0x31, 0x20, + 0x30, 0x20, 0x31, 0x20, 0x30, 0x20, 0x20, 0x31, 0x20, 0x31, 0x20, 0x30, + 0x20, 0x30, 0x20, 0x20, 0x20, 0x31, 0x20, 0x31, 0x20, 0x31, 0x20, 0x78, + 0x20, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, 0x22, 0x2c, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x22, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x20, + 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x20, 0x20, 0x31, 0x20, + 0x31, 0x20, 0x69, 0x20, 0x69, 0x20, 0x20, 0x69, 0x20, 0x69, 0x20, 0x69, + 0x20, 0x69, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x6d, 0x69, 0x6e, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x64, + 0x65, 0x6c, 0x61, 0x79, 0x20, 0x3d, 0x20, 0x39, 0x30, 0x30, 0x30, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x78, + 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, + 0x20, 0x3d, 0x20, 0x39, 0x30, 0x30, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x65, + 0x6d, 0x6f, 0x72, 0x79, 0x20, 0x22, 0x63, 0x61, 0x6c, 0x69, 0x62, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x31, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x61, 0x64, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x3d, 0x20, 0x22, 0x30, 0x20, 0x30, 0x20, 0x31, 0x20, 0x31, 0x20, 0x20, + 0x31, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x20, 0x20, 0x20, 0x78, + 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x20, 0x78, 0x20, 0x78, 0x20, + 0x78, 0x20, 0x78, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x30, 0x20, 0x30, + 0x20, 0x30, 0x20, 0x30, 0x20, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, + 0x30, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x20, 0x6f, 0x20, 0x6f, 0x20, 0x6f, + 0x20, 0x20, 0x6f, 0x20, 0x6f, 0x20, 0x6f, 0x20, 0x6f, 0x22, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x20, 0x22, 0x73, 0x69, 0x67, + 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x33, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x61, + 0x64, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x3d, 0x20, 0x22, 0x30, 0x20, 0x20, 0x30, 0x20, 0x20, 0x31, 0x20, + 0x20, 0x31, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x30, 0x20, 0x20, 0x30, + 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, 0x78, 0x20, 0x20, + 0x78, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, 0x78, 0x20, + 0x20, 0x78, 0x20, 0x20, 0x78, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x78, + 0x20, 0x20, 0x78, 0x20, 0x20, 0x78, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, + 0x78, 0x20, 0x20, 0x78, 0x20, 0x61, 0x31, 0x20, 0x61, 0x30, 0x20, 0x20, + 0x20, 0x6f, 0x20, 0x20, 0x6f, 0x20, 0x20, 0x6f, 0x20, 0x20, 0x6f, 0x20, + 0x20, 0x20, 0x6f, 0x20, 0x20, 0x6f, 0x20, 0x20, 0x6f, 0x20, 0x20, 0x6f, + 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3b, 0x0a, 0x20, + 0x20, 0x3b, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x23, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x23, 0x20, + 0x41, 0x54, 0x6d, 0x65, 0x67, 0x61, 0x33, 0x32, 0x75, 0x34, 0x0a, 0x23, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x74, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, + 0x64, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x22, 0x6d, 0x33, 0x32, 0x75, 0x34, + 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x65, 0x73, 0x63, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x3d, 0x20, 0x22, 0x41, 0x54, 0x6d, 0x65, 0x67, 0x61, 0x33, 0x32, 0x55, + 0x34, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x69, 0x67, 0x6e, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x3d, 0x20, 0x30, 0x78, 0x31, 0x65, 0x20, 0x30, 0x78, 0x39, 0x35, + 0x20, 0x30, 0x78, 0x38, 0x37, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, + 0x73, 0x62, 0x70, 0x69, 0x64, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x30, 0x78, 0x32, 0x66, 0x66, 0x34, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x68, 0x61, 0x73, 0x5f, 0x6a, 0x74, + 0x61, 0x67, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, + 0x20, 0x79, 0x65, 0x73, 0x3b, 0x0a, 0x23, 0x20, 0x20, 0x20, 0x20, 0x73, + 0x74, 0x6b, 0x35, 0x30, 0x30, 0x5f, 0x64, 0x65, 0x76, 0x63, 0x6f, 0x64, + 0x65, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x30, 0x78, 0x42, 0x32, 0x3b, 0x0a, + 0x23, 0x20, 0x20, 0x20, 0x20, 0x61, 0x76, 0x72, 0x39, 0x31, 0x30, 0x5f, + 0x64, 0x65, 0x76, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x20, 0x20, 0x3d, 0x20, + 0x30, 0x78, 0x34, 0x33, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x68, + 0x69, 0x70, 0x5f, 0x65, 0x72, 0x61, 0x73, 0x65, 0x5f, 0x64, 0x65, 0x6c, + 0x61, 0x79, 0x20, 0x3d, 0x20, 0x39, 0x30, 0x30, 0x30, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x70, 0x61, 0x67, 0x65, 0x6c, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x30, 0x78, + 0x44, 0x37, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x73, 0x32, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x3d, 0x20, 0x30, 0x78, 0x41, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x73, 0x65, 0x74, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x64, 0x65, 0x64, 0x69, + 0x63, 0x61, 0x74, 0x65, 0x64, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, + 0x67, 0x6d, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x22, 0x31, 0x20, 0x30, 0x20, 0x31, + 0x20, 0x30, 0x20, 0x20, 0x31, 0x20, 0x31, 0x20, 0x30, 0x20, 0x30, 0x20, + 0x20, 0x20, 0x20, 0x30, 0x20, 0x31, 0x20, 0x30, 0x20, 0x31, 0x20, 0x20, + 0x30, 0x20, 0x30, 0x20, 0x31, 0x20, 0x31, 0x22, 0x2c, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x78, 0x20, + 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, + 0x20, 0x78, 0x20, 0x20, 0x20, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, + 0x78, 0x20, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, 0x22, 0x3b, + 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x68, 0x69, 0x70, 0x5f, 0x65, + 0x72, 0x61, 0x73, 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, + 0x20, 0x22, 0x31, 0x20, 0x30, 0x20, 0x31, 0x20, 0x30, 0x20, 0x20, 0x31, + 0x20, 0x31, 0x20, 0x30, 0x20, 0x30, 0x20, 0x20, 0x20, 0x20, 0x31, 0x20, + 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, + 0x20, 0x30, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x22, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, + 0x20, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x20, 0x20, + 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x20, 0x78, 0x20, + 0x78, 0x20, 0x78, 0x20, 0x78, 0x22, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x20, 0x20, 0x20, 0x3d, + 0x20, 0x32, 0x30, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, + 0x61, 0x62, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x20, 0x20, 0x20, 0x3d, 0x20, + 0x31, 0x30, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6d, 0x64, + 0x65, 0x78, 0x65, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x20, 0x20, 0x20, 0x3d, + 0x20, 0x32, 0x35, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x79, 0x6e, + 0x63, 0x68, 0x6c, 0x6f, 0x6f, 0x70, 0x73, 0x20, 0x20, 0x20, 0x20, 0x3d, + 0x20, 0x33, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x79, 0x74, + 0x65, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x30, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x6f, 0x6c, 0x6c, 0x69, 0x6e, + 0x64, 0x65, 0x78, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x33, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x70, 0x6f, 0x6c, 0x6c, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x20, 0x20, 0x20, 0x3d, 0x20, 0x30, 0x78, 0x35, 0x33, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x70, 0x72, 0x65, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x20, + 0x20, 0x20, 0x20, 0x3d, 0x20, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x70, 0x6f, 0x73, 0x74, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x20, 0x20, 0x20, + 0x3d, 0x20, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x6f, 0x6c, + 0x6c, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x20, 0x20, 0x20, 0x20, 0x3d, + 0x20, 0x31, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x70, 0x5f, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x73, 0x74, 0x61, 0x63, 0x6b, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x30, 0x78, 0x30, 0x45, 0x2c, 0x20, 0x30, 0x78, 0x31, + 0x45, 0x2c, 0x20, 0x30, 0x78, 0x30, 0x46, 0x2c, 0x20, 0x30, 0x78, 0x31, + 0x46, 0x2c, 0x20, 0x30, 0x78, 0x32, 0x45, 0x2c, 0x20, 0x30, 0x78, 0x33, + 0x45, 0x2c, 0x20, 0x30, 0x78, 0x32, 0x46, 0x2c, 0x20, 0x30, 0x78, 0x33, + 0x46, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, + 0x78, 0x34, 0x45, 0x2c, 0x20, 0x30, 0x78, 0x35, 0x45, 0x2c, 0x20, 0x30, + 0x78, 0x34, 0x46, 0x2c, 0x20, 0x30, 0x78, 0x35, 0x46, 0x2c, 0x20, 0x30, + 0x78, 0x36, 0x45, 0x2c, 0x20, 0x30, 0x78, 0x37, 0x45, 0x2c, 0x20, 0x30, + 0x78, 0x36, 0x46, 0x2c, 0x20, 0x30, 0x78, 0x37, 0x46, 0x2c, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x78, 0x36, 0x36, 0x2c, + 0x20, 0x30, 0x78, 0x37, 0x36, 0x2c, 0x20, 0x30, 0x78, 0x36, 0x37, 0x2c, + 0x20, 0x30, 0x78, 0x37, 0x37, 0x2c, 0x20, 0x30, 0x78, 0x36, 0x41, 0x2c, + 0x20, 0x30, 0x78, 0x37, 0x41, 0x2c, 0x20, 0x30, 0x78, 0x36, 0x42, 0x2c, + 0x20, 0x30, 0x78, 0x37, 0x42, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x30, 0x78, 0x42, 0x45, 0x2c, 0x20, 0x30, 0x78, 0x46, + 0x44, 0x2c, 0x20, 0x30, 0x78, 0x30, 0x30, 0x2c, 0x20, 0x30, 0x78, 0x30, + 0x31, 0x2c, 0x20, 0x30, 0x78, 0x30, 0x30, 0x2c, 0x20, 0x30, 0x78, 0x30, + 0x30, 0x2c, 0x20, 0x30, 0x78, 0x30, 0x30, 0x2c, 0x20, 0x30, 0x78, 0x30, + 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x68, 0x76, 0x65, 0x6e, 0x74, + 0x65, 0x72, 0x73, 0x74, 0x61, 0x62, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x20, + 0x20, 0x20, 0x20, 0x3d, 0x20, 0x31, 0x30, 0x30, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x6d, 0x6f, 0x64, 0x65, 0x64, 0x65, + 0x6c, 0x61, 0x79, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, + 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x61, 0x74, 0x63, 0x68, + 0x63, 0x79, 0x63, 0x6c, 0x65, 0x73, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x3d, 0x20, 0x35, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x74, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x76, 0x74, 0x67, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x31, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x6f, 0x77, 0x65, 0x72, 0x6f, 0x66, + 0x66, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x3d, 0x20, 0x31, 0x35, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, + 0x65, 0x73, 0x65, 0x74, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x6d, 0x73, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x31, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x65, 0x74, 0x64, 0x65, 0x6c, + 0x61, 0x79, 0x75, 0x73, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x68, 0x76, 0x6c, + 0x65, 0x61, 0x76, 0x65, 0x73, 0x74, 0x61, 0x62, 0x64, 0x65, 0x6c, 0x61, + 0x79, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x31, 0x35, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x63, 0x68, 0x69, 0x70, 0x65, 0x72, 0x61, 0x73, 0x65, + 0x70, 0x75, 0x6c, 0x73, 0x65, 0x77, 0x69, 0x64, 0x74, 0x68, 0x20, 0x3d, + 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x68, 0x69, 0x70, + 0x65, 0x72, 0x61, 0x73, 0x65, 0x70, 0x6f, 0x6c, 0x6c, 0x74, 0x69, 0x6d, + 0x65, 0x6f, 0x75, 0x74, 0x20, 0x3d, 0x20, 0x31, 0x30, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x66, 0x75, + 0x73, 0x65, 0x70, 0x75, 0x6c, 0x73, 0x65, 0x77, 0x69, 0x64, 0x74, 0x68, + 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, + 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x66, 0x75, 0x73, 0x65, 0x70, 0x6f, 0x6c, + 0x6c, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x20, 0x3d, 0x20, 0x35, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, + 0x6d, 0x6c, 0x6f, 0x63, 0x6b, 0x70, 0x75, 0x6c, 0x73, 0x65, 0x77, 0x69, + 0x64, 0x74, 0x68, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6c, 0x6f, 0x63, 0x6b, + 0x70, 0x6f, 0x6c, 0x6c, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x20, + 0x3d, 0x20, 0x35, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x64, + 0x72, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x30, 0x78, 0x33, 0x31, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x70, 0x6d, 0x63, 0x72, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x3d, 0x20, 0x30, 0x78, 0x35, 0x37, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x61, 0x6d, 0x70, 0x7a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, + 0x30, 0x78, 0x33, 0x62, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x6c, + 0x6c, 0x6f, 0x77, 0x66, 0x75, 0x6c, 0x6c, 0x70, 0x61, 0x67, 0x65, 0x62, + 0x69, 0x74, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x20, 0x3d, 0x20, 0x6e, + 0x6f, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x63, 0x64, 0x72, + 0x65, 0x76, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x33, 0x3b, 0x0a, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x20, 0x22, 0x65, 0x65, + 0x70, 0x72, 0x6f, 0x6d, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x70, 0x61, 0x67, 0x65, 0x64, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x6e, 0x6f, 0x3b, 0x20, + 0x2f, 0x2a, 0x20, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x20, 0x74, 0x68, 0x69, + 0x73, 0x20, 0x22, 0x6e, 0x6f, 0x22, 0x20, 0x2a, 0x2f, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x73, + 0x69, 0x7a, 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, + 0x34, 0x3b, 0x20, 0x20, 0x2f, 0x2a, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x70, + 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x20, 0x70, 0x72, 0x6f, 0x67, + 0x72, 0x61, 0x6d, 0x6d, 0x69, 0x6e, 0x67, 0x20, 0x2a, 0x2f, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, + 0x20, 0x31, 0x30, 0x32, 0x34, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x6d, 0x69, 0x6e, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, + 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x20, 0x3d, 0x20, 0x39, 0x30, 0x30, + 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, + 0x61, 0x78, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x64, 0x65, 0x6c, + 0x61, 0x79, 0x20, 0x3d, 0x20, 0x39, 0x30, 0x30, 0x30, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x61, 0x64, 0x62, + 0x61, 0x63, 0x6b, 0x5f, 0x70, 0x31, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, + 0x20, 0x30, 0x78, 0x30, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x72, 0x65, 0x61, 0x64, 0x62, 0x61, 0x63, 0x6b, 0x5f, + 0x70, 0x32, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x30, 0x78, 0x30, + 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, + 0x65, 0x61, 0x64, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x3d, 0x20, 0x22, 0x20, 0x20, 0x31, 0x20, 0x20, 0x20, + 0x30, 0x20, 0x20, 0x20, 0x31, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, + 0x20, 0x20, 0x20, 0x30, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x20, 0x20, + 0x78, 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, + 0x78, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x20, 0x61, 0x31, 0x30, + 0x20, 0x20, 0x61, 0x39, 0x20, 0x20, 0x61, 0x38, 0x22, 0x2c, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x22, 0x20, 0x61, 0x37, 0x20, 0x20, 0x61, 0x36, 0x20, 0x20, 0x61, + 0x35, 0x20, 0x20, 0x61, 0x34, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x33, + 0x20, 0x20, 0x61, 0x32, 0x20, 0x20, 0x61, 0x31, 0x20, 0x20, 0x61, 0x30, + 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x20, 0x20, 0x6f, 0x20, 0x20, 0x20, + 0x6f, 0x20, 0x20, 0x20, 0x6f, 0x20, 0x20, 0x20, 0x6f, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x6f, 0x20, 0x20, 0x20, 0x6f, 0x20, 0x20, 0x20, 0x6f, + 0x20, 0x20, 0x20, 0x6f, 0x22, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x77, 0x72, 0x69, 0x74, 0x65, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x22, 0x20, + 0x20, 0x31, 0x20, 0x20, 0x20, 0x31, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, + 0x20, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, + 0x30, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x22, 0x2c, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x22, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, + 0x20, 0x78, 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x78, 0x20, 0x61, 0x31, 0x30, 0x20, 0x20, 0x61, 0x39, 0x20, 0x20, 0x61, + 0x38, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x20, 0x61, 0x37, 0x20, 0x20, + 0x61, 0x36, 0x20, 0x20, 0x61, 0x35, 0x20, 0x20, 0x61, 0x34, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x61, 0x33, 0x20, 0x20, 0x61, 0x32, 0x20, 0x20, 0x61, + 0x31, 0x20, 0x20, 0x61, 0x30, 0x22, 0x2c, 0x20, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, + 0x20, 0x20, 0x69, 0x20, 0x20, 0x20, 0x69, 0x20, 0x20, 0x20, 0x69, 0x20, + 0x20, 0x20, 0x69, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x20, 0x20, + 0x20, 0x69, 0x20, 0x20, 0x20, 0x69, 0x20, 0x20, 0x20, 0x69, 0x22, 0x3b, + 0x0a, 0x0a, 0x20, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x61, 0x67, 0x65, + 0x5f, 0x6c, 0x6f, 0x20, 0x3d, 0x20, 0x22, 0x20, 0x20, 0x31, 0x20, 0x20, + 0x20, 0x31, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, + 0x30, 0x20, 0x20, 0x20, 0x31, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x22, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, + 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x20, + 0x20, 0x20, 0x30, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x22, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, + 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x30, 0x20, 0x20, 0x61, 0x32, 0x20, 0x20, 0x61, 0x31, 0x20, 0x20, 0x61, + 0x30, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x22, 0x20, 0x20, 0x69, 0x20, 0x20, 0x20, 0x69, 0x20, 0x20, 0x20, 0x69, + 0x20, 0x20, 0x20, 0x69, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x20, + 0x20, 0x20, 0x69, 0x20, 0x20, 0x20, 0x69, 0x20, 0x20, 0x20, 0x69, 0x22, + 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x77, 0x72, 0x69, 0x74, 0x65, 0x70, 0x61, + 0x67, 0x65, 0x20, 0x3d, 0x20, 0x22, 0x20, 0x20, 0x31, 0x20, 0x20, 0x20, + 0x31, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x31, + 0x20, 0x20, 0x20, 0x30, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x22, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x20, + 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x78, 0x20, 0x61, 0x31, 0x30, 0x20, 0x20, 0x61, 0x39, 0x20, 0x20, + 0x61, 0x38, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x22, 0x20, 0x61, 0x37, 0x20, 0x20, 0x61, 0x36, 0x20, 0x20, 0x61, + 0x35, 0x20, 0x20, 0x61, 0x34, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x33, + 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, + 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, + 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, 0x78, 0x20, + 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, + 0x20, 0x78, 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, 0x78, 0x22, 0x3b, + 0x0a, 0x0a, 0x20, 0x20, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x20, 0x20, 0x20, + 0x3d, 0x20, 0x30, 0x78, 0x34, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x64, 0x65, + 0x6c, 0x61, 0x79, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x32, 0x30, 0x3b, 0x0a, + 0x20, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x69, 0x7a, 0x65, 0x20, + 0x3d, 0x20, 0x34, 0x3b, 0x0a, 0x20, 0x20, 0x72, 0x65, 0x61, 0x64, 0x73, + 0x69, 0x7a, 0x65, 0x20, 0x20, 0x3d, 0x20, 0x32, 0x35, 0x36, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x20, 0x22, 0x66, 0x6c, 0x61, + 0x73, 0x68, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x70, 0x61, 0x67, 0x65, 0x64, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x79, 0x65, 0x73, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, + 0x20, 0x33, 0x32, 0x37, 0x36, 0x38, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x69, 0x7a, + 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x31, 0x32, + 0x38, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6e, + 0x75, 0x6d, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x73, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x3d, 0x20, 0x32, 0x35, 0x36, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x6e, 0x5f, 0x77, 0x72, + 0x69, 0x74, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x20, 0x3d, 0x20, + 0x34, 0x35, 0x30, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x6d, 0x61, 0x78, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, + 0x64, 0x65, 0x6c, 0x61, 0x79, 0x20, 0x3d, 0x20, 0x34, 0x35, 0x30, 0x30, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x61, 0x64, 0x62, 0x61, 0x63, 0x6b, 0x5f, 0x70, 0x31, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x3d, 0x20, 0x30, 0x78, 0x30, 0x30, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x61, 0x64, 0x62, 0x61, + 0x63, 0x6b, 0x5f, 0x70, 0x32, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, + 0x30, 0x78, 0x30, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x6c, 0x6f, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x22, 0x20, 0x20, 0x30, + 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x31, 0x20, 0x20, 0x20, 0x30, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x20, + 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x22, 0x2c, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x22, 0x20, 0x20, 0x30, 0x20, 0x61, 0x31, 0x34, 0x20, 0x61, 0x31, 0x33, + 0x20, 0x61, 0x31, 0x32, 0x20, 0x20, 0x20, 0x20, 0x61, 0x31, 0x31, 0x20, + 0x61, 0x31, 0x30, 0x20, 0x20, 0x61, 0x39, 0x20, 0x20, 0x61, 0x38, 0x22, + 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x22, 0x20, 0x61, 0x37, 0x20, 0x20, 0x61, 0x36, + 0x20, 0x20, 0x61, 0x35, 0x20, 0x20, 0x61, 0x34, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x61, 0x33, 0x20, 0x20, 0x61, 0x32, 0x20, 0x20, 0x61, 0x31, 0x20, + 0x20, 0x61, 0x30, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x20, 0x20, 0x6f, + 0x20, 0x20, 0x20, 0x6f, 0x20, 0x20, 0x20, 0x6f, 0x20, 0x20, 0x20, 0x6f, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x20, 0x20, 0x20, 0x6f, 0x20, + 0x20, 0x20, 0x6f, 0x20, 0x20, 0x20, 0x6f, 0x22, 0x3b, 0x0a, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x61, 0x64, 0x5f, + 0x68, 0x69, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, + 0x20, 0x22, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, + 0x31, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x31, + 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, + 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x20, 0x20, 0x30, 0x20, 0x61, 0x31, + 0x34, 0x20, 0x61, 0x31, 0x33, 0x20, 0x61, 0x31, 0x32, 0x20, 0x20, 0x20, + 0x20, 0x61, 0x31, 0x31, 0x20, 0x61, 0x31, 0x30, 0x20, 0x20, 0x61, 0x39, + 0x20, 0x20, 0x61, 0x38, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x20, 0x61, + 0x37, 0x20, 0x20, 0x61, 0x36, 0x20, 0x20, 0x61, 0x35, 0x20, 0x20, 0x61, + 0x34, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x33, 0x20, 0x20, 0x61, 0x32, + 0x20, 0x20, 0x61, 0x31, 0x20, 0x20, 0x61, 0x30, 0x22, 0x2c, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x22, 0x20, 0x20, 0x6f, 0x20, 0x20, 0x20, 0x6f, 0x20, 0x20, 0x20, + 0x6f, 0x20, 0x20, 0x20, 0x6f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6f, + 0x20, 0x20, 0x20, 0x6f, 0x20, 0x20, 0x20, 0x6f, 0x20, 0x20, 0x20, 0x6f, + 0x22, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x6c, 0x6f, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x22, 0x20, 0x20, 0x30, 0x20, 0x20, + 0x20, 0x31, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, + 0x30, 0x20, 0x20, 0x20, 0x30, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x20, + 0x20, 0x78, 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, + 0x20, 0x78, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, + 0x78, 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, 0x78, 0x22, 0x2c, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x22, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, + 0x61, 0x35, 0x20, 0x20, 0x61, 0x34, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, + 0x33, 0x20, 0x20, 0x61, 0x32, 0x20, 0x20, 0x61, 0x31, 0x20, 0x20, 0x61, + 0x30, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x20, 0x20, 0x69, 0x20, 0x20, + 0x20, 0x69, 0x20, 0x20, 0x20, 0x69, 0x20, 0x20, 0x20, 0x69, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x69, 0x20, 0x20, 0x20, 0x69, 0x20, 0x20, 0x20, + 0x69, 0x20, 0x20, 0x20, 0x69, 0x22, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x61, 0x67, + 0x65, 0x5f, 0x68, 0x69, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x22, + 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x31, 0x20, 0x20, 0x20, 0x30, 0x20, + 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x31, 0x20, 0x20, + 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x22, 0x2c, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x22, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, 0x78, 0x20, + 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x78, 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, + 0x20, 0x78, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x20, 0x20, 0x78, 0x20, + 0x20, 0x20, 0x78, 0x20, 0x20, 0x61, 0x35, 0x20, 0x20, 0x61, 0x34, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x61, 0x33, 0x20, 0x20, 0x61, 0x32, 0x20, 0x20, + 0x61, 0x31, 0x20, 0x20, 0x61, 0x30, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, + 0x20, 0x20, 0x69, 0x20, 0x20, 0x20, 0x69, 0x20, 0x20, 0x20, 0x69, 0x20, + 0x20, 0x20, 0x69, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x20, 0x20, + 0x20, 0x69, 0x20, 0x20, 0x20, 0x69, 0x20, 0x20, 0x20, 0x69, 0x22, 0x3b, + 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x77, 0x72, + 0x69, 0x74, 0x65, 0x70, 0x61, 0x67, 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x3d, 0x20, 0x22, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x31, + 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x31, 0x20, 0x20, 0x20, 0x31, 0x20, 0x20, 0x20, 0x30, 0x20, + 0x20, 0x20, 0x30, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x20, 0x61, 0x31, + 0x35, 0x20, 0x61, 0x31, 0x34, 0x20, 0x61, 0x31, 0x33, 0x20, 0x61, 0x31, + 0x32, 0x20, 0x20, 0x20, 0x20, 0x61, 0x31, 0x31, 0x20, 0x61, 0x31, 0x30, + 0x20, 0x20, 0x61, 0x39, 0x20, 0x20, 0x61, 0x38, 0x22, 0x2c, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x22, 0x20, 0x61, 0x37, 0x20, 0x20, 0x61, 0x36, 0x20, 0x20, 0x20, + 0x78, 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, + 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, 0x78, + 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, + 0x78, 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, 0x78, + 0x20, 0x20, 0x20, 0x78, 0x22, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x6d, 0x6f, + 0x64, 0x65, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x30, 0x78, 0x34, 0x31, + 0x3b, 0x0a, 0x20, 0x20, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x20, 0x20, 0x20, + 0x3d, 0x20, 0x36, 0x3b, 0x0a, 0x20, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x73, 0x69, 0x7a, 0x65, 0x20, 0x3d, 0x20, 0x31, 0x32, 0x38, 0x3b, 0x0a, + 0x20, 0x20, 0x72, 0x65, 0x61, 0x64, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x20, + 0x3d, 0x20, 0x32, 0x35, 0x36, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x65, 0x6d, 0x6f, + 0x72, 0x79, 0x20, 0x22, 0x6c, 0x66, 0x75, 0x73, 0x65, 0x22, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, + 0x20, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x77, 0x72, 0x69, 0x74, 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x22, 0x31, 0x20, 0x30, 0x20, 0x31, + 0x20, 0x30, 0x20, 0x20, 0x31, 0x20, 0x31, 0x20, 0x30, 0x20, 0x30, 0x20, + 0x20, 0x31, 0x20, 0x30, 0x20, 0x31, 0x20, 0x30, 0x20, 0x20, 0x30, 0x20, + 0x30, 0x20, 0x30, 0x20, 0x30, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x78, + 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x20, 0x78, 0x20, 0x78, 0x20, + 0x78, 0x20, 0x78, 0x20, 0x20, 0x69, 0x20, 0x69, 0x20, 0x69, 0x20, 0x69, + 0x20, 0x20, 0x69, 0x20, 0x69, 0x20, 0x69, 0x20, 0x69, 0x22, 0x3b, 0x0a, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x61, + 0x64, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x3d, 0x20, 0x22, 0x30, 0x20, 0x31, 0x20, 0x30, 0x20, 0x31, 0x20, + 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x20, 0x30, 0x20, + 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, + 0x20, 0x30, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x78, 0x20, 0x78, 0x20, + 0x78, 0x20, 0x78, 0x20, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, + 0x20, 0x20, 0x6f, 0x20, 0x6f, 0x20, 0x6f, 0x20, 0x6f, 0x20, 0x20, 0x6f, + 0x20, 0x6f, 0x20, 0x6f, 0x20, 0x6f, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x6e, 0x5f, 0x77, 0x72, 0x69, + 0x74, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x20, 0x3d, 0x20, 0x39, + 0x30, 0x30, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x6d, 0x61, 0x78, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x64, + 0x65, 0x6c, 0x61, 0x79, 0x20, 0x3d, 0x20, 0x39, 0x30, 0x30, 0x30, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3b, 0x0a, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x20, 0x22, 0x68, 0x66, + 0x75, 0x73, 0x65, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x31, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x77, 0x72, 0x69, 0x74, 0x65, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, + 0x22, 0x31, 0x20, 0x30, 0x20, 0x31, 0x20, 0x30, 0x20, 0x20, 0x31, 0x20, + 0x31, 0x20, 0x30, 0x20, 0x30, 0x20, 0x20, 0x31, 0x20, 0x30, 0x20, 0x31, + 0x20, 0x30, 0x20, 0x20, 0x31, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x22, + 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x22, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, + 0x20, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x20, 0x69, + 0x20, 0x69, 0x20, 0x69, 0x20, 0x69, 0x20, 0x20, 0x69, 0x20, 0x69, 0x20, + 0x69, 0x20, 0x69, 0x22, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x72, 0x65, 0x61, 0x64, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x22, 0x30, 0x20, + 0x31, 0x20, 0x30, 0x20, 0x31, 0x20, 0x20, 0x31, 0x20, 0x30, 0x20, 0x30, + 0x20, 0x30, 0x20, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, + 0x20, 0x31, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x22, 0x2c, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x22, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x20, 0x78, + 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x20, 0x6f, 0x20, 0x6f, 0x20, + 0x6f, 0x20, 0x6f, 0x20, 0x20, 0x6f, 0x20, 0x6f, 0x20, 0x6f, 0x20, 0x6f, + 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, + 0x69, 0x6e, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x64, 0x65, 0x6c, + 0x61, 0x79, 0x20, 0x3d, 0x20, 0x39, 0x30, 0x30, 0x30, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x78, 0x5f, 0x77, + 0x72, 0x69, 0x74, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x20, 0x3d, + 0x20, 0x39, 0x30, 0x30, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x65, 0x6d, 0x6f, + 0x72, 0x79, 0x20, 0x22, 0x65, 0x66, 0x75, 0x73, 0x65, 0x22, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, + 0x20, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x77, 0x72, 0x69, 0x74, 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x22, 0x31, 0x20, 0x30, 0x20, 0x31, + 0x20, 0x30, 0x20, 0x20, 0x31, 0x20, 0x31, 0x20, 0x30, 0x20, 0x30, 0x20, + 0x20, 0x31, 0x20, 0x30, 0x20, 0x31, 0x20, 0x30, 0x20, 0x20, 0x30, 0x20, + 0x31, 0x20, 0x30, 0x20, 0x30, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x78, + 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x20, 0x78, 0x20, 0x78, 0x20, + 0x78, 0x20, 0x78, 0x20, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, + 0x20, 0x20, 0x69, 0x20, 0x69, 0x20, 0x69, 0x20, 0x69, 0x22, 0x3b, 0x0a, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x61, + 0x64, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x3d, 0x20, 0x22, 0x30, 0x20, 0x31, 0x20, 0x30, 0x20, 0x31, 0x20, + 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x20, 0x30, 0x20, + 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x20, 0x31, 0x20, 0x30, 0x20, 0x30, + 0x20, 0x30, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x78, 0x20, 0x78, 0x20, + 0x78, 0x20, 0x78, 0x20, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, + 0x20, 0x20, 0x6f, 0x20, 0x6f, 0x20, 0x6f, 0x20, 0x6f, 0x20, 0x20, 0x6f, + 0x20, 0x6f, 0x20, 0x6f, 0x20, 0x6f, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x6e, 0x5f, 0x77, 0x72, 0x69, + 0x74, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x20, 0x3d, 0x20, 0x39, + 0x30, 0x30, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x6d, 0x61, 0x78, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x64, + 0x65, 0x6c, 0x61, 0x79, 0x20, 0x3d, 0x20, 0x39, 0x30, 0x30, 0x30, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3b, 0x0a, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x20, 0x22, 0x6c, 0x6f, + 0x63, 0x6b, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x73, 0x69, 0x7a, 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x61, 0x64, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x22, + 0x30, 0x20, 0x31, 0x20, 0x30, 0x20, 0x31, 0x20, 0x20, 0x31, 0x20, 0x30, + 0x20, 0x30, 0x20, 0x30, 0x20, 0x20, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, + 0x20, 0x30, 0x20, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x22, + 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x22, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, + 0x20, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x20, 0x20, + 0x78, 0x20, 0x78, 0x20, 0x6f, 0x20, 0x6f, 0x20, 0x20, 0x6f, 0x20, 0x6f, + 0x20, 0x6f, 0x20, 0x6f, 0x22, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x77, 0x72, 0x69, 0x74, 0x65, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x22, 0x31, + 0x20, 0x30, 0x20, 0x31, 0x20, 0x30, 0x20, 0x20, 0x31, 0x20, 0x31, 0x20, + 0x30, 0x20, 0x30, 0x20, 0x20, 0x20, 0x31, 0x20, 0x31, 0x20, 0x31, 0x20, + 0x78, 0x20, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, 0x22, 0x2c, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x22, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, + 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x20, 0x20, 0x31, + 0x20, 0x31, 0x20, 0x69, 0x20, 0x69, 0x20, 0x20, 0x69, 0x20, 0x69, 0x20, + 0x69, 0x20, 0x69, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x6d, 0x69, 0x6e, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, + 0x64, 0x65, 0x6c, 0x61, 0x79, 0x20, 0x3d, 0x20, 0x39, 0x30, 0x30, 0x30, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, + 0x78, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x61, + 0x79, 0x20, 0x3d, 0x20, 0x39, 0x30, 0x30, 0x30, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d, + 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x20, 0x22, 0x63, 0x61, 0x6c, 0x69, 0x62, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x31, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x61, + 0x64, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x3d, 0x20, 0x22, 0x30, 0x20, 0x30, 0x20, 0x31, 0x20, 0x31, 0x20, + 0x20, 0x31, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x20, 0x20, 0x20, + 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x78, 0x20, 0x20, 0x78, 0x20, 0x78, + 0x20, 0x78, 0x20, 0x78, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x30, 0x20, + 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, + 0x20, 0x30, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x20, 0x6f, 0x20, 0x6f, 0x20, + 0x6f, 0x20, 0x20, 0x6f, 0x20, 0x6f, 0x20, 0x6f, 0x20, 0x6f, 0x22, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3b, 0x0a, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x20, 0x22, 0x73, 0x69, + 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x33, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x61, 0x64, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x3d, 0x20, 0x22, 0x30, 0x20, 0x20, 0x30, 0x20, 0x20, 0x31, + 0x20, 0x20, 0x31, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x30, 0x20, 0x20, + 0x30, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, 0x78, 0x20, + 0x20, 0x78, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, 0x78, + 0x20, 0x20, 0x78, 0x20, 0x20, 0x78, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, + 0x78, 0x20, 0x20, 0x78, 0x20, 0x20, 0x78, 0x20, 0x20, 0x78, 0x20, 0x20, + 0x20, 0x78, 0x20, 0x20, 0x78, 0x20, 0x61, 0x31, 0x20, 0x61, 0x30, 0x20, + 0x20, 0x20, 0x6f, 0x20, 0x20, 0x6f, 0x20, 0x20, 0x6f, 0x20, 0x20, 0x6f, + 0x20, 0x20, 0x20, 0x6f, 0x20, 0x20, 0x6f, 0x20, 0x20, 0x6f, 0x20, 0x20, + 0x6f, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3b, 0x0a, + 0x20, 0x20, 0x3b, 0x0a, 0x0a, 0x0a, + 0, 0 +}; +size_t avrdude_slic3r_conf_size = 14178; +size_t avrdude_slic3r_conf_size_yy = 14180; diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index efb381ca8..fd1e9e08c 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -342,8 +342,13 @@ void ConfigBase::apply_only(const ConfigBase &other, const t_config_option_keys if (other_opt == nullptr) { // The key was not found in the source config, therefore it will not be initialized! // printf("Not found, therefore not initialized: %s\n", opt_key.c_str()); - } else - my_opt->set(other_opt); + } else { + try { + my_opt->set(other_opt); + } catch (ConfigurationException e) { + throw ConfigurationException(std::string(e.what()) + ", when ConfigBase::apply_only on " + opt_key); + } + } } } diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index e6f2ca6cb..bf768159d 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -46,6 +46,15 @@ public: NoDefinitionException(const std::string &opt_key) : std::runtime_error(std::string("No definition exception: ") + opt_key) {} }; +// a bit more specific than a runtime_error +class ConfigurationException : public std::runtime_error +{ +public: + ConfigurationException() : + std::runtime_error("Configuration exception") {} + ConfigurationException(const std::string &opt_key) : + std::runtime_error(std::string("Configuration exception: ") + opt_key) {} +}; // Type of a configuration value. enum ConfigOptionType { @@ -650,7 +659,7 @@ public: void set(const ConfigOption *rhs) override { if (rhs->type() != this->type()) - throw std::runtime_error("ConfigOptionFloatOrPercent: Assigning an incompatible type"); + throw ConfigurationException("ConfigOptionFloatOrPercent: Assigning an incompatible type"); assert(dynamic_cast(rhs)); *this = *static_cast(rhs); } diff --git a/src/libslic3r/Flow.cpp b/src/libslic3r/Flow.cpp index 5ed8ec5d5..c45ee30c8 100644 --- a/src/libslic3r/Flow.cpp +++ b/src/libslic3r/Flow.cpp @@ -12,9 +12,9 @@ static inline float auto_extrusion_width(FlowRole role, float nozzle_diameter, f case frSupportMaterial: case frSupportMaterialInterface: case frTopSolidInfill: - return nozzle_diameter; - default: case frExternalPerimeter: + return 1.05f * nozzle_diameter; + default: case frPerimeter: case frSolidInfill: case frInfill: diff --git a/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp index ff1da37cc..2496c221c 100644 --- a/src/libslic3r/Format/AMF.cpp +++ b/src/libslic3r/Format/AMF.cpp @@ -841,20 +841,19 @@ bool load_amf(const char *path, DynamicPrintConfig *config, Model *model) return false; } -bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config) +bool store_amf(std::string &path, Model *model, const DynamicPrintConfig *config) { - if ((path == nullptr) || (model == nullptr)) + if ((path.empty()) || (model == nullptr)) return false; // forces ".zip.amf" extension - std::string export_path = path; - if (!boost::iends_with(export_path, ".zip.amf")) - export_path = boost::filesystem::path(export_path).replace_extension(".zip.amf").string(); + if (!boost::iends_with(path, ".zip.amf")) + path = boost::filesystem::path(path).replace_extension(".zip.amf").string(); mz_zip_archive archive; mz_zip_zero_struct(&archive); - mz_bool res = mz_zip_writer_init_file(&archive, export_path.c_str(), 0); + mz_bool res = mz_zip_writer_init_file(&archive, path.c_str(), 0); if (res == 0) return false; @@ -1013,20 +1012,20 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config) } stream << "\n"; - std::string internal_amf_filename = boost::ireplace_last_copy(boost::filesystem::path(export_path).filename().string(), ".zip.amf", ".amf"); + std::string internal_amf_filename = boost::ireplace_last_copy(boost::filesystem::path(path).filename().string(), ".zip.amf", ".amf"); std::string out = stream.str(); if (!mz_zip_writer_add_mem(&archive, internal_amf_filename.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) { mz_zip_writer_end(&archive); - boost::filesystem::remove(export_path); + boost::filesystem::remove(path); return false; } if (!mz_zip_writer_finalize_archive(&archive)) { mz_zip_writer_end(&archive); - boost::filesystem::remove(export_path); + boost::filesystem::remove(path); return false; } diff --git a/src/libslic3r/Format/AMF.hpp b/src/libslic3r/Format/AMF.hpp index e085ad22e..d7b08edeb 100644 --- a/src/libslic3r/Format/AMF.hpp +++ b/src/libslic3r/Format/AMF.hpp @@ -11,7 +11,7 @@ extern bool load_amf(const char *path, DynamicPrintConfig *config, Model *model) // Save the given model and the config data into an amf file. // The model could be modified during the export process if meshes are not repaired or have no shared vertices -extern bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config); +extern bool store_amf(std::string &path, Model *model, const DynamicPrintConfig *config); }; // namespace Slic3r diff --git a/src/libslic3r/Point.hpp b/src/libslic3r/Point.hpp index 0d4cea0e7..112918ece 100644 --- a/src/libslic3r/Point.hpp +++ b/src/libslic3r/Point.hpp @@ -94,6 +94,7 @@ public: template Point(const Eigen::MatrixBase &other) : Vec2crd(other) {} static Point new_scale(coordf_t x, coordf_t y) { return Point(coord_t(scale_(x)), coord_t(scale_(y))); } + static Point new_scale(const Point &p) { return Point(coord_t(scale_(p.x())), coord_t(scale_(p.y()))); } // This method allows you to assign Eigen expressions to MyVectorType template diff --git a/src/slic3r.cpp b/src/slic3r.cpp index 3046e3b79..7b37ba56c 100644 --- a/src/slic3r.cpp +++ b/src/slic3r.cpp @@ -555,7 +555,7 @@ bool CLI::setup(int argc, char **argv) void CLI::print_help(bool include_print_options, PrinterTechnology printer_technology) const { boost::nowide::cout - << "Slic3r Prusa Edition " << SLIC3R_BUILD << std::endl + << "Slic3r++ " << SLIC3R_BUILD << std::endl << "https://github.com/prusa3d/Slic3r" << std::endl << std::endl << "Usage: slic3r [ ACTIONS ] [ TRANSFORM ] [ OPTIONS ] [ file.stl ... ]" << std::endl << std::endl @@ -586,10 +586,10 @@ void CLI::print_help(bool include_print_options, PrinterTechnology printer_techn bool CLI::export_models(IO::ExportFormat format) { for (Model &model : m_models) { - const std::string path = this->output_filepath(model, format); + std::string path = this->output_filepath(model, format); bool success = false; switch (format) { - case IO::AMF: success = Slic3r::store_amf(path.c_str(), &model, nullptr); break; + case IO::AMF: success = Slic3r::store_amf(path, &model, nullptr); break; case IO::OBJ: success = Slic3r::store_obj(path.c_str(), &model); break; case IO::STL: success = Slic3r::store_stl(path.c_str(), &model, true); break; case IO::TMF: success = Slic3r::store_3mf(path.c_str(), &model, nullptr); break; diff --git a/src/slic3r.hpp b/src/slic3r.hpp index a775e7b9d..6be1d148b 100644 --- a/src/slic3r.hpp +++ b/src/slic3r.hpp @@ -21,6 +21,8 @@ class CLI { public: int run(int argc, char **argv); + DynamicPrintConfig& full_print_config() { return m_print_config; } + private: DynamicPrintAndCLIConfig m_config; DynamicPrintConfig m_print_config; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index bc6128abc..43c76db0a 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3272,11 +3272,11 @@ void Plater::export_amf() if (! dialog) { return; } const wxString path = dialog->GetPath(); - const std::string path_u8 = into_u8(path); + std::string path_u8 = into_u8(path); - DynamicPrintConfig cfg = wxGetApp().preset_bundle->full_config_secure(); + DynamicPrintConfig cfg = wxGetApp().preset_bundle->full_config_secure(); wxBusyCursor wait; - if (Slic3r::store_amf(path_u8.c_str(), &p->model, dialog->get_checkbox_value() ? &cfg : nullptr)) { + if (Slic3r::store_amf(path_u8, &p->model, dialog->get_checkbox_value() ? &cfg : nullptr)) { // Success p->statusbar()->set_status_text(wxString::Format(_(L("AMF file exported to %s")), path)); } else { diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt new file mode 100644 index 000000000..b334da67c --- /dev/null +++ b/src/test/CMakeLists.txt @@ -0,0 +1,58 @@ +project(slic3r_test) +cmake_minimum_required(VERSION 2.6) + +include(PrecompiledHeader) + +set(TESTDIR ${CMAKE_CURRENT_SOURCE_DIR}) +set(TESTFILE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/inputs/) +set(SLIC3R_TEST_SOURCES + test_data.cpp + test_data.hpp + test_harness.cpp + GUI/test_cli.cpp +# libslic3r/test_config.cpp # toredo + libslic3r/test_fill.cpp + libslic3r/test_flow.cpp +) + +if (NOT TARGET Catch) + include (ExternalProject) + if(IS_TRAVIS_BUILD) # on travis, use git for fetching instead of wget + set(FETCH_EXTERNAL_CATCH + GIT_REPOSITORY https://github.com/catchorg/Catch.git + GIT_TAG 03d122a35c3f5c398c43095a87bc82ed44642516) + elseif(WIN32) + set(FETCH_EXTERNAL_CATCH + URL https://github.com/catchorg/Catch2/archive/v2.4.2.zip + URL_HASH MD5=6a2ffb9c69d368ebc1ad13146c5a5e1e) + else() + set(FETCH_EXTERNAL_CATCH + URL https://github.com/catchorg/Catch2/archive/v2.4.2.tar.gz + URL_HASH MD5=26927b878b1f42633f15a9ef1c4bd8e7) + endif() + ExternalProject_Add(Catch-External + PREFIX ${CMAKE_BINARY_DIR}/external/Catch + ${FETCH_EXTERNAL_CATCH} + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/external/Catch/src/Catch-External/single_include/catch2/catch.hpp + ${CMAKE_BINARY_DIR}/external/Catch/include/catch.hpp + ) + add_library(Catch INTERFACE) + add_dependencies(Catch Catch-External) + target_include_directories(Catch INTERFACE ${CMAKE_BINARY_DIR}/external/Catch/include) + target_compile_definitions(Catch INTERFACE $<$:_SILENCE_CXX17_UNCAUGHT_EXCEPTION_DEPRECATION_WARNING>) +endif () + + configure_file("${TESTDIR}/test_options.hpp.in" "${TESTDIR}/test_options.hpp") + add_executable(slic3r_test ${SLIC3R_TEST_SOURCES}) + add_test(NAME TestSlic3r COMMAND slic3r_test) + target_compile_features(slic3r_test PUBLIC cxx_std_14) + # add_library(slic3r_test STATIC ${SLIC3R_TEST_SOURCES}) + # target_link_libraries(slic3r_test PUBLIC libslic3r Catch ${LIBSLIC3R_DEPENDS}) + target_link_libraries(slic3r_test slic3r_lib Catch) + # libnest2d admesh miniz ${Boost_LIBRARIES} clipper nowide ${EXPAT_LIBRARIES} ${GLEW_LIBRARIES} ${PNG_LIBRARIES} glu-libtess polypartition poly2tri qhull semver tbb + +# if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY) + # add_precompiled_header(slic3r_test libslic3r_gui pchheader.hpp FORCEINCLUDE) +# endif () diff --git a/src/test/GUI/test_cli.cpp b/src/test/GUI/test_cli.cpp new file mode 100644 index 000000000..bd112753a --- /dev/null +++ b/src/test/GUI/test_cli.cpp @@ -0,0 +1,286 @@ +#include +#include +#include + +#include "../test_options.hpp" + +#include "../../slic3r.hpp" +#include "../../libslic3r/GCodeReader.hpp" + +using namespace Slic3r; +using namespace std::string_literals; + +bool file_exists(const std::string& name, const std::string& ext) { + std::string filename {""}; + filename.append(name); + filename.append("."); + filename.append(ext); + + std::ifstream f(testfile(filename)); + bool result {f.good()}; + f.close(); + + return result; +} + +char** to_cstr_array(std::vector in, char** argv) { + int i = 0; + for (auto& str : in) { + argv[i] = new char[str.size()]; + strcpy(argv[i], str.c_str()); + i++; + } + return argv; +} + +void clean_array(size_t argc, char** argv) { + //for (size_t i = 0; i < argc; ++i) { + // delete[] argv[i]; + //} +} + +SCENARIO( "CLI Export Arguments", "[!mayfail]") { + char* args_cli[20]; + std::vector in_args; + in_args.reserve(20); + in_args.emplace_back("gui_test"s); + in_args.emplace_back(testfile("test_cli/20mmbox.stl"s)); + + GIVEN( "Default configuration and a simple 3D model" ) { + WHEN ( "[ ACTION ] is export-gcode with long option") { + in_args.emplace(in_args.cend()-1, "--export-gcode"); + CLI().run(in_args.size(), to_cstr_array(in_args, args_cli)); + THEN ("GCode file is created.") { + REQUIRE(file_exists("test_cli/20mmbox"s, "gcode"s)); + } + clean_array(in_args.size(), args_cli); + clean_file("test_cli/20mmbox", "gcode"); + } + WHEN ( "[ ACTION ] is export-gcode with short option") { + in_args.emplace(in_args.cend()-1, "-g"); + CLI().run(in_args.size(), to_cstr_array(in_args, args_cli)); + THEN ("GCode file is created.") { + REQUIRE(file_exists("test_cli/20mmbox"s, "gcode"s)); + } + clean_array(in_args.size(), args_cli); + clean_file("test_cli/20mmbox", "gcode"); + } + // doesn't work anymore + //WHEN ( "[ ACTION ] is export-obj") { + // in_args.emplace(in_args.cend()-1, "--export-obj"); + // CLI().run(in_args.size(), to_cstr_array(in_args, args_cli)); + // THEN ("OBJ file is created.") { + // REQUIRE(file_exists("test_cli/20mmbox"s, "obj"s)); + // } + // clean_array(in_args.size(), args_cli); + // clean_file("test_cli/20mmbox", "obj"); + //} + //doesn't work + //WHEN ( "[ ACTION ] is export-pov") { + // in_args.emplace(in_args.cend()-1, "--export-pov"); + // CLI().run(in_args.size(), to_cstr_array(in_args, args_cli)); + // THEN ("POV file is created.") { + // REQUIRE(file_exists("test_cli/20mmbox", "pov")); + // } + // clean_array(in_args.size(), args_cli); + // clean_file("test_cli/20mmbox", "pov"); + //} + WHEN ( "[ ACTION ] is export-amf") { + in_args.emplace(in_args.cend()-1, "--export-amf"); + CLI().run(in_args.size(), to_cstr_array(in_args, args_cli)); + THEN ("Compressed AMF file is created.") { + REQUIRE(file_exists("test_cli/20mmbox.zip", "amf")); + } + clean_array(in_args.size(), args_cli); + clean_file("test_cli/20mmbox.zip", "amf"); + } + WHEN ( "[ ACTION ] is export-3mf") { + in_args.emplace(in_args.cend()-1, "--export-3mf"); + CLI().run(in_args.size(), to_cstr_array(in_args, args_cli)); + THEN ("3MF file is created.") { + REQUIRE(file_exists("test_cli/20mmbox", "3mf")); + } + clean_array(in_args.size(), args_cli); + clean_file("test_cli/20mmbox", "3mf"); + } + // now export-sla + //WHEN ( "[ ACTION ] is export-svg") { + // in_args.emplace(in_args.cend()-1, "--export-svg"); + // CLI().run(in_args.size(), to_cstr_array(in_args, args_cli)); + // THEN ("SVG files are created.") { + // REQUIRE(file_exists("test_cli/20mmbox_0", "svg")); + // REQUIRE(file_exists("test_cli/20mmbox_1", "svg")); + // REQUIRE(file_exists("test_cli/20mmbox_2", "svg")); + // REQUIRE(file_exists("test_cli/20mmbox_3", "svg")); + // REQUIRE(file_exists("test_cli/20mmbox_4", "svg")); + // } + // clean_file("test_cli/20mmbox_0", "svg", true); + // clean_file("test_cli/20mmbox_1", "svg", true); + // clean_file("test_cli/20mmbox_2", "svg", true); + // clean_file("test_cli/20mmbox_3", "svg", true); + // clean_file("test_cli/20mmbox_4", "svg", true); + // clean_array(in_args.size(), args_cli); + //} + //WHEN ( "[ ACTION ] is export-sla-svg") { + // in_args.emplace(in_args.cend()-1, "--export-sla-svg"); + // CLI().run(in_args.size(), to_cstr_array(in_args, args_cli)); + // THEN ("SVG files are created.") { + // REQUIRE(file_exists("test_cli/20mmbox", "svg")); + // } + // clean_array(in_args.size(), args_cli); + // clean_file("test_cli/20mmbox", "svg", true); + //} + //WHEN("[ ACTION ] is sla") { + // in_args.emplace(in_args.cend() - 1, "--sla"); + // CLI().run(in_args.size(), to_cstr_array(in_args, args_cli)); + // THEN("SVG file is created.") { + // REQUIRE(file_exists("test_cli/20mmbox", "svg")); + // } + // clean_array(in_args.size(), args_cli); + // clean_file("test_cli/20mmbox", "svg", true); + //} + //WHEN("[ ACTION ] is sla for sl1") { + // in_args.emplace(in_args.cend() - 1, "--sla"); + // CLI().run(in_args.size(), to_cstr_array(in_args, args_cli)); + // THEN("SVG file is created.") { + // REQUIRE(file_exists("test_cli/20mmbox", "sl1")); + // } + // clean_array(in_args.size(), args_cli); + // clean_file("test_cli/20mmbox", "sl1", true); + //} + //WHEN ( "[ ACTION ] is sla and --output is output.svg") { + // in_args.emplace(in_args.cend()-1, "--sla"); + // in_args.emplace(in_args.cend()-1, "--output"); + // in_args.emplace(in_args.cend()-1, testfile("output.svg")); + // CLI().run(in_args.size(), to_cstr_array(in_args, args_cli)); + // THEN ("SVG files are created.") { + // REQUIRE(file_exists("output", "svg")); + // } + // clean_array(in_args.size(), args_cli); + // clean_file("output", "svg", true); + //} + WHEN ( "[ ACTION ] is save") { + in_args.emplace(in_args.cend()-1, "--save"); + in_args.emplace(in_args.cend()-1, testfile("cfg.ini")); + CLI().run(in_args.size(), to_cstr_array(in_args, args_cli)); + THEN ("Configuration file is created.") { + REQUIRE(file_exists("cfg", "ini")); + } + clean_array(in_args.size(), args_cli); + clean_file("cfg", "ini"); + } + WHEN ( "[ ACTION ] is export-stl and --output option is specified") { + in_args.emplace(in_args.cend()-1, "--export-stl"); + in_args.emplace(in_args.cend()-1, "--output"); + in_args.emplace(in_args.cend()-1, testfile("output.stl")); + CLI().run(in_args.size(), to_cstr_array(in_args, args_cli)); + THEN ("STL file is created.") { + REQUIRE(file_exists("output", "stl")); + } + clean_array(in_args.size(), args_cli); + clean_file("output", "stl"); + } + + } +} + +// ?? +//SCENARIO("CLI Transform arguments", "[!shouldfail]") { +// char* args_cli[20]; +// std::vector in_args; +// in_args.reserve(20); +// in_args.emplace_back("gui_test"s); +// in_args.emplace_back(testfile("test_cli/20mmbox.stl"s)); +// WHEN("Tests are implemented for CLI model transform") { +// THEN ("Tests should not fail :D") { +// REQUIRE(false); +// } +// } +//} + +// Test the --center and --dont-arrange parameters. +SCENARIO("CLI positioning arguments") { + char* args_cli[20]; + std::vector in_args; + in_args.reserve(20); + in_args.emplace_back("gui_test"s); + GIVEN( " 3D Model for a 20mm box, centered around 0,0 and gcode export" ) { + in_args.emplace_back(testfile("test_cli/20mmbox.stl"s)); + in_args.emplace(in_args.cend()-1, "-g"); + in_args.emplace(in_args.cend()-1, "--load"s); + in_args.emplace(in_args.cend()-1, testfile("test_cli/20mmbox_config.ini")); + CLI cut; + + WHEN("--center is supplied with 40,40") { + in_args.emplace(in_args.cend()-1, "--center"); + in_args.emplace(in_args.cend()-1, "40,40"); + cut.run(in_args.size(), to_cstr_array(in_args, args_cli)); + + THEN ("The first layer of the print should be centered around 0,0") { + std::string exported { read_to_string("test_cli/20mmbox.gcode"s)}; + GCodeReader reader; + REQUIRE(exported != ""s); + double min_x = 50.0, max_x = -50.0, min_y = 50.0, max_y = -50.0; + reader.apply_config(cut.full_print_config()); + reader.parse_buffer(exported, [&min_x, &min_y, &max_x, &max_y] (GCodeReader& self, const GCodeReader::GCodeLine& line) + { + if (self.x() != 0.0 && self.y() != 0.0) { // avoid the first pass + if (self.z() <= 0.6 && self.z() > 0.3) { + min_x = std::min(min_x, static_cast(self.x())); + min_y = std::min(min_y, static_cast(self.y())); + max_x = std::max(max_x, static_cast(self.x())); + max_y = std::max(max_y, static_cast(self.y())); + } + } + }); + AND_THEN("Minimum X encountered is about 30.1") { + REQUIRE(min_x == Approx(30.1)); + } + AND_THEN("Minimum Y encountered is about 30.1") { + REQUIRE(min_y == Approx(30.1)); + } + AND_THEN("Maximum X encountered is about 49.9") { + REQUIRE(max_x == Approx(49.9)); + } + AND_THEN("Maximum Y encountered is about 49.9") { + REQUIRE(max_y == Approx(49.9)); + } + } + } + WHEN("--dont-arrange is supplied") { + in_args.emplace(in_args.cend()-1, "--dont-arrange"); + cut.run(in_args.size(), to_cstr_array(in_args, args_cli)); + + THEN ("The first layer of the print should be centered around 0,0") { + auto reader {GCodeReader()}; + std::string exported { read_to_string("test_cli/20mmbox.gcode"s)}; + REQUIRE(exported != ""s); + double min_x = 50.0, max_x = -50.0, min_y = 50.0, max_y = -50.0; + reader.apply_config(cut.full_print_config()); + reader.parse_buffer(exported, [&min_x, &min_y, &max_x, &max_y] (GCodeReader& self, const GCodeReader::GCodeLine& line) + { + if (self.z() < 0.6) { + min_x = std::min(min_x, static_cast(self.x())); + min_y = std::min(min_y, static_cast(self.y())); + max_x = std::max(max_x, static_cast(self.x())); + max_y = std::max(max_y, static_cast(self.y())); + } + }); + AND_THEN("Minimum X encountered is about -9.9") { + REQUIRE(min_x == Approx(-9.9)); + } + AND_THEN("Minimum Y encountered is about -9.9") { + REQUIRE(min_y == Approx(-9.9)); + } + AND_THEN("Maximum X encountered is about 9.9") { + REQUIRE(max_x == Approx(9.9)); + } + AND_THEN("Maximum Y encountered is about 9.9") { + REQUIRE(max_y == Approx(9.9)); + } + } + } + clean_array(in_args.size(), args_cli); + clean_file("test_cli/20mmbox", "gcode"); + } +} diff --git a/src/test/GUI/test_field_checkbox.cpp b/src/test/GUI/test_field_checkbox.cpp new file mode 100644 index 000000000..5a71c7a0d --- /dev/null +++ b/src/test/GUI/test_field_checkbox.cpp @@ -0,0 +1,235 @@ +#include + +#ifndef WX_PRECOMP + #include "wx/app.h" + #include "wx/sizer.h" + #include "wx/checkbox.h" + #include "wx/uiaction.h" +#endif // WX_PRECOMP +#include +#include "testableframe.h" + +#include "OptionsGroup/Field.hpp" +#include "ConfigBase.hpp" + +using namespace Slic3r::GUI; + +SCENARIO( "GUI Checkbox option items fire their on_kill_focus when focus leaves the checkbox." ) { + wxTestableFrame* old = dynamic_cast(wxTheApp->GetTopWindow()); + old->Destroy(); + wxTheApp->SetTopWindow(new wxTestableFrame()); + wxUIActionSimulator sim; + wxMilliSleep(500); + GIVEN( "A checkbox field item exists on a window") { + auto exec_counter {0}; + auto test_field {Slic3r::GUI::UI_Checkbox(wxTheApp->GetTopWindow(), Slic3r::ConfigOptionDef())}; + + auto killfunc {[&exec_counter](const std::string& opt_id) { exec_counter += 1; }}; + + test_field.on_kill_focus = killfunc; + wxTheApp->GetTopWindow()->Show(); + wxTheApp->GetTopWindow()->Fit(); + + WHEN ( "focus leaves the checkbox") { + exec_counter = 0; + + test_field.check()->SetFocus(); + wxMilliSleep(500); + + auto ev {wxFocusEvent(wxEVT_KILL_FOCUS, test_field.check()->GetId())}; + ev.SetEventObject(test_field.check()); + test_field.check()->ProcessWindowEvent(ev); + wxYield(); + wxMilliSleep(500); + THEN( "on_focus_kill is executed.") { + REQUIRE(exec_counter == 1); + } + } + + WHEN ( "focus leaves the checkbox and no callback is assigned") { + test_field.on_kill_focus = nullptr; + exec_counter = 0; + + test_field.check()->SetFocus(); + wxMilliSleep(500); + + auto ev {wxFocusEvent(wxEVT_KILL_FOCUS, test_field.check()->GetId())}; + ev.SetEventObject(test_field.check()); + test_field.check()->ProcessWindowEvent(ev); + wxYield(); + wxMilliSleep(500); + THEN( "on_focus_kill doesn't try to execute nullptr") { + REQUIRE(1 == 1); + REQUIRE(exec_counter == 0); + } + } + } +} +SCENARIO( "GUI Checkbox set_value and get_bool work as expected." ) { + wxTestableFrame* old = dynamic_cast(wxTheApp->GetTopWindow()); + old->Destroy(); + wxTheApp->SetTopWindow(new wxTestableFrame()); + wxMilliSleep(500); + + auto test_field {Slic3r::GUI::UI_Checkbox(wxTheApp->GetTopWindow(), Slic3r::ConfigOptionDef())}; + + GIVEN( "A checkbox field item exists on a window") { + WHEN ( "set_value is an bool and true") { + test_field.set_value(true); + THEN( " Result is converted correctly.") { + REQUIRE( test_field.get_bool() == true); + } + } + WHEN ( "set_value is an bool and false") { + test_field.set_value(false); + THEN( " Result is converted correctly.") { + REQUIRE( test_field.get_bool() == false); + } + } + WHEN ( "set_value is a floating point number > 0") { + test_field.set_value(true); + try { + test_field.set_value(10.2); + } catch (boost::bad_any_cast &e) { + THEN( " Nothing happens; exception was thrown (and caught).") { + REQUIRE(true); + } + } + THEN( " Value did not change.") { + REQUIRE( test_field.get_bool() == true); + } + } + } +} + +SCENARIO( "GUI Checkbox option respond to EVT_CHECKBOX when appropriate." ) { + wxUIActionSimulator sim; + wxTestableFrame* old = dynamic_cast(wxTheApp->GetTopWindow()); + old->Destroy(); + wxTheApp->SetTopWindow(new wxTestableFrame()); + auto* boolopt { new Slic3r::ConfigOptionBool(true) }; + + wxMilliSleep(500); + GIVEN( "A checkbox field item and disable_change = false") { + auto exec_counter {0}; + + auto changefunc {[&exec_counter](const std::string& opt_id, bool value) { exec_counter += 1; }}; + auto test_field {Slic3r::GUI::UI_Checkbox(wxTheApp->GetTopWindow(), Slic3r::ConfigOptionDef())}; + + test_field.disable_change_event = false; + test_field.on_change = changefunc; + + wxTheApp->GetTopWindow()->Show(); + wxTheApp->GetTopWindow()->Fit(); + + WHEN ( "CHECKBOX event is received") { + exec_counter = 0; + auto ev {wxCommandEvent(wxEVT_CHECKBOX, test_field.check()->GetId())}; + ev.SetEventObject(test_field.check()); + test_field.check()->ProcessWindowEvent(ev); + wxYield(); + wxMilliSleep(250); + THEN ( "on_change is executed.") { + REQUIRE(exec_counter == 1); + } + } + } + GIVEN( "A checkbox field item and disable_change = true") { + auto exec_counter {0}; + auto changefunc {[&exec_counter] (const std::string& opt_id, bool value) { exec_counter++; }}; + auto test_field {Slic3r::GUI::UI_Checkbox(wxTheApp->GetTopWindow(), Slic3r::ConfigOptionDef())}; + wxTheApp->GetTopWindow()->Show(); + wxTheApp->GetTopWindow()->Fit(); + test_field.disable_change_event = true; + test_field.on_change = changefunc; + + WHEN ( "CHECKBOX event is received") { + exec_counter = 0; + auto ev {wxCommandEvent(wxEVT_CHECKBOX, test_field.check()->GetId())}; + ev.SetEventObject(test_field.check()); + test_field.check()->ProcessWindowEvent(ev); + wxYield(); + wxMilliSleep(250); + THEN ( "on_change is not executed.") { + REQUIRE(exec_counter == 0); + } + } + } + GIVEN( "A checkbox field item and readonly") { + + struct Slic3r::ConfigOptionDef simple_option; + simple_option.default_value = boolopt; + simple_option.readonly = true; + + auto exec_counter {0}; + auto changefunc {[&exec_counter] (const std::string& opt_id, bool value) { exec_counter++; }}; + auto* test_field {new Slic3r::GUI::UI_Checkbox(wxTheApp->GetTopWindow(), simple_option)}; + wxTheApp->GetTopWindow()->Show(); + wxTheApp->GetTopWindow()->Fit(); + test_field->disable_change_event = false; // don't disable :D + test_field->on_change = changefunc; + + WHEN ( "CHECKBOX event is received") { + exec_counter = 0; + test_field->set_value(false); + auto ev {wxCommandEvent(wxEVT_CHECKBOX, test_field->check()->GetId())}; + ev.SetEventObject(test_field->check()); + test_field->check()->ProcessWindowEvent(ev); + wxYield(); + wxMilliSleep(250); + THEN ( "on_change is not executed.") { + REQUIRE(exec_counter == 0); + } + } + WHEN ( "the box is clicked and enabled") { + exec_counter = 0; + test_field->enable(); + test_field->set_value(true); + + auto ev {wxCommandEvent(wxEVT_CHECKBOX, test_field->check()->GetId())}; + ev.SetEventObject(test_field->check()); + test_field->check()->ProcessWindowEvent(ev); + wxYield(); + wxMilliSleep(250); + THEN ( "on_change is executed.") { + REQUIRE(exec_counter == 1); + } + THEN ( "Box is checked.") { + REQUIRE(test_field->get_bool() == true); + } + } + WHEN ( "the box is clicked and toggled true") { + exec_counter = 0; + test_field->set_value(true); + test_field->toggle(true); + auto ev {wxCommandEvent(wxEVT_CHECKBOX, test_field->check()->GetId())}; + ev.SetEventObject(test_field->check()); + test_field->check()->ProcessWindowEvent(ev); + wxYield(); + wxMilliSleep(250); + THEN ( "on_change is executed.") { + REQUIRE(exec_counter == 1); + } + THEN ( "Box is checked.") { + REQUIRE(test_field->get_bool() == true); + } + } + WHEN ( "the box is clicked and toggled false") { + exec_counter = 0; + test_field->set_value(true); + test_field->toggle(false); + auto ev {wxCommandEvent(wxEVT_CHECKBOX, test_field->check()->GetId())}; + ev.SetEventObject(test_field->check()); + test_field->check()->ProcessWindowEvent(ev); + wxYield(); + wxMilliSleep(250); + THEN ( "on_change is not executed.") { + REQUIRE(exec_counter == 0); + } + THEN ( "Box is checked.") { + REQUIRE(test_field->get_bool() == true); + } + } + delete test_field; + } +} diff --git a/src/test/GUI/test_field_choice.cpp b/src/test/GUI/test_field_choice.cpp new file mode 100644 index 000000000..d2c17def1 --- /dev/null +++ b/src/test/GUI/test_field_choice.cpp @@ -0,0 +1,143 @@ +#include + +#ifndef WX_PRECOMP +#include "wx/app.h" +#include "wx/uiaction.h" +#endif // WX_PRECOMP + +#include +#include "testableframe.h" +#include "OptionsGroup/Field.hpp" +#include "ConfigBase.hpp" + +using namespace std::string_literals; + +SCENARIO( "UI_Choice: default values from options") { + wxTestableFrame* old = dynamic_cast(wxTheApp->GetTopWindow()); + old->Destroy(); + wxTheApp->SetTopWindow(new wxTestableFrame()); + wxUIActionSimulator sim; + wxMilliSleep(500); + + GIVEN( "I have a UI Choice with 3 options from ConfigOptionDef and a default_value that is not in the enumeration.") { + auto simple_option {ConfigOptionDef()}; + auto* default_string {new ConfigOptionString("A")}; + + simple_option.default_value = default_string; // owned by ConfigOptionDef + simple_option.enum_values.push_back("B"); + simple_option.enum_values.push_back("C"); + simple_option.enum_values.push_back("D"); + + auto test_field {Slic3r::GUI::UI_Choice(wxTheApp->GetTopWindow(), simple_option)}; + WHEN( "I don't explicitly select any option in the drop-down") { + THEN( "get_value() returns the the value associuted with the listed default option in the related ConfigOptionDef") { + REQUIRE(simple_option.default_value->getString() == test_field.get_string()); + } + } + WHEN( "I select the first option in the drop-down") { + test_field.choice()->SetSelection(0); + THEN( "get_value() returns the the value associuted with the first option in the related ConfigOptionDef") { + REQUIRE(simple_option.enum_values[0] == test_field.get_string()); + } + } + WHEN( "I select the second option in the drop-down") { + test_field.choice()->SetSelection(1); + THEN( "get_value() returns the the value associuted with the second option in the related ConfigOptionDef") { + REQUIRE(simple_option.enum_values[1] == test_field.get_string()); + } + } + WHEN( "I select the third option in the drop-down") { + test_field.choice()->SetSelection(2); + THEN( "get_value() returns the the value associuted with the third option in the related ConfigOptionDef") { + REQUIRE(simple_option.enum_values[2] == test_field.get_string()); + } + } + } + GIVEN( "I have a UI Choice with 3 options from ConfigOptionDef and a default_value that is in the enumeration.") { + auto simple_option {ConfigOptionDef()}; + auto* default_string {new ConfigOptionString("B"s)}; + + simple_option.default_value = default_string; // owned by ConfigOptionDef + simple_option.enum_values.push_back("B"s); + simple_option.enum_values.push_back("C"s); + simple_option.enum_values.push_back("D"s); + + auto test_field {Slic3r::GUI::UI_Choice(wxTheApp->GetTopWindow(), simple_option)}; + WHEN( "I don't explicitly select any option in the drop-down") { + THEN( "get_value() returns the first value in the related ConfigOptionDef") { + REQUIRE(simple_option.enum_values[0] == test_field.get_string()); + REQUIRE(test_field.choice()->FindString(simple_option.enum_values[0]) == 0); + } + } + WHEN( "I set the string value to another item in the enumeration") { + test_field.combo()->SetValue("C"s); + THEN( "get_string() returns the matching item in ConfigOptionDef") { + REQUIRE(test_field.get_string() == "C"s); + REQUIRE(test_field.choice()->FindString(simple_option.enum_values[1]) == 1); + } + } + WHEN( "I set the string value to another item that is not in the enumeration") { + test_field.combo()->SetValue("F"s); + THEN( "get_string() returns the matching item in ConfigOptionDef") { + REQUIRE(test_field.get_string() == "F"s); + REQUIRE(test_field.choice()->GetSelection() == wxNOT_FOUND); + } + } + } +} + + +SCENARIO( "UI_Choice: event handling for on_change and on_kill_focus") { + auto event_count {0}; + auto killfunc {[&event_count](const std::string& opt_id) { event_count += 1; }}; + auto changefunc {[&event_count](const std::string& opt_id, std::string value) { event_count += 1; }}; + GIVEN( "I have a UI Choice with 2 options from ConfigOptionDef, no default value, and an on_change handler and on_kill_focus handler.") { + auto simple_option {ConfigOptionDef()}; + auto* default_string {new ConfigOptionString("B")}; + + simple_option.default_value = default_string; // owned by ConfigOptionDef + simple_option.enum_values.push_back("B"); + simple_option.enum_values.push_back("C"); + + auto test_field {Slic3r::GUI::UI_Choice(wxTheApp->GetTopWindow(), simple_option)}; + + test_field.on_kill_focus = killfunc; + test_field.on_change = changefunc; + + WHEN( "I receive a wxEVT_COMBOBOX event") { + event_count = 0; + wxMilliSleep(250); + + auto ev {wxCommandEvent(wxEVT_COMBOBOX, test_field.choice()->GetId())}; + ev.SetEventObject(test_field.choice()); + test_field.choice()->ProcessWindowEvent(ev); + THEN( "on_change handler is executed.") { + REQUIRE(event_count == 1); + } + } + WHEN( "I receive a wxEVT_TEXT_ENTER event") { + event_count = 0; + wxMilliSleep(250); + + auto ev {wxCommandEvent(wxEVT_TEXT_ENTER, test_field.choice()->GetId())}; + ev.SetEventObject(test_field.choice()); + test_field.choice()->ProcessWindowEvent(ev); + THEN( "on_change handler is executed.") { + REQUIRE(event_count == 1); + } + } + WHEN( "My control loses focus.") { + event_count = 0; + test_field.choice()->SetFocus(); + wxMilliSleep(250); + + auto ev {wxFocusEvent(wxEVT_KILL_FOCUS, test_field.choice()->GetId())}; + ev.SetEventObject(test_field.choice()); + test_field.choice()->ProcessWindowEvent(ev); + + THEN( "on_change handler is executed and on_kill_focus handler is executed.") { + REQUIRE(event_count == 2); + } + } + } +} diff --git a/src/test/GUI/test_field_colorpicker.cpp b/src/test/GUI/test_field_colorpicker.cpp new file mode 100644 index 000000000..a0616f452 --- /dev/null +++ b/src/test/GUI/test_field_colorpicker.cpp @@ -0,0 +1,113 @@ +#include + +#ifndef WX_PRECOMP +#include +#include +#include +#include +#include +#endif // WX_PRECOMP + +#include +#include "testableframe.h" +#include "OptionsGroup/Field.hpp" +#include "ConfigBase.hpp" +using namespace std::string_literals; + +SCENARIO("UI_Color: default values from options and basic accessor methods") { + wxTestableFrame* old = dynamic_cast(wxTheApp->GetTopWindow()); + old->Destroy(); + wxTheApp->SetTopWindow(new wxTestableFrame()); + wxUIActionSimulator sim; + wxMilliSleep(500); + auto simple_option {ConfigOptionDef()}; + auto* default_color {new ConfigOptionString("#FFFF00")}; + auto event_count {0}; + auto changefunc {[&event_count] (const std::string& opt_id, const std::string& color) { event_count++; }}; + GIVEN("A Color Picker") { + simple_option.default_value = default_color; + auto test_field {Slic3r::GUI::UI_Color(wxTheApp->GetTopWindow(), simple_option)}; + + test_field.on_change = changefunc; + WHEN("Object is constructed with default_value of '#FFFF00'.") { + THEN("get_string() returns '#FFFF00'") { + REQUIRE(test_field.get_string() == "#FFFF00"s); + } + THEN("get_int() returns 0") { + REQUIRE(test_field.get_int() == 0); + } + } + WHEN("Color picker receives a color picked event") { + event_count = 0; + test_field.disable_change_event = false; + auto ev {wxFocusEvent(wxEVT_COLOURPICKER_CHANGED, test_field.picker()->GetId())}; + ev.SetEventObject(test_field.picker()); + test_field.picker()->ProcessWindowEvent(ev); + THEN("_on_change fires.") { + REQUIRE(event_count == 1); + } + } + } +} +SCENARIO( "Color string value tests") { + wxTestableFrame* old = dynamic_cast(wxTheApp->GetTopWindow()); + old->Destroy(); + wxTheApp->SetTopWindow(new wxTestableFrame()); + wxUIActionSimulator sim; + wxMilliSleep(500); + auto simple_option {ConfigOptionDef()}; + auto* default_color {new ConfigOptionString("#FFFFFF")}; + GIVEN("A Color Picker") { + auto test_field {Slic3r::GUI::UI_Color(wxTheApp->GetTopWindow(), simple_option)}; + WHEN("Set_value is called with a string of '#FFFFFF'") { + test_field.set_value("#FFFFFF"); + THEN("Internal wxColor is equal to wxWhite") { + REQUIRE(test_field.picker()->GetColour() == wxColour(*wxWHITE)); + } + } + WHEN("Set_value is called with a string of '#FFAACC'") { + test_field.set_value("#FFAACC"); + THEN("Internal wxColor is equal to wxColor(255, 170, 204)") { + REQUIRE(test_field.picker()->GetColour() == wxColour(255, 170, 204)); + } + } + WHEN("Set_value is called with a string of '#3020FF'") { + test_field.set_value("#3020FF"); + THEN("Internal wxColor is equal to wxColor(48, 32, 255)") { + REQUIRE(test_field.picker()->GetColour() == wxColour(48,32,255)); + } + } + WHEN("Set_value is called with a string of '#01A06D'") { + test_field.set_value("#01A06D"); + THEN("Internal wxColor is equal to wxColor(01, 160, 109)") { + REQUIRE(test_field.picker()->GetColour() == wxColour(1,160,109)); + } + } + WHEN("Internal color is set to wxWHITE") { + test_field.picker()->SetColour(wxColour(*wxWHITE)); + THEN("String value is #FFFFFF") { + REQUIRE(test_field.get_string() == "#FFFFFF"s); + } + } + WHEN("Internal color is set to wxRED") { + test_field.picker()->SetColour(wxColour(*wxRED)); + THEN("String value is #FF0000") { + REQUIRE(test_field.get_string() == "#FF0000"s); + } + } + WHEN("Internal color is set to wxGREEN") { + test_field.picker()->SetColour(wxColour(*wxGREEN)); + THEN("String value is #00FF00") { + REQUIRE(test_field.get_string() == "#00FF00"s); + } + } + WHEN("Internal color is set to wxBLUE") { + test_field.picker()->SetColour(wxColour(*wxBLUE)); + THEN("String value is #0000FF") { + REQUIRE(test_field.get_string() == "#0000FF"s); + } + } + } + delete default_color; +} + diff --git a/src/test/GUI/test_field_numchoice.cpp b/src/test/GUI/test_field_numchoice.cpp new file mode 100644 index 000000000..a09923104 --- /dev/null +++ b/src/test/GUI/test_field_numchoice.cpp @@ -0,0 +1,312 @@ +#include + +#ifndef WX_PRECOMP +#include "wx/app.h" +#include "wx/uiaction.h" +#endif // WX_PRECOMP + +#include +#include "testableframe.h" +#include "OptionsGroup/Field.hpp" +#include "ConfigBase.hpp" + +using namespace std::string_literals; + +SCENARIO( "UI_NumChoice: default values from options") { + wxTestableFrame* old = dynamic_cast(wxTheApp->GetTopWindow()); + old->Destroy(); + wxTheApp->SetTopWindow(new wxTestableFrame()); + wxUIActionSimulator sim; + wxMilliSleep(500); + + GIVEN( "I have a UI NumChoice with 3 options from ConfigOptionDef that has only values and a default_value that is not in the enumeration.") { + auto simple_option {ConfigOptionDef()}; + auto* default_string {new ConfigOptionString("1")}; + + simple_option.default_value = default_string; // owned by ConfigOptionDef + simple_option.enum_values.push_back("2"); + simple_option.enum_values.push_back("3"); + simple_option.enum_values.push_back("4"); + + auto test_field {Slic3r::GUI::UI_NumChoice(wxTheApp->GetTopWindow(), simple_option)}; + + wxTheApp->GetTopWindow()->Show(); + wxTheApp->GetTopWindow()->Fit(); + + WHEN( "I don't explicitly select any option in the drop-down") { + THEN( "get_value() returns the the value associated with the listed default option in the related ConfigOptionDef") { + REQUIRE(test_field.get_string() == simple_option.default_value->getString()); + } + } + WHEN( "I select the first option in the drop-down") { + test_field.choice()->SetSelection(0); + THEN( "get_value() returns the the value associated with the first option in the related ConfigOptionDef") { + REQUIRE(test_field.get_string() == simple_option.enum_values[0]); + } + } + WHEN( "I select the second option in the drop-down") { + test_field.choice()->SetSelection(1); + THEN( "get_value() returns the the value associated with the second option in the related ConfigOptionDef") { + REQUIRE(test_field.get_string() == simple_option.enum_values[1]); + } + } + WHEN( "I select the third option in the drop-down") { + test_field.choice()->SetSelection(2); + THEN( "get_value() returns the the value associated with the third option in the related ConfigOptionDef") { + REQUIRE(test_field.get_string() == simple_option.enum_values[2]); + } + } + } + GIVEN( "I have a UI NumChoice with 3 options from ConfigOptionDef that has only values and a default_value that is in the enumeration.") { + auto simple_option {ConfigOptionDef()}; + auto* default_string {new ConfigOptionString("2"s)}; + + simple_option.default_value = default_string; // owned by ConfigOptionDef + simple_option.enum_values.push_back("2"); + simple_option.enum_values.push_back("3"); + simple_option.enum_values.push_back("4"); + + auto test_field {Slic3r::GUI::UI_NumChoice(wxTheApp->GetTopWindow(), simple_option)}; + wxTheApp->GetTopWindow()->Show(); + wxTheApp->GetTopWindow()->Fit(); + + WHEN( "I don't explicitly select any option in the drop-down") { + THEN( "get_value() returns the first value in the related ConfigOptionDef") { + REQUIRE(simple_option.enum_values[0] == test_field.get_string()); + REQUIRE(test_field.choice()->FindString(simple_option.enum_values[0]) == 0); + } + } + WHEN( "I set the string value to another item in the enumeration") { + test_field.choice()->SetValue("3"s); + THEN( "get_string() returns the matching item in ConfigOptionDef") { + REQUIRE(test_field.get_string() == "3"s); + } + THEN( "get_int() returns the matching item in ConfigOptionDef as an integer") { + REQUIRE(test_field.get_int() == 3); + } + THEN( "get_double() returns the matching item in ConfigOptionDef as a double") { + REQUIRE(test_field.get_double() == 3.0); + } + THEN( "Combobox string shows up as the first enumeration value.") { + REQUIRE(test_field.choice()->GetValue() == simple_option.enum_values[1]); + } + } + WHEN( "I set the string value to another item that is not in the enumeration") { + test_field.choice()->SetValue("7"s); + THEN( "get_string() returns the matching item in ConfigOptionDef") { + REQUIRE(test_field.get_string() == "7"s); + REQUIRE(test_field.get_int() == 7); + REQUIRE(test_field.get_double() == 7.0); + REQUIRE(test_field.choice()->GetSelection() == wxNOT_FOUND); + } + } + } + + GIVEN( "I have a UI NumChoice with 3 options from ConfigOptionDef that has values that are doubles and labels and a default_value that is not in the enumeration.") { + auto simple_option {ConfigOptionDef()}; + auto* default_int {new ConfigOptionFloat(1.0)}; + + simple_option.default_value = default_int; // owned by ConfigOptionDef + simple_option.enum_values.push_back("2.2"); + simple_option.enum_values.push_back("3.3"); + simple_option.enum_values.push_back("4.4"); + + simple_option.enum_labels.push_back("B"); + simple_option.enum_labels.push_back("C"); + simple_option.enum_labels.push_back("D"); + + auto test_field {Slic3r::GUI::UI_NumChoice(wxTheApp->GetTopWindow(), simple_option)}; + wxTheApp->GetTopWindow()->Show(); + wxTheApp->GetTopWindow()->Fit(); + + WHEN( "I don't explicitly select any option in the drop-down") { + THEN( "get_value() returns the the value associated with the listed default option in the related ConfigOptionDef") { + REQUIRE(simple_option.default_value->getString() == test_field.get_string()); + } + } + WHEN( "I select the first option in the drop-down") { + test_field.choice()->SetSelection(0); + THEN( "get_value() returns the the value associated with the first option in the related ConfigOptionDef") { + REQUIRE(simple_option.enum_values[0] == test_field.get_string()); + } + } + WHEN( "I select the second option in the drop-down") { + test_field.choice()->SetSelection(1); + THEN( "get_value() returns the the value associated with the second option in the related ConfigOptionDef") { + REQUIRE(simple_option.enum_values[1] == test_field.get_string()); + } + } + WHEN( "I select the third option in the drop-down") { + test_field.choice()->SetSelection(2); + THEN( "get_value() returns the the value associated with the third option in the related ConfigOptionDef") { + REQUIRE(simple_option.enum_values[2] == test_field.get_string()); + } + } + } + + GIVEN( "I have a UI NumChoice with 3 options from ConfigOptionDef that has values and labels and a default_value that is not in the enumeration.") { + auto simple_option {ConfigOptionDef()}; + auto* default_int {new ConfigOptionInt(1)}; + + simple_option.default_value = default_int; // owned by ConfigOptionDef + simple_option.enum_values.push_back("2"); + simple_option.enum_values.push_back("3"); + simple_option.enum_values.push_back("4"); + + simple_option.enum_labels.push_back("B"); + simple_option.enum_labels.push_back("C"); + simple_option.enum_labels.push_back("D"); + + auto test_field {Slic3r::GUI::UI_NumChoice(wxTheApp->GetTopWindow(), simple_option)}; + wxTheApp->GetTopWindow()->Show(); + wxTheApp->GetTopWindow()->Fit(); + + WHEN( "I don't explicitly select any option in the drop-down") { + THEN( "get_value() returns the the value associated with the listed default option in the related ConfigOptionDef") { + REQUIRE(simple_option.default_value->getString() == test_field.get_string()); + } + } + WHEN( "I select the first option in the drop-down") { + test_field.choice()->SetSelection(0); + THEN( "get_value() returns the the value associated with the first option in the related ConfigOptionDef") { + REQUIRE(simple_option.enum_values[0] == test_field.get_string()); + } + } + WHEN( "I select the second option in the drop-down") { + test_field.choice()->SetSelection(1); + THEN( "get_value() returns the the value associated with the second option in the related ConfigOptionDef") { + REQUIRE(simple_option.enum_values[1] == test_field.get_string()); + } + } + WHEN( "I select the third option in the drop-down") { + test_field.choice()->SetSelection(2); + THEN( "get_value() returns the the value associated with the third option in the related ConfigOptionDef") { + REQUIRE(simple_option.enum_values[2] == test_field.get_string()); + } + } + } + GIVEN( "I have a UI NumChoice with 3 options from ConfigOptionDef that has values and labels and a default_value that is in the enumeration.") { + auto simple_option {ConfigOptionDef()}; + auto* default_string {new ConfigOptionString("2"s)}; + + simple_option.default_value = default_string; // owned by ConfigOptionDef + simple_option.enum_values.push_back("2"); + simple_option.enum_values.push_back("3"); + simple_option.enum_values.push_back("4"); + + simple_option.enum_labels.push_back("B"); + simple_option.enum_labels.push_back("C"); + simple_option.enum_labels.push_back("D"); + + auto test_field {Slic3r::GUI::UI_NumChoice(wxTheApp->GetTopWindow(), simple_option)}; + + wxTheApp->GetTopWindow()->Show(); + wxTheApp->GetTopWindow()->Fit(); + + WHEN( "I don't explicitly select any option in the drop-down") { + THEN( "get_value functions return the first value in the related ConfigOptionDef") { + REQUIRE(test_field.get_string() == simple_option.enum_values[0]); + } + THEN( "get_int() returns the matching item in ConfigOptionDef as an integer") { + REQUIRE(test_field.get_int() == 2); + } + THEN( "get_double() returns the matching item in ConfigOptionDef as a double") { + REQUIRE(test_field.get_double() == 2.0); + } + THEN( "choice.FindString returns the label matching the item") { + REQUIRE(test_field.choice()->FindString(simple_option.enum_labels[0]) == 0); + } + } + WHEN( "I set the string value to another item in the enumeration") { + test_field.set_value(3); + THEN( "get_string() returns the matching item in ConfigOptionDef") { + REQUIRE(test_field.get_string() == "3"s); + } + THEN( "get_int() returns the matching item in ConfigOptionDef as an integer") { + REQUIRE(test_field.get_int() == 3); + } + THEN( "get_double() returns the matching item in ConfigOptionDef as a double") { + REQUIRE(test_field.get_double() == 3.0); + } + THEN( "choice.GetValue() returns the label.") { + REQUIRE(test_field.choice()->GetValue() == "C"s); + REQUIRE(test_field.choice()->FindString(simple_option.enum_labels[1]) == 1); + } + } + WHEN( "I set the string value to another item that is not in the enumeration") { + test_field.set_value(7); + THEN( "get_string() returns the entered value.") { + REQUIRE(test_field.get_string() == "7"s); + } + THEN( "get_int() returns the entered value as an integer.") { + REQUIRE(test_field.get_int() == 7); + } + THEN( "get_double() returns the entered value as a double.") { + REQUIRE(test_field.get_double() == 7.0); + } + THEN( "Underlying selection is wxNOT_FOUND") { + REQUIRE(test_field.choice()->GetSelection() == wxNOT_FOUND); + } + } + } +} + + + +SCENARIO( "UI_NumChoice: event handling for on_change and on_kill_focus") { + auto event_count {0}; + auto killfunc {[&event_count](const std::string& opt_id) { event_count += 1; }}; + auto changefunc {[&event_count](const std::string& opt_id, std::string value) { event_count += 1; }}; + GIVEN( "I have a UI NumChoice with 2 options from ConfigOptionDef, no default value, and an on_change handler and on_kill_focus handler.") { + auto simple_option {ConfigOptionDef()}; + auto* default_string {new ConfigOptionString("2")}; + + simple_option.default_value = default_string; // owned by ConfigOptionDef + simple_option.enum_values.push_back("2"); + simple_option.enum_values.push_back("3"); + + auto test_field {Slic3r::GUI::UI_NumChoice(wxTheApp->GetTopWindow(), simple_option)}; + + wxTheApp->GetTopWindow()->Show(); + wxTheApp->GetTopWindow()->Fit(); + + test_field.on_kill_focus = killfunc; + test_field.on_change = changefunc; + + WHEN( "I receive a wxEVT_COMBOBOX event") { + event_count = 0; + wxMilliSleep(250); + + auto ev {wxCommandEvent(wxEVT_COMBOBOX, test_field.choice()->GetId())}; + ev.SetEventObject(test_field.choice()); + test_field.choice()->ProcessWindowEvent(ev); + THEN( "on_change handler is executed.") { + REQUIRE(event_count == 1); + } + } + WHEN( "I receive a wxEVT_TEXT_ENTER event") { + event_count = 0; + wxMilliSleep(250); + + auto ev {wxCommandEvent(wxEVT_TEXT_ENTER, test_field.choice()->GetId())}; + ev.SetEventObject(test_field.choice()); + test_field.choice()->ProcessWindowEvent(ev); + THEN( "on_change handler is executed.") { + REQUIRE(event_count == 1); + } + } + WHEN( "My control loses focus.") { + event_count = 0; + test_field.choice()->SetFocus(); + wxMilliSleep(250); + + auto ev {wxFocusEvent(wxEVT_KILL_FOCUS, test_field.choice()->GetId())}; + ev.SetEventObject(test_field.choice()); + test_field.choice()->ProcessWindowEvent(ev); + + THEN( "on_change handler is executed and on_kill_focus handler is executed.") { + REQUIRE(event_count == 2); + } + } + } +} diff --git a/src/test/GUI/test_field_point.cpp b/src/test/GUI/test_field_point.cpp new file mode 100644 index 000000000..e67ee7ecd --- /dev/null +++ b/src/test/GUI/test_field_point.cpp @@ -0,0 +1,299 @@ +#include + +#ifndef WX_PRECOMP +#include "wx/app.h" +#include "wx/sizer.h" +#include "wx/uiaction.h" +#endif // WX_PRECOMP + +#include +#include + +#include "testableframe.h" +#include "OptionsGroup/Field.hpp" +#include "ConfigBase.hpp" +#include "Point.hpp" + +using namespace std::string_literals; + +SCENARIO( "UI_Point: default values from options and basic accessor methods") { + wxTestableFrame* old = dynamic_cast(wxTheApp->GetTopWindow()); + old->Destroy(); + wxTheApp->SetTopWindow(new wxTestableFrame()); + wxUIActionSimulator sim; + wxMilliSleep(500); + + GIVEN( "A UI point method and a X,Y coordinate (3.2, 10.2) as the default_value") { + auto simple_option {ConfigOptionDef()}; + auto* default_point {new ConfigOptionPoint(Pointf(3.2, 10.2))}; + simple_option.default_value = default_point; + auto test_field {Slic3r::GUI::UI_Point(wxTheApp->GetTopWindow(), simple_option)}; + + THEN( "get_string() returns '3.2;10.2'.") { + REQUIRE(test_field.get_string() == "3.2;10.2"s); + } + THEN( "get_point() yields a Pointf structure with x = 3.2, y = 10.2") { + REQUIRE(test_field.get_point().x == 3.2); + REQUIRE(test_field.get_point().y == 10.2); + } + } + GIVEN( "A UI point method and a tooltip in simple_option") { + auto simple_option {ConfigOptionDef()}; + auto* default_point {new ConfigOptionPoint(Pointf(3.2, 10.2))}; + simple_option.default_value = default_point; + + auto test_field {Slic3r::GUI::UI_Point(wxTheApp->GetTopWindow(), simple_option)}; + THEN( "Tooltip for both labels and textctrls matches simple_option") { + REQUIRE(test_field.ctrl_x()->GetToolTipText().ToStdString() == simple_option.tooltip ); + REQUIRE(test_field.lbl_x()->GetToolTipText().ToStdString() == simple_option.tooltip ); + REQUIRE(test_field.ctrl_y()->GetToolTipText().ToStdString() == simple_option.tooltip ); + REQUIRE(test_field.lbl_y()->GetToolTipText().ToStdString() == simple_option.tooltip ); + } + THEN( "get_point() yields a Pointf structure with x = 3.2, y = 10.2") { + REQUIRE(test_field.get_point().x == 3.2); + REQUIRE(test_field.get_point().y == 10.2); + } + } +} + +SCENARIO( "UI_Point: set_value works with several types of inputs") { + wxTestableFrame* old = dynamic_cast(wxTheApp->GetTopWindow()); + old->Destroy(); + wxTheApp->SetTopWindow(new wxTestableFrame()); + wxUIActionSimulator sim; + wxMilliSleep(500); + GIVEN( "A UI point method with no default value.") { + auto simple_option {ConfigOptionDef()}; + auto test_field {Slic3r::GUI::UI_Point(wxTheApp->GetTopWindow(), simple_option)}; + WHEN( "set_value is called with a Pointf(19.0, 2.1)") { + test_field.set_value(Pointf(19.0, 2.1)); + THEN( "get_point() returns a Pointf(19.0, 2.1)") { + REQUIRE(test_field.get_point() == Pointf(19.0, 2.1)); + } + THEN( "get_string() returns '19.0;2.1'") { + REQUIRE(test_field.get_string() == "19.0;2.1"s); + } + THEN( "X TextCtrl contains X coordinate") { + REQUIRE(test_field.ctrl_x()->GetValue() == wxString("19.0"s)); + } + THEN( "Y TextCtrl contains Y coordinate") { + REQUIRE(test_field.ctrl_y()->GetValue() == wxString("2.1"s)); + } + } + WHEN( "set_value is called with a Pointf3(19.0, 2.1, 0.2)") { + test_field.set_value(Pointf3(19.0, 2.1, 0.2)); + THEN( "get_point() returns a Pointf(19.0, 2.1)") { + REQUIRE(test_field.get_point() == Pointf(19.0, 2.1)); + } + THEN( "get_point3() returns a Pointf3(19.0, 2.1, 0.0)") { + REQUIRE(test_field.get_point3() == Pointf3(19.0, 2.1, 0.0)); + } + THEN( "get_string() returns '19.0;2.1'") { + REQUIRE(test_field.get_string() == "19.0;2.1"s); + } + THEN( "X TextCtrl contains X coordinate") { + REQUIRE(test_field.ctrl_x()->GetValue() == wxString("19.0"s)); + } + THEN( "Y TextCtrl contains Y coordinate") { + REQUIRE(test_field.ctrl_y()->GetValue() == wxString("2.1"s)); + } + } + WHEN( "set_value is called with a string of the form '30.9;211.2'") { + test_field.set_value("30.9;211.2"s); + THEN( "get_point() returns a Pointf(30.9, 211.2)") { + REQUIRE(test_field.get_point() == Pointf(30.9, 211.2)); + } + THEN( "get_string() returns '30.9;211.2'") { + REQUIRE(test_field.get_string() == "30.9;211.2"s); + } + THEN( "X TextCtrl contains X coordinate") { + REQUIRE(test_field.ctrl_x()->GetValue() == wxString("30.9"s)); + } + THEN( "Y TextCtrl contains Y coordinate") { + REQUIRE(test_field.ctrl_y()->GetValue() == wxString("211.2"s)); + } + } + WHEN( "set_value is called with a wxString of the form '30.9;211.2'") { + test_field.set_value(wxString("30.9;211.2"s)); + THEN( "get_point() returns a Pointf(30.9, 211.2)") { + REQUIRE(test_field.get_point() == Pointf(30.9, 211.2)); + } + THEN( "get_string() returns '30.9;211.2'") { + REQUIRE(test_field.get_string() == "30.9;211.2"s); + } + THEN( "X TextCtrl contains X coordinate") { + REQUIRE(test_field.ctrl_x()->GetValue() == wxString("30.9"s)); + } + THEN( "Y TextCtrl contains Y coordinate") { + REQUIRE(test_field.ctrl_y()->GetValue() == wxString("211.2"s)); + } + } + } +} + +SCENARIO( "UI_Point: Event responses") { + wxTestableFrame* old = dynamic_cast(wxTheApp->GetTopWindow()); + old->Destroy(); + wxTheApp->SetTopWindow(new wxTestableFrame()); + wxMilliSleep(250); + GIVEN ( "A UI_Point with no default value and a registered on_change method that increments a counter.") { + auto simple_option {ConfigOptionDef()}; + auto test_field {Slic3r::GUI::UI_Point(wxTheApp->GetTopWindow(), simple_option)}; + auto event_count {0}; + auto changefunc {[&event_count] (const std::string& opt_id, std::tuple value) { event_count++; }}; + auto killfunc {[&event_count](const std::string& opt_id) { event_count++; }}; + + test_field.on_change = changefunc; + test_field.on_kill_focus = killfunc; + + test_field.disable_change_event = false; + + WHEN( "kill focus event is received on X") { + event_count = 0; + auto ev {wxFocusEvent(wxEVT_KILL_FOCUS, test_field.ctrl_x()->GetId())}; + ev.SetEventObject(test_field.ctrl_x()); + test_field.ctrl_x()->ProcessWindowEvent(ev); + THEN( "on_kill_focus is executed.") { + REQUIRE(event_count == 2); + } + } + WHEN( "kill focus event is received on Y") { + event_count = 0; + auto ev {wxFocusEvent(wxEVT_KILL_FOCUS, test_field.ctrl_y()->GetId())}; + ev.SetEventObject(test_field.ctrl_y()); + test_field.ctrl_y()->ProcessWindowEvent(ev); + THEN( "on_kill_focus is executed.") { + REQUIRE(event_count == 2); + } + } + WHEN( "enter key pressed event is received on X") { + event_count = 0; + auto ev {wxCommandEvent(wxEVT_TEXT_ENTER, test_field.ctrl_x()->GetId())}; + ev.SetEventObject(test_field.ctrl_x()); + test_field.ctrl_x()->ProcessWindowEvent(ev); + THEN( "on_change is executed.") { + REQUIRE(event_count == 1); + } + } + WHEN( "enter key pressed event is received on Y") { + event_count = 0; + auto ev {wxCommandEvent(wxEVT_TEXT_ENTER, test_field.ctrl_y()->GetId())}; + ev.SetEventObject(test_field.ctrl_y()); + test_field.ctrl_y()->ProcessWindowEvent(ev); + THEN( "on_change is executed.") { + REQUIRE(event_count == 1); + } + } + } + GIVEN ( "A UI_Point with no default value and a registered on_change method that increments a counter only when disable_change_event = false.") { + auto simple_option {ConfigOptionDef()}; + auto test_field {Slic3r::GUI::UI_Point(wxTheApp->GetTopWindow(), simple_option)}; + auto event_count {0}; + auto changefunc {[&event_count] (const std::string& opt_id, std::tuple value) { event_count++; }}; + auto killfunc {[&event_count](const std::string& opt_id) { event_count += 1; }}; + + test_field.on_change = changefunc; + test_field.on_kill_focus = killfunc; + test_field.disable_change_event = true; + + WHEN( "kill focus event is received on X") { + event_count = 0; + auto ev {wxFocusEvent(wxEVT_KILL_FOCUS, test_field.ctrl_x()->GetId())}; + ev.SetEventObject(test_field.ctrl_x()); + test_field.ctrl_x()->ProcessWindowEvent(ev); + THEN( "on_kill_focus is executed.") { + REQUIRE(event_count == 1); + } + } + WHEN( "kill focus event is received on Y") { + event_count = 0; + auto ev {wxFocusEvent(wxEVT_KILL_FOCUS, test_field.ctrl_y()->GetId())}; + ev.SetEventObject(test_field.ctrl_y()); + test_field.ctrl_y()->ProcessWindowEvent(ev); + THEN( "on_kill_focus is executed.") { + REQUIRE(event_count == 1); + } + } + WHEN( "enter key pressed event is received on X") { + event_count = 0; + auto ev {wxCommandEvent(wxEVT_TEXT_ENTER, test_field.ctrl_x()->GetId())}; + ev.SetEventObject(test_field.ctrl_x()); + test_field.ctrl_x()->ProcessWindowEvent(ev); + THEN( "on_change is not executed.") { + REQUIRE(event_count == 0); + } + } + WHEN( "enter key pressed event is received on Y") { + event_count = 0; + auto ev {wxCommandEvent(wxEVT_TEXT_ENTER, test_field.ctrl_y()->GetId())}; + ev.SetEventObject(test_field.ctrl_y()); + test_field.ctrl_y()->ProcessWindowEvent(ev); + THEN( "on_change is not executed.") { + REQUIRE(event_count == 0); + } + } + } +} + +SCENARIO( "UI_Point: Enable/Disable") { + wxTestableFrame* old = dynamic_cast(wxTheApp->GetTopWindow()); + old->Destroy(); + wxTheApp->SetTopWindow(new wxTestableFrame()); + wxMilliSleep(250); + GIVEN ( "A UI_Point with no default value.") { + auto simple_option {ConfigOptionDef()}; + auto test_field {Slic3r::GUI::UI_Point(wxTheApp->GetTopWindow(), simple_option)}; + WHEN( "disable() is called") { + test_field.disable(); + THEN( "IsEnabled == False for X and Y textctrls") { + REQUIRE(test_field.ctrl_x()->IsEnabled() == false); + REQUIRE(test_field.ctrl_y()->IsEnabled() == false); + } + } + WHEN( "enable() is called") { + test_field.enable(); + THEN( "IsEnabled == True for X and Y textctrls") { + REQUIRE(test_field.ctrl_x()->IsEnabled() == true); + REQUIRE(test_field.ctrl_y()->IsEnabled() == true); + } + } + WHEN( "toggle() is called with false argument") { + test_field.toggle(false); + THEN( "IsEnabled == False for X and Y textctrls") { + REQUIRE(test_field.ctrl_x()->IsEnabled() == false); + REQUIRE(test_field.ctrl_y()->IsEnabled() == false); + } + } + WHEN( "toggle() is called with true argument") { + test_field.toggle(true); + THEN( "IsEnabled == True for X and Y textctrls") { + REQUIRE(test_field.ctrl_x()->IsEnabled() == true); + REQUIRE(test_field.ctrl_y()->IsEnabled() == true); + } + } + } +} + +SCENARIO( "UI_Point: get_sizer()") { + wxTestableFrame* old = dynamic_cast(wxTheApp->GetTopWindow()); + old->Destroy(); + wxTheApp->SetTopWindow(new wxTestableFrame()); + wxMilliSleep(250); + GIVEN ( "A UI_Point with no default value.") { + auto simple_option {ConfigOptionDef()}; + auto test_field {Slic3r::GUI::UI_Point(wxTheApp->GetTopWindow(), simple_option)}; + WHEN( "get_sizer() is called") { + THEN( "get_sizer() returns a wxSizer that has 4 direct children in it that are Windows.") { + REQUIRE(test_field.get_sizer()->GetItemCount() == 4); + auto tmp {test_field.get_sizer()->GetChildren().begin()}; + REQUIRE((*tmp)->IsWindow() == true); + tmp++; + REQUIRE((*tmp)->IsWindow() == true); + tmp++; + REQUIRE((*tmp)->IsWindow() == true); + tmp++; + REQUIRE((*tmp)->IsWindow() == true); + } + } + } +} diff --git a/src/test/GUI/test_field_point3.cpp b/src/test/GUI/test_field_point3.cpp new file mode 100644 index 000000000..8cf6f3951 --- /dev/null +++ b/src/test/GUI/test_field_point3.cpp @@ -0,0 +1,376 @@ +#include + +#ifndef WX_PRECOMP +#include "wx/app.h" +#include "wx/sizer.h" +#include "wx/uiaction.h" +#endif // WX_PRECOMP + +#include +#include + +#include "testableframe.h" +#include "OptionsGroup/Field.hpp" +#include "ConfigBase.hpp" +#include "Point.hpp" + +using namespace std::string_literals; + +SCENARIO( "UI_Point3: default values from options and basic accessor methods") { + wxTestableFrame* old = dynamic_cast(wxTheApp->GetTopWindow()); + old->Destroy(); + wxTheApp->SetTopWindow(new wxTestableFrame()); + wxUIActionSimulator sim; + wxMilliSleep(500); + + GIVEN( "A UI point method and a X,Y coordinate (3.2, 10.2, 0.0) as the default_value") { + auto simple_option {ConfigOptionDef()}; + auto* default_point {new ConfigOptionPoint3(Pointf3(3.2, 10.2, 0.0))}; + simple_option.default_value = default_point; + auto test_field {Slic3r::GUI::UI_Point3(wxTheApp->GetTopWindow(), simple_option)}; + + THEN( "get_string() returns '3.2;10.2;0.0'.") { + REQUIRE(test_field.get_string() == "3.2;10.2;0.0"s); + } + THEN( "get_point() yields a Pointf structure with x = 3.2, y = 10.2") { + REQUIRE(test_field.get_point().x == 3.2); + REQUIRE(test_field.get_point().y == 10.2); + } + THEN( "get_point3() yields a Pointf3 structure with x = 3.2, y = 10.2, z = 0.0") { + REQUIRE(test_field.get_point3().x == 3.2); + REQUIRE(test_field.get_point3().y == 10.2); + REQUIRE(test_field.get_point3().z == 0.0); + } + } + GIVEN( "A UI point, coordinate in X/Y/Z and a tooltip in simple_option") { + auto simple_option {ConfigOptionDef()}; + auto* default_point {new ConfigOptionPoint3(Pointf3(3.2, 10.2, 0.2))}; + simple_option.default_value = default_point; + + auto test_field {Slic3r::GUI::UI_Point3(wxTheApp->GetTopWindow(), simple_option)}; + THEN( "Tooltip for both labels and textctrls matches simple_option") { + REQUIRE(test_field.ctrl_x()->GetToolTipText().ToStdString() == simple_option.tooltip ); + REQUIRE(test_field.lbl_x()->GetToolTipText().ToStdString() == simple_option.tooltip ); + REQUIRE(test_field.ctrl_y()->GetToolTipText().ToStdString() == simple_option.tooltip ); + REQUIRE(test_field.lbl_y()->GetToolTipText().ToStdString() == simple_option.tooltip ); + REQUIRE(test_field.ctrl_z()->GetToolTipText().ToStdString() == simple_option.tooltip ); + REQUIRE(test_field.lbl_z()->GetToolTipText().ToStdString() == simple_option.tooltip ); + } + THEN( "get_point3() yields a Pointf3 structure with x = 3.2, y = 10.2, z = 0.2") { + REQUIRE(test_field.get_point3().x == 3.2); + REQUIRE(test_field.get_point3().y == 10.2); + REQUIRE(test_field.get_point3().z == 0.2); + } + THEN( "get_point() yields a Pointf structure with x = 3.2, y = 10.2") { + REQUIRE(test_field.get_point().x == 3.2); + REQUIRE(test_field.get_point().y == 10.2); + } + } +} + +SCENARIO( "UI_Point3: set_value works with several types of inputs") { + wxTestableFrame* old = dynamic_cast(wxTheApp->GetTopWindow()); + old->Destroy(); + wxTheApp->SetTopWindow(new wxTestableFrame()); + wxUIActionSimulator sim; + wxMilliSleep(500); + GIVEN( "A UI point method with no default value.") { + auto simple_option {ConfigOptionDef()}; + auto test_field {Slic3r::GUI::UI_Point3(wxTheApp->GetTopWindow(), simple_option)}; + WHEN( "set_value is called with a Pointf(19.0, 2.1)") { + test_field.set_value(Pointf(19.0, 2.1)); + THEN( "get_point3() returns a Pointf3(19.0, 2.1, 0.0)") { + REQUIRE(test_field.get_point3() == Pointf3(19.0, 2.1, 0.0)); + } + THEN( "get_point() returns a Pointf(19.0, 2.1)") { + REQUIRE(test_field.get_point() == Pointf(19.0, 2.1)); + } + THEN( "get_string() returns '19.0;2.1'") { + REQUIRE(test_field.get_string() == "19.0;2.1;0.0"s); + } + THEN( "X TextCtrl contains X coordinate") { + REQUIRE(test_field.ctrl_x()->GetValue() == wxString("19.0"s)); + } + THEN( "Y TextCtrl contains Y coordinate") { + REQUIRE(test_field.ctrl_y()->GetValue() == wxString("2.1"s)); + } + THEN( "Z TextCtrl contains Z coordinate") { + REQUIRE(test_field.ctrl_z()->GetValue() == wxString("0.0"s)); + } + } + WHEN( "set_value is called with a Pointf3(19.0, 2.1, 0.6)") { + test_field.set_value(Pointf3(19.0, 2.1, 0.6)); + THEN( "get_point3() returns a Pointf3(19.0, 2.1, 0.6)") { + REQUIRE(test_field.get_point3() == Pointf3(19.0, 2.1, 0.6)); + } + THEN( "get_point() returns a Pointf(19.0, 2.1)") { + REQUIRE(test_field.get_point() == Pointf(19.0, 2.1)); + } + THEN( "get_string() returns '19.0;2.1;0.6'") { + REQUIRE(test_field.get_string() == "19.0;2.1;0.6"s); + } + THEN( "X TextCtrl contains X coordinate") { + REQUIRE(test_field.ctrl_x()->GetValue() == wxString("19.0"s)); + } + THEN( "Y TextCtrl contains Y coordinate") { + REQUIRE(test_field.ctrl_y()->GetValue() == wxString("2.1"s)); + } + THEN( "Z TextCtrl contains Z coordinate") { + REQUIRE(test_field.ctrl_z()->GetValue() == wxString("0.6"s)); + } + } + WHEN( "set_value is called with a string of the form '30.9;211.2;411.0'") { + test_field.set_value("30.9;211.2;411.0"s); + THEN( "get_point3() returns a Pointf3(30.9, 211.2, 411.0)") { + REQUIRE(test_field.get_point3() == Pointf3(30.9, 211.2, 411.0)); + } + THEN( "get_point() returns a Pointf(30.9, 211.2)") { + REQUIRE(test_field.get_point() == Pointf(30.9, 211.2)); + } + THEN( "get_string() returns '30.9;211.2;411.0'") { + REQUIRE(test_field.get_string() == "30.9;211.2;411.0"s); + } + THEN( "X TextCtrl contains X coordinate") { + REQUIRE(test_field.ctrl_x()->GetValue() == wxString("30.9"s)); + } + THEN( "Y TextCtrl contains Y coordinate") { + REQUIRE(test_field.ctrl_y()->GetValue() == wxString("211.2"s)); + } + THEN( "Z TextCtrl contains Z coordinate") { + REQUIRE(test_field.ctrl_z()->GetValue() == wxString("411.0"s)); + } + } + WHEN( "set_value is called with a wxString of the form '30.9;211.2;411.0'") { + test_field.set_value(wxString("30.9;211.2;411.0"s)); + THEN( "get_point3() returns a Pointf3(30.9, 211.2, 411.0)") { + REQUIRE(test_field.get_point3() == Pointf3(30.9, 211.2, 411.0)); + } + THEN( "get_point() returns a Pointf(30.9, 211.2)") { + REQUIRE(test_field.get_point() == Pointf(30.9, 211.2)); + } + THEN( "get_string() returns '30.9;211.2;411.0'") { + REQUIRE(test_field.get_string() == "30.9;211.2;411.0"s); + } + THEN( "X TextCtrl contains X coordinate") { + REQUIRE(test_field.ctrl_x()->GetValue() == wxString("30.9"s)); + } + THEN( "Y TextCtrl contains Y coordinate") { + REQUIRE(test_field.ctrl_y()->GetValue() == wxString("211.2"s)); + } + THEN( "Z TextCtrl contains Z coordinate") { + REQUIRE(test_field.ctrl_z()->GetValue() == wxString("411.0"s)); + } + } + } +} + +SCENARIO( "UI_Point3: Event responses") { + wxTestableFrame* old = dynamic_cast(wxTheApp->GetTopWindow()); + old->Destroy(); + wxTheApp->SetTopWindow(new wxTestableFrame()); + wxMilliSleep(250); + GIVEN ( "A UI_Point3 with no default value and a registered on_change method that increments a counter.") { + auto simple_option {ConfigOptionDef()}; + auto test_field {Slic3r::GUI::UI_Point3(wxTheApp->GetTopWindow(), simple_option)}; + auto event_count {0}; + auto changefunc {[&event_count] (const std::string& opt_id, std::tuple value) { event_count++; }}; + auto killfunc {[&event_count](const std::string& opt_id) { event_count++; }}; + + test_field.on_change = changefunc; + test_field.on_kill_focus = killfunc; + + test_field.disable_change_event = false; + + WHEN( "kill focus event is received on X") { + event_count = 0; + auto ev {wxFocusEvent(wxEVT_KILL_FOCUS, test_field.ctrl_x()->GetId())}; + ev.SetEventObject(test_field.ctrl_x()); + test_field.ctrl_x()->ProcessWindowEvent(ev); + THEN( "on_kill_focus is executed.") { + REQUIRE(event_count == 2); + } + } + WHEN( "kill focus event is received on Y") { + event_count = 0; + auto ev {wxFocusEvent(wxEVT_KILL_FOCUS, test_field.ctrl_y()->GetId())}; + ev.SetEventObject(test_field.ctrl_y()); + test_field.ctrl_y()->ProcessWindowEvent(ev); + THEN( "on_kill_focus is executed.") { + REQUIRE(event_count == 2); + } + } + WHEN( "kill focus event is received on Z") { + event_count = 0; + auto ev {wxFocusEvent(wxEVT_KILL_FOCUS, test_field.ctrl_z()->GetId())}; + ev.SetEventObject(test_field.ctrl_z()); + test_field.ctrl_z()->ProcessWindowEvent(ev); + THEN( "on_kill_focus is executed.") { + REQUIRE(event_count == 2); + } + } + WHEN( "enter key pressed event is received on X") { + event_count = 0; + auto ev {wxCommandEvent(wxEVT_TEXT_ENTER, test_field.ctrl_x()->GetId())}; + ev.SetEventObject(test_field.ctrl_x()); + test_field.ctrl_x()->ProcessWindowEvent(ev); + THEN( "on_change is executed.") { + REQUIRE(event_count == 1); + } + } + WHEN( "enter key pressed event is received on Y") { + event_count = 0; + auto ev {wxCommandEvent(wxEVT_TEXT_ENTER, test_field.ctrl_y()->GetId())}; + ev.SetEventObject(test_field.ctrl_y()); + test_field.ctrl_y()->ProcessWindowEvent(ev); + THEN( "on_change is executed.") { + REQUIRE(event_count == 1); + } + } + WHEN( "enter key pressed event is received on Z") { + event_count = 0; + auto ev {wxCommandEvent(wxEVT_TEXT_ENTER, test_field.ctrl_z()->GetId())}; + ev.SetEventObject(test_field.ctrl_z()); + test_field.ctrl_z()->ProcessWindowEvent(ev); + THEN( "on_change is executed.") { + REQUIRE(event_count == 1); + } + } + } + GIVEN ( "A UI_Point3 with no default value and a registered on_change method that increments a counter only when disable_change_event = false.") { + auto simple_option {ConfigOptionDef()}; + auto test_field {Slic3r::GUI::UI_Point3(wxTheApp->GetTopWindow(), simple_option)}; + auto event_count {0}; + auto changefunc {[&event_count] (const std::string& opt_id, std::tuple value) { event_count++; }}; + auto killfunc {[&event_count](const std::string& opt_id) { event_count += 1; }}; + + test_field.on_change = changefunc; + test_field.on_kill_focus = killfunc; + test_field.disable_change_event = true; + + WHEN( "kill focus event is received on X") { + event_count = 0; + auto ev {wxFocusEvent(wxEVT_KILL_FOCUS, test_field.ctrl_x()->GetId())}; + ev.SetEventObject(test_field.ctrl_x()); + test_field.ctrl_x()->ProcessWindowEvent(ev); + THEN( "on_kill_focus is executed.") { + REQUIRE(event_count == 1); + } + } + WHEN( "kill focus event is received on Y") { + event_count = 0; + auto ev {wxFocusEvent(wxEVT_KILL_FOCUS, test_field.ctrl_y()->GetId())}; + ev.SetEventObject(test_field.ctrl_y()); + test_field.ctrl_y()->ProcessWindowEvent(ev); + THEN( "on_kill_focus is executed.") { + REQUIRE(event_count == 1); + } + } + WHEN( "kill focus event is received on Z") { + event_count = 0; + auto ev {wxFocusEvent(wxEVT_KILL_FOCUS, test_field.ctrl_z()->GetId())}; + ev.SetEventObject(test_field.ctrl_z()); + test_field.ctrl_z()->ProcessWindowEvent(ev); + THEN( "on_kill_focus is executed.") { + REQUIRE(event_count == 1); + } + } + WHEN( "enter key pressed event is received on X") { + event_count = 0; + auto ev {wxCommandEvent(wxEVT_TEXT_ENTER, test_field.ctrl_x()->GetId())}; + ev.SetEventObject(test_field.ctrl_x()); + test_field.ctrl_x()->ProcessWindowEvent(ev); + THEN( "on_change is not executed.") { + REQUIRE(event_count == 0); + } + } + WHEN( "enter key pressed event is received on Y") { + event_count = 0; + auto ev {wxCommandEvent(wxEVT_TEXT_ENTER, test_field.ctrl_y()->GetId())}; + ev.SetEventObject(test_field.ctrl_y()); + test_field.ctrl_y()->ProcessWindowEvent(ev); + THEN( "on_change is not executed.") { + REQUIRE(event_count == 0); + } + } + WHEN( "enter key pressed event is received on Z") { + event_count = 0; + auto ev {wxCommandEvent(wxEVT_TEXT_ENTER, test_field.ctrl_z()->GetId())}; + ev.SetEventObject(test_field.ctrl_z()); + test_field.ctrl_z()->ProcessWindowEvent(ev); + THEN( "on_change is not executed.") { + REQUIRE(event_count == 0); + } + } + } +} + +SCENARIO( "UI_Point3: Enable/Disable") { + wxTestableFrame* old = dynamic_cast(wxTheApp->GetTopWindow()); + old->Destroy(); + wxTheApp->SetTopWindow(new wxTestableFrame()); + wxMilliSleep(250); + GIVEN ( "A UI_Point3 with no default value.") { + auto simple_option {ConfigOptionDef()}; + auto test_field {Slic3r::GUI::UI_Point3(wxTheApp->GetTopWindow(), simple_option)}; + WHEN( "disable() is called") { + test_field.disable(); + THEN( "IsEnabled == False for X and Y and Z textctrls") { + REQUIRE(test_field.ctrl_x()->IsEnabled() == false); + REQUIRE(test_field.ctrl_y()->IsEnabled() == false); + REQUIRE(test_field.ctrl_z()->IsEnabled() == false); + } + } + WHEN( "enable() is called") { + test_field.enable(); + THEN( "IsEnabled == True for X and Y and Z textctrls") { + REQUIRE(test_field.ctrl_x()->IsEnabled() == true); + REQUIRE(test_field.ctrl_y()->IsEnabled() == true); + REQUIRE(test_field.ctrl_z()->IsEnabled() == true); + } + } + WHEN( "toggle() is called with false argument") { + test_field.toggle(false); + THEN( "IsEnabled == False for X and Y and Z textctrls") { + REQUIRE(test_field.ctrl_x()->IsEnabled() == false); + REQUIRE(test_field.ctrl_y()->IsEnabled() == false); + REQUIRE(test_field.ctrl_z()->IsEnabled() == false); + } + } + WHEN( "toggle() is called with true argument") { + test_field.toggle(true); + THEN( "IsEnabled == True for X and Y and Z textctrls") { + REQUIRE(test_field.ctrl_x()->IsEnabled() == true); + REQUIRE(test_field.ctrl_y()->IsEnabled() == true); + REQUIRE(test_field.ctrl_z()->IsEnabled() == true); + } + } + } +} + +SCENARIO( "UI_Point3: get_sizer()") { + wxTestableFrame* old = dynamic_cast(wxTheApp->GetTopWindow()); + old->Destroy(); + wxTheApp->SetTopWindow(new wxTestableFrame()); + wxMilliSleep(250); + GIVEN ( "A UI_Point3 with no default value.") { + auto simple_option {ConfigOptionDef()}; + auto test_field {Slic3r::GUI::UI_Point3(wxTheApp->GetTopWindow(), simple_option)}; + WHEN( "get_sizer() is called") { + THEN( "get_sizer() returns a wxSizer that has 6 direct children in it that are Windows.") { + REQUIRE(test_field.get_sizer()->GetItemCount() == 6); + auto tmp {test_field.get_sizer()->GetChildren().begin()}; + REQUIRE((*tmp)->IsWindow() == true); + tmp++; + REQUIRE((*tmp)->IsWindow() == true); + tmp++; + REQUIRE((*tmp)->IsWindow() == true); + tmp++; + REQUIRE((*tmp)->IsWindow() == true); + tmp++; + REQUIRE((*tmp)->IsWindow() == true); + tmp++; + REQUIRE((*tmp)->IsWindow() == true); + } + } + } +} diff --git a/src/test/GUI/test_field_slider.cpp b/src/test/GUI/test_field_slider.cpp new file mode 100644 index 000000000..df534ead1 --- /dev/null +++ b/src/test/GUI/test_field_slider.cpp @@ -0,0 +1,207 @@ +#include +#ifndef WX_PRECOMP +#include +#include +#include +#include +#endif // WX_PRECOMP + +#include "testableframe.h" +#include "OptionsGroup/Field.hpp" +#include "ConfigBase.hpp" + +using namespace std::string_literals; + +SCENARIO( "UI_Slider: Defaults, Min/max handling, accessors.") { + wxTestableFrame* old = dynamic_cast(wxTheApp->GetTopWindow()); + old->Destroy(); + wxTheApp->SetTopWindow(new wxTestableFrame()); + wxUIActionSimulator sim; + wxMilliSleep(500); + auto simple_option {ConfigOptionDef()}; + auto* default_color {new ConfigOptionFloat(30.0)}; + simple_option.min = 0; + simple_option.max = 60; + simple_option.default_value = default_color; + GIVEN("A UI Slider with default scale") { + auto event_count {0}; + auto changefunc {[&event_count] (const std::string& opt_id, const double& color) { event_count++; }}; + WHEN("Option min is 0") { + simple_option.default_value = default_color; + auto test_field {Slic3r::GUI::UI_Slider(wxTheApp->GetTopWindow(), simple_option)}; + THEN("default of 0 is used.") { + REQUIRE(test_field.slider()->GetMin() == 0); + } + } + WHEN("Option max is 0") { + simple_option.max = 0; + simple_option.default_value = default_color; + auto test_field {Slic3r::GUI::UI_Slider(wxTheApp->GetTopWindow(), simple_option)}; + THEN("default of 100*10(scale) is used.") { + REQUIRE(test_field.slider()->GetMax() == 1000); + } + } + WHEN("Default value is used.") { + simple_option.default_value = default_color; + auto test_field {Slic3r::GUI::UI_Slider(wxTheApp->GetTopWindow(), simple_option)}; + THEN("Raw slider value is 30 * scale (10) = 300") { + REQUIRE(test_field.slider()->GetValue() == 300); + } + } + WHEN("set_scale is called with 25 for argument") { + simple_option.default_value = default_color; + simple_option.max = 100; + auto test_field {Slic3r::GUI::UI_Slider(wxTheApp->GetTopWindow(), simple_option)}; + test_field.set_scale(25); + THEN("Slider min is 0") { + REQUIRE(test_field.slider()->GetMin() == 0); + } + THEN("Slider max is 100*25 = 2500") { + REQUIRE(test_field.slider()->GetMax() == 2500); + } + THEN("Slider raw value is 30*25 = 750") { + REQUIRE(test_field.slider()->GetValue() == 750); + } + THEN("UI_Slider get_double still reads 30") { + REQUIRE(test_field.get_double() == Approx(30)); + } + THEN("UI_Slider get_int reads 30") { + REQUIRE(test_field.get_int() == 30); + } + THEN("Textctrl raw value still reads 30") { + REQUIRE(test_field.textctrl()->GetValue() == "30.0"s); + } + test_field.on_change = changefunc; + THEN("on_change does not fire.") { + REQUIRE(event_count == 0); + } + } + } + GIVEN("A UI Slider with default scale") { + WHEN("No default value is given from config.") { + simple_option.default_value = nullptr; + auto test_field {Slic3r::GUI::UI_Slider(wxTheApp->GetTopWindow(), simple_option)}; + THEN("Initial value of slider is 0.") { + REQUIRE(test_field.get_int() == 0); + REQUIRE(test_field.get_double() == 0); + REQUIRE(test_field.slider()->GetValue() == 0); + REQUIRE(test_field.textctrl()->GetValue() == "0.0"); + } + } + WHEN("disable() is called") { + simple_option.default_value = default_color; + auto test_field {Slic3r::GUI::UI_Slider(wxTheApp->GetTopWindow(), simple_option)}; + test_field.slider()->Enable(); + test_field.textctrl()->Enable(); + test_field.textctrl()->SetEditable(true); + + test_field.disable(); + THEN("Internal slider is disabled.") { + REQUIRE(test_field.slider()->IsEnabled() == false); + } + THEN("Internal textctrl is disabled.") { + REQUIRE(test_field.textctrl()->IsEnabled() == false); + REQUIRE(test_field.textctrl()->IsEditable() == false); + } + } + WHEN("enable() is called") { + simple_option.default_value = default_color; + auto test_field {Slic3r::GUI::UI_Slider(wxTheApp->GetTopWindow(), simple_option)}; + test_field.slider()->Disable(); + test_field.textctrl()->Disable(); + test_field.textctrl()->SetEditable(false); + + test_field.enable(); + THEN("Internal slider is enabled.") { + REQUIRE(test_field.slider()->IsEnabled() == true); + } + THEN("Internal textctrl is enabled.") { + REQUIRE(test_field.textctrl()->IsEnabled() == true); + REQUIRE(test_field.textctrl()->IsEditable() == true); + } + } + } + + GIVEN("A UI Slider with scale of 1") { + WHEN("Option min is 0") { + simple_option.default_value = default_color; + simple_option.min = 0; + auto test_field {Slic3r::GUI::UI_Slider(wxTheApp->GetTopWindow(), simple_option, 1)}; + WHEN("default of 0 is used.") { + REQUIRE(test_field.slider()->GetMin() == 0); + } + } + WHEN("Option max is 0") { + simple_option.default_value = default_color; + simple_option.max = 0; + auto test_field {Slic3r::GUI::UI_Slider(wxTheApp->GetTopWindow(), simple_option, 1)}; + WHEN("default of 10 * 100 = 1000 is used.") { + REQUIRE(test_field.slider()->GetMax() == 100); + } + } + WHEN("Default value is used.") { + simple_option.default_value = default_color; + auto test_field {Slic3r::GUI::UI_Slider(wxTheApp->GetTopWindow(), simple_option, 1)}; + THEN("Raw slider value is 30 * scale (1)") { + REQUIRE(test_field.slider()->GetValue() == 30); + } + } + } +} + +SCENARIO( "UI_Slider: Event handlers") { + wxTestableFrame* old = dynamic_cast(wxTheApp->GetTopWindow()); + old->Destroy(); + wxTheApp->SetTopWindow(new wxTestableFrame()); + wxUIActionSimulator sim; + wxMilliSleep(500); + auto simple_option {ConfigOptionDef()}; + auto* default_color {new ConfigOptionString("30")}; + simple_option.min = 0; + simple_option.max = 60; + simple_option.default_value = default_color; + auto event_count {0}; + auto changefunc {[&event_count] (const std::string& opt_id, const double& color) { event_count++; }}; + auto killfunc {[&event_count](const std::string& opt_id) { event_count += 1; }}; + GIVEN("A UI Slider") { + auto test_field {Slic3r::GUI::UI_Slider(wxTheApp->GetTopWindow(), simple_option)}; + test_field.on_change = changefunc; + test_field.on_kill_focus = killfunc; + WHEN("UI Slider receives a text change event (enter is pressed).") { + event_count = 0; + auto ev {wxCommandEvent(wxEVT_TEXT_ENTER, test_field.textctrl()->GetId())}; + ev.SetEventObject(test_field.textctrl()); + test_field.textctrl()->ProcessWindowEvent(ev); + THEN("_on_change fires") { + REQUIRE(event_count == 1); + } + } + WHEN("UI Slider textbox receives a text change event (enter is not pressed).") { + event_count = 0; + auto ev {wxCommandEvent(wxEVT_TEXT, test_field.textctrl()->GetId())}; + ev.SetEventObject(test_field.textctrl()); + test_field.textctrl()->ProcessWindowEvent(ev); + THEN("Nothing happens") { + REQUIRE(event_count == 0); + } + } + WHEN("UI Slider receives a slider changed event.") { + event_count = 0; + auto ev {wxCommandEvent(wxEVT_SLIDER, test_field.slider()->GetId())}; + ev.SetEventObject(test_field.slider()); + test_field.slider()->ProcessWindowEvent(ev); + THEN("on_change fires") { + REQUIRE(event_count == 1); + } + } + WHEN("UI_Slider text ctrl receives a kill focus event.") { + event_count = 0; + auto ev {wxFocusEvent(wxEVT_KILL_FOCUS, test_field.textctrl()->GetId())}; + ev.SetEventObject(test_field.textctrl()); + test_field.textctrl()->ProcessWindowEvent(ev); + THEN("_kill_focus and on_change fires") { + REQUIRE(event_count == 2); + } + } + } +} diff --git a/src/test/GUI/test_field_spinctrl.cpp b/src/test/GUI/test_field_spinctrl.cpp new file mode 100644 index 000000000..e7bcd3f72 --- /dev/null +++ b/src/test/GUI/test_field_spinctrl.cpp @@ -0,0 +1,156 @@ +#include + +#ifndef WX_PRECOMP + #include "wx/app.h" + #include "wx/sizer.h" + #include "wx/checkbox.h" + #include "wx/uiaction.h" +#endif // WX_PRECOMP +#include +#include "testableframe.h" +#include "OptionsGroup/Field.hpp" +#include "ConfigBase.hpp" + +using namespace Slic3r::GUI; +SCENARIO( "Spinctrl initializes with default value if available.") { + wxTestableFrame* old = dynamic_cast(wxTheApp->GetTopWindow()); + + old->Destroy(); + wxTheApp->SetTopWindow(new wxTestableFrame()); + wxMilliSleep(250); + + Slic3r::ConfigOptionDef simple_option; + simple_option.type = coInt; + auto* intopt { new Slic3r::ConfigOptionInt(7) }; + simple_option.default_value = intopt; // no delete, it's taken care of by ConfigOptionDef + GIVEN ( "A UI Spinctrl") { + auto test_field {Slic3r::GUI::UI_SpinCtrl(wxTheApp->GetTopWindow(), simple_option)}; + + wxTheApp->GetTopWindow()->Show(); + wxTheApp->GetTopWindow()->Fit(); + + REQUIRE(test_field.get_int() == 7); + } +} + +SCENARIO( "Receiving a Spinctrl event") { + wxTestableFrame* old = dynamic_cast(wxTheApp->GetTopWindow()); + old->Destroy(); + wxTheApp->SetTopWindow(new wxTestableFrame()); + wxMilliSleep(250); + GIVEN ( "A UI Spinctrl") { + auto exec_counter {0}; + auto changefunc {[&exec_counter] (const std::string& opt_id, int value) { exec_counter++; }}; + auto test_field {Slic3r::GUI::UI_SpinCtrl(wxTheApp->GetTopWindow(), Slic3r::ConfigOptionDef())}; + + test_field.on_change = changefunc; + + wxTheApp->GetTopWindow()->Show(); + wxTheApp->GetTopWindow()->Fit(); + WHEN( "A spin event occurs") { + exec_counter = 0; + auto ev {wxSpinEvent(wxEVT_SPINCTRL, test_field.spinctrl()->GetId())}; + ev.SetEventObject(test_field.spinctrl()); + test_field.spinctrl()->ProcessWindowEvent(ev); + wxYield(); + wxMilliSleep(250); + THEN( "on_change is executed.") { + REQUIRE(exec_counter == 1); + } + } + WHEN( "A spin event occurs and change event is disabled") { + exec_counter = 0; + test_field.disable_change_event = false; + auto ev {wxSpinEvent(wxEVT_SPINCTRL, test_field.spinctrl()->GetId())}; + ev.SetEventObject(test_field.spinctrl()); + test_field.spinctrl()->ProcessWindowEvent(ev); + wxYield(); + wxMilliSleep(250); + THEN( "on_change is not executed.") { + REQUIRE(exec_counter == 1); + } + } + } +} + +SCENARIO( "Changing the text via entry works on pressing enter") { + wxTestableFrame* old = dynamic_cast(wxTheApp->GetTopWindow()); + old->Destroy(); + wxTheApp->SetTopWindow(new wxTestableFrame()); + wxUIActionSimulator sim; + wxMilliSleep(500); + GIVEN ( "A UI Spinctrl") { + auto exec_counter {0}; + auto changefunc {[&exec_counter] (const std::string& opt_id, int value) { exec_counter++; }}; + auto test_field {Slic3r::GUI::UI_SpinCtrl(wxTheApp->GetTopWindow(), Slic3r::ConfigOptionDef())}; + + test_field.on_change = changefunc; + + wxTheApp->GetTopWindow()->Show(); + wxTheApp->GetTopWindow()->Fit(); + WHEN( "A number is entered followed by enter key") { + exec_counter = 0; + test_field.spinctrl()->SetFocus(); + wxYield(); + wxMilliSleep(250); + sim.Char('3'); + wxMilliSleep(250); + sim.Char(WXK_RETURN); + wxMilliSleep(250); + wxYield(); + THEN( "on_change is executed.") { + REQUIRE(exec_counter == 1); + } + THEN( "get_int returns entered value.") { + REQUIRE(test_field.get_int() == 3); + } + } + WHEN( "A number is entered followed by enter key and change event is disabled") { + exec_counter = 0; + test_field.disable_change_event = true; + test_field.spinctrl()->SetFocus(); + wxYield(); + wxMilliSleep(250); + sim.Char('3'); + wxMilliSleep(250); + sim.Char(WXK_RETURN); + wxMilliSleep(250); + wxYield(); + THEN( "on_change is not executed.") { + REQUIRE(exec_counter == 0); + } + THEN( "get_int returns entered value.") { + REQUIRE(test_field.get_int() == 3); + } + } + WHEN( "A number is entered and focus is lost") { + auto killfunc {[&exec_counter](const std::string& opt_id) { exec_counter += 1; }}; + test_field.on_kill_focus = killfunc; + + auto ev {wxFocusEvent(wxEVT_KILL_FOCUS, test_field.spinctrl()->GetId())}; + ev.SetEventObject(test_field.spinctrl()); + + exec_counter = 0; + test_field.spinctrl()->SetValue(3); + test_field.spinctrl()->SetFocus(); + wxYield(); + wxMilliSleep(250); + sim.Char('7'); + wxYield(); + wxMilliSleep(250); + test_field.spinctrl()->ProcessWindowEvent(ev); + wxMilliSleep(250); + wxYield(); + THEN( "on_kill_focus is executed and on_change are both executed.") { + REQUIRE(exec_counter == 2); + } + THEN( "get_int returns updated value.") { + REQUIRE(test_field.get_int() == 7); + } + THEN( "get_bool returns 0.") { + REQUIRE(test_field.get_bool() == 0); + } + } + } +} + diff --git a/src/test/GUI/test_field_textbox.cpp b/src/test/GUI/test_field_textbox.cpp new file mode 100644 index 000000000..97f11e06b --- /dev/null +++ b/src/test/GUI/test_field_textbox.cpp @@ -0,0 +1,219 @@ +#include + +#ifndef WX_PRECOMP + #include "wx/app.h" + #include "wx/sizer.h" + #include "wx/uiaction.h" +#endif // WX_PRECOMP +#include +#include +#include "testableframe.h" +#include "OptionsGroup/Field.hpp" +#include "ConfigBase.hpp" + +using namespace Slic3r::GUI; +using namespace std::string_literals; + +SCENARIO( "TextCtrl initializes with default value if available.") { + wxTestableFrame* old = dynamic_cast(wxTheApp->GetTopWindow()); + + old->Destroy(); + wxTheApp->SetTopWindow(new wxTestableFrame()); + wxMilliSleep(250); + + Slic3r::ConfigOptionDef simple_option; + simple_option.type = coString; + auto* stropt { new Slic3r::ConfigOptionString("7") }; + simple_option.default_value = stropt; // no delete, it's taken care of by ConfigOptionDef + GIVEN ( "A UI Textctrl") { + auto test_field {Slic3r::GUI::UI_TextCtrl(wxTheApp->GetTopWindow(), simple_option)}; + + wxTheApp->GetTopWindow()->Show(); + wxTheApp->GetTopWindow()->Fit(); + + REQUIRE(test_field.get_string() == "7"s); + } +} + +SCENARIO( "Receiving a Textctrl event") { + wxTestableFrame* old = dynamic_cast(wxTheApp->GetTopWindow()); + old->Destroy(); + wxTheApp->SetTopWindow(new wxTestableFrame()); + wxMilliSleep(250); + GIVEN ( "A UI Textctrl") { + auto exec_counter {0}; + auto changefunc {[&exec_counter] (const std::string& opt_id, std::string value) { exec_counter++; }}; + auto test_field {Slic3r::GUI::UI_TextCtrl(wxTheApp->GetTopWindow(), Slic3r::ConfigOptionDef())}; + + test_field.on_change = changefunc; + + wxTheApp->GetTopWindow()->Show(); + wxTheApp->GetTopWindow()->Fit(); + WHEN( "A text event occurs") { + exec_counter = 0; + auto ev {wxCommandEvent(wxEVT_TEXT_ENTER, test_field.textctrl()->GetId())}; + ev.SetEventObject(test_field.textctrl()); + test_field.textctrl()->ProcessWindowEvent(ev); + wxYield(); + wxMilliSleep(250); + THEN( "on_change is executed.") { + REQUIRE(exec_counter == 1); + } + } + WHEN( "A text event occurs and change event is disabled") { + exec_counter = 0; + test_field.disable_change_event = false; + auto ev {wxCommandEvent(wxEVT_TEXT_ENTER, test_field.textctrl()->GetId())}; + ev.SetEventObject(test_field.textctrl()); + test_field.textctrl()->ProcessWindowEvent(ev); + wxYield(); + wxMilliSleep(250); + THEN( "on_change is not executed.") { + REQUIRE(exec_counter == 1); + } + } + } +} + +SCENARIO( "TextCtrl: Changing the text via entry works on pressing enter") { + wxTestableFrame* old = dynamic_cast(wxTheApp->GetTopWindow()); + old->Destroy(); + wxTheApp->SetTopWindow(new wxTestableFrame()); + wxUIActionSimulator sim; + wxMilliSleep(500); + GIVEN ( "A UI Textctrl") { + auto exec_counter {0}; + auto changefunc {[&exec_counter] (const std::string& opt_id, std::string value) { exec_counter++; }}; + auto test_field {Slic3r::GUI::UI_TextCtrl(wxTheApp->GetTopWindow(), Slic3r::ConfigOptionDef())}; + + test_field.on_change = changefunc; + + wxTheApp->GetTopWindow()->Show(); + wxTheApp->GetTopWindow()->Fit(); + WHEN( "A number is entered followed by enter key") { + exec_counter = 0; + test_field.textctrl()->SetFocus(); + wxYield(); + wxMilliSleep(250); + sim.Char('3'); + wxMilliSleep(250); + sim.Char(WXK_RETURN); + wxMilliSleep(250); + wxYield(); + THEN( "on_change is executed.") { + REQUIRE(exec_counter == 1); + } + THEN( "get_string returns entered value.") { + REQUIRE(test_field.get_string() == "3"s); + } + } + WHEN( "A number is entered followed by enter key and change event is disabled") { + exec_counter = 0; + test_field.disable_change_event = true; + test_field.textctrl()->SetFocus(); + wxYield(); + wxMilliSleep(250); + sim.Char('3'); + wxMilliSleep(250); + sim.Char(WXK_RETURN); + wxMilliSleep(250); + wxYield(); + THEN( "on_change is not executed.") { + REQUIRE(exec_counter == 0); + } + THEN( "get_string returns entered value.") { + REQUIRE(test_field.get_string() == "3"s); + } + } + WHEN( "A number is entered and focus is lost") { + auto killfunc {[&exec_counter](const std::string& opt_id) { exec_counter += 1; }}; + test_field.on_kill_focus = killfunc; + + auto ev {wxFocusEvent(wxEVT_KILL_FOCUS, test_field.textctrl()->GetId())}; + ev.SetEventObject(test_field.textctrl()); + + exec_counter = 0; + test_field.textctrl()->SetValue("3"); + test_field.textctrl()->SetFocus(); + wxYield(); + wxMilliSleep(250); + sim.Char('7'); + wxYield(); + wxMilliSleep(250); + test_field.textctrl()->ProcessWindowEvent(ev); + wxMilliSleep(250); + wxYield(); + THEN( "on_kill_focus is executed and on_change are both executed.") { + REQUIRE(exec_counter == 2); + } + THEN( "get_string returns updated value.") { + REQUIRE(test_field.get_string() == "7"s); + } + THEN( "get_bool returns 0.") { + REQUIRE(test_field.get_bool() == 0); + } + THEN( "get_int returns 0.") { + REQUIRE(test_field.get_int() == 0); + } + } + + + } +} + +SCENARIO( "Multiline doesn't update other than on focus change.") { + wxTestableFrame* old = dynamic_cast(wxTheApp->GetTopWindow()); + old->Destroy(); + wxTheApp->SetTopWindow(new wxTestableFrame()); + wxUIActionSimulator sim; + wxMilliSleep(500); + + Slic3r::ConfigOptionDef simple_option; + simple_option.type = coString; + simple_option.multiline = true; + auto* stropt { new Slic3r::ConfigOptionString("7") }; + GIVEN ( "A multiline UI Textctrl") { + auto exec_counter {0}; + auto changefunc {[&exec_counter] (const std::string& opt_id, std::string value) { exec_counter++; }}; + auto test_field {Slic3r::GUI::UI_TextCtrl(wxTheApp->GetTopWindow(), Slic3r::ConfigOptionDef())}; + + test_field.on_change = changefunc; + + wxTheApp->GetTopWindow()->Show(); + wxTheApp->GetTopWindow()->Fit(); + WHEN( "text is entered and focus is lost") { + auto killfunc {[&exec_counter](const std::string& opt_id) { exec_counter += 1; }}; + test_field.on_kill_focus = killfunc; + + auto ev {wxFocusEvent(wxEVT_KILL_FOCUS, test_field.textctrl()->GetId())}; + ev.SetEventObject(test_field.textctrl()); + + exec_counter = 0; + test_field.textctrl()->SetFocus(); + wxYield(); + wxMilliSleep(250); + sim.Char(WXK_LEFT); + sim.Char('7'); + wxYield(); + sim.Char('7'); + wxYield(); + wxMilliSleep(250); + test_field.textctrl()->ProcessWindowEvent(ev); + wxYield(); + wxMilliSleep(250); + wxYield(); + THEN( "on_kill_focus is executed and on_change are both executed.") { + REQUIRE(exec_counter == 2); + } + THEN( "get_string returns updated value.") { + REQUIRE(test_field.get_string() == "77"s); + } + THEN( "get_bool returns 0.") { + REQUIRE(test_field.get_bool() == 0); + } + THEN( "get_int returns 0.") { + REQUIRE(test_field.get_int() == 0); + } + } + } +} diff --git a/src/test/GUI/test_harness_gui.cpp b/src/test/GUI/test_harness_gui.cpp new file mode 100644 index 000000000..3ff5ef555 --- /dev/null +++ b/src/test/GUI/test_harness_gui.cpp @@ -0,0 +1,239 @@ +#define CATCH_CONFIG_RUNNER +#include +#include "wx/wx.h" + +#include "wx/apptrait.h" +#include "testableframe.h" + +#undef wxUSE_GUI +#define wxUSE_GUI 1 +#undef wxUSE_LOG +#define wxUSE_LOG 0 + +typedef int (*FilterEventFunc)(wxEvent&); +typedef bool (*ProcessEventFunc)(wxEvent&); + +typedef wxApp TestAppBase; +typedef wxGUIAppTraits TestAppTraitsBase; +using namespace std; + +// The application class +// +class TestApp : public TestAppBase +{ +public: + TestApp(); + + // standard overrides + virtual bool OnInit(); + virtual int OnExit(); + +#ifdef __WIN32__ + virtual wxAppTraits *CreateTraits() + { + // Define a new class just to customize CanUseStderr() behaviour. + class TestAppTraits : public TestAppTraitsBase + { + public: + // We want to always use stderr, tests are also run unattended and + // in this case we really don't want to show any message boxes, as + // wxMessageOutputBest, used e.g. from the default implementation + // of wxApp::OnUnhandledException(), would do by default. + virtual bool CanUseStderr() { return true; } + + // Overriding CanUseStderr() is not enough, we also need to + // override this one to avoid returning false from it. + virtual bool WriteToStderr(const wxString& text) + { + wxFputs(text, stderr); + fflush(stderr); + + // Intentionally ignore any errors, we really don't want to + // show any message boxes in any case. + return true; + } + }; + + return new TestAppTraits; + } +#endif // __WIN32__ + + // Also override this method to avoid showing any dialogs from here -- and + // show some details about the exception along the way. + virtual bool OnExceptionInMainLoop() + { + wxFprintf(stderr, "Unhandled exception in the main loop: %s\n", + Catch::translateActiveException()); + + throw; + } + + // used by events propagation test + virtual int FilterEvent(wxEvent& event); + virtual bool ProcessEvent(wxEvent& event); + + void SetFilterEventFunc(FilterEventFunc f) { m_filterEventFunc = f; } + void SetProcessEventFunc(ProcessEventFunc f) { m_processEventFunc = f; } + + // In console applications we run the tests directly from the overridden + // OnRun(), but in the GUI ones we run them when we get the first call to + // our EVT_IDLE handler to ensure that we do everything from inside the + // main event loop. This is especially important under wxOSX/Cocoa where + // the main event loop is different from the others but it's also safer to + // do it like this in the other ports as we test the GUI code in the same + // context as it's used usually, in normal programs, and it might behave + // differently without the event loop. +#if wxUSE_GUI + void OnIdle(wxIdleEvent& event) + { + if ( m_runTests ) + { + m_runTests = false; + +#ifdef __WXOSX__ + // we need to wait until the window is activated and fully ready + // otherwise no events can be posted + wxEventLoopBase* const loop = wxEventLoop::GetActive(); + if ( loop ) + { + loop->DispatchTimeout(1000); + loop->Yield(); + } +#endif // __WXOSX__ + + m_exitcode = RunTests(); + ExitMainLoop(); + } + + event.Skip(); + } + + virtual int OnRun() + { + if ( TestAppBase::OnRun() != 0 ) + m_exitcode = EXIT_FAILURE; + + return m_exitcode; + } +#else // !wxUSE_GUI + virtual int OnRun() + { + return RunTests(); + } +#endif // wxUSE_GUI/!wxUSE_GUI + +private: + int RunTests(); + + // flag telling us whether we should run tests from our EVT_IDLE handler + bool m_runTests; + + // event handling hooks + FilterEventFunc m_filterEventFunc; + ProcessEventFunc m_processEventFunc; + +#if wxUSE_GUI + // the program exit code + int m_exitcode; +#endif // wxUSE_GUI +}; + +wxIMPLEMENT_APP_NO_MAIN(TestApp); + +// ---------------------------------------------------------------------------- +// TestApp +// ---------------------------------------------------------------------------- + +TestApp::TestApp() +{ + m_runTests = true; + + m_filterEventFunc = NULL; + m_processEventFunc = NULL; + +#if wxUSE_GUI + m_exitcode = EXIT_SUCCESS; +#endif // wxUSE_GUI +} + +// Init +// +bool TestApp::OnInit() +{ + // Hack: don't call TestAppBase::OnInit() to let CATCH handle command line. + + // Output some important information about the test environment. +#if wxUSE_GUI + cout << "Test program for wxWidgets GUI features\n" +#else + cout << "Test program for wxWidgets non-GUI features\n" +#endif + << "build: " << WX_BUILD_OPTIONS_SIGNATURE << "\n" + << "running under " << wxGetOsDescription() + << " as " << wxGetUserId() + << ", locale is " << setlocale(LC_ALL, NULL) + << std::endl; + +#if wxUSE_GUI + // create a parent window to be used as parent for the GUI controls + new wxTestableFrame(); + + Connect(wxEVT_IDLE, wxIdleEventHandler(TestApp::OnIdle)); + +#ifdef GDK_WINDOWING_X11 + XSetErrorHandler(wxTestX11ErrorHandler); +#endif // GDK_WINDOWING_X11 + +#endif // wxUSE_GUI + + return true; +} + +// Event handling +int TestApp::FilterEvent(wxEvent& event) +{ + if ( m_filterEventFunc ) + return (*m_filterEventFunc)(event); + + return TestAppBase::FilterEvent(event); +} + +bool TestApp::ProcessEvent(wxEvent& event) +{ + if ( m_processEventFunc ) + return (*m_processEventFunc)(event); + + return TestAppBase::ProcessEvent(event); +} + +// Run +// +int TestApp::RunTests() +{ +#if wxUSE_LOG + // Switch off logging unless --verbose + bool verbose = wxLog::GetVerbose(); + wxLog::EnableLogging(verbose); +#else + bool verbose = false; +#endif + + // Cast is needed under MSW where Catch also provides an overload taking + // wchar_t, but as it simply converts arguments to char internally anyhow, + // we can just as well always use the char version. + return Catch::Session().run(argc, static_cast(argv)); +} + +int TestApp::OnExit() +{ +#if wxUSE_GUI + delete GetTopWindow(); +#endif // wxUSE_GUI + + return TestAppBase::OnExit(); +} + +int main(int argc, char **argv) +{ + return wxEntry(argc, argv); +} diff --git a/src/test/GUI/test_misc_ui.cpp b/src/test/GUI/test_misc_ui.cpp new file mode 100644 index 000000000..46a1da0f9 --- /dev/null +++ b/src/test/GUI/test_misc_ui.cpp @@ -0,0 +1,27 @@ +#include +#include "misc_ui.hpp" +#include + +using namespace std::string_literals; +using namespace Slic3r::GUI; + +SCENARIO( "trim_zeroes leaves parsable numbers.") { + auto n192 {"19.200000000"s}; + auto n00 {"0.0"s}; + auto n0 {"0."s}; + REQUIRE(trim_zeroes(n00) == "0.0"s); + REQUIRE(trim_zeroes(n0) == "0.0"s); + REQUIRE(trim_zeroes(n192) == "19.2"s); +} + +SCENARIO ( "trim_zeroes doesn't reduce precision.") { + GIVEN( "A number with a long string of zeroes and a 0") { + auto n12 {"19.200000002"s}; + auto n120 {"19.2000000020"s}; + auto n1200 {"19.20000000200"s}; + + REQUIRE(trim_zeroes(n12) == "19.200000002"s); + REQUIRE(trim_zeroes(n120) == "19.200000002"s); + REQUIRE(trim_zeroes(n1200) == "19.200000002"s); + } +} diff --git a/src/test/GUI/test_optionsgroup.cpp b/src/test/GUI/test_optionsgroup.cpp new file mode 100644 index 000000000..3b782e2e8 --- /dev/null +++ b/src/test/GUI/test_optionsgroup.cpp @@ -0,0 +1,29 @@ +#include +#ifndef WX_PRECOMP + #include "wx/app.h" + #include "wx/sizer.h" + #include "wx/uiaction.h" +#endif // WX_PRECOMP +#include + +#include "OptionsGroup.hpp" +#include "ConfigBase.hpp" +#include "Config.hpp" + +using namespace Slic3r::GUI; + +SCENARIO("OptionsGroup: Construction.") { +} +SCENARIO("ConfigOptionsGroup: Factory methods") { + GIVEN("A default Slic3r config") { + OptionsGroup optgroup; + WHEN("add_single_option is called for avoid_crossing_perimeters") { + THEN("a UI_Checkbox is added to the field map") { + REQUIRE(optgroup.get_field("avoid_crossing_perimeters") != nullptr); + } + THEN("a ConfigOptionBool is added to the option map") { + REQUIRE(optgroup.get_field("avoid_crossing_perimeters") != nullptr); + } + } + } +} diff --git a/src/test/GUI/test_preset.cpp b/src/test/GUI/test_preset.cpp new file mode 100644 index 000000000..5fa8b1321 --- /dev/null +++ b/src/test/GUI/test_preset.cpp @@ -0,0 +1,195 @@ +#include +#include "Preset.hpp" +#include "Config.hpp" +#include + +using namespace Slic3r::GUI; +using namespace std::literals::string_literals; + +SCENARIO( "Preset construction" ) { + GIVEN("A preset file with at least one configuration") { + WHEN( "Preset is constructed." ) { + Preset item {std::string(testfile_dir) + "test_preset", "preset_load_numeric.ini"s, preset_t::Print}; + THEN("Name is preset_load_numeric") { + REQUIRE(item.name == "preset_load_numeric"s); + } + THEN("group is Print") { + REQUIRE(item.group == preset_t::Print); + } + THEN("preset default is false.") { + REQUIRE(item.default_preset == false); + } + } + } + GIVEN("A default preset construction for Print") { + WHEN( "Preset is constructed." ) { + Preset item {true, "- default -", preset_t::Print}; + THEN("Name is - default -") { + REQUIRE(item.name == "- default -"s); + } + THEN("group is Print") { + REQUIRE(item.group == preset_t::Print); + } + THEN("preset default is true.") { + REQUIRE(item.default_preset == true); + } + THEN("file_exists() is false") { + REQUIRE(item.file_exists() == false); + } + THEN("Does not contain filament_colour (a material option)") { + if (auto config = item.config().lock()) { + REQUIRE(!config->has("filament_colour")); + } + } + THEN("Does not contain gcode_flavor (a printer option)") { + if (auto config = item.config().lock()) { + REQUIRE(!config->has("gcode_flavor")); + } + } + THEN("Does contain layer_height (a print option)") { + if (auto config = item.config().lock()) { + REQUIRE(config->has("layer_height")); + } + } + } + } + GIVEN("A default preset construction for Material") { + WHEN( "Preset is constructed." ) { + Preset item {true, "- default -", preset_t::Material}; + THEN("Name is - default -") { + REQUIRE(item.name == "- default -"s); + } + THEN("group is Material") { + REQUIRE(item.group == preset_t::Material); + } + THEN("preset default is true.") { + REQUIRE(item.default_preset == true); + } + THEN("file_exists() is false") { + REQUIRE(item.file_exists() == false); + } + THEN("Does contain filament_colour (a material option)") { + if (auto config = item.config().lock()) { + REQUIRE(config->has("filament_colour")); + } + } + THEN("Does not contain gcode_flavor (a printer option)") { + if (auto config = item.config().lock()) { + REQUIRE(!config->has("gcode_flavor")); + } + } + THEN("Does not contain layer_height (a print option)") { + if (auto config = item.config().lock()) { + REQUIRE(!config->has("layer_height")); + } + } + } + } + GIVEN("A default preset construction for Printer") { + WHEN( "Preset is constructed." ) { + Preset item {true, "- default -", preset_t::Printer}; + THEN("Name is - default -") { + REQUIRE(item.name == "- default -"s); + } + THEN("group is Printer") { + REQUIRE(item.group == preset_t::Printer); + } + THEN("preset default is true.") { + REQUIRE(item.default_preset == true); + } + THEN("file_exists() is false") { + REQUIRE(item.file_exists() == false); + } + THEN("Does not contain filament_colour (a material option)") { + if (auto config = item.config().lock()) { + REQUIRE(!config->has("filament_colour")); + } + } + THEN("Does contain gcode_flavor (a printer option)") { + if (auto config = item.config().lock()) { + REQUIRE(config->has("gcode_flavor")); + } + } + THEN("Does not contain layer_height (a print option)") { + if (auto config = item.config().lock()) { + REQUIRE(!config->has("layer_height")); + } + } + } + } +} +SCENARIO( "Preset loading" ) { + GIVEN("A preset file with a config item that has adaptive_slicing = 1") { + Preset item {std::string(testfile_dir) + "test_preset", "preset_load_numeric.ini"s, preset_t::Print}; + THEN("file_exists() returns true") { + REQUIRE(item.file_exists() == true); + } + config_ptr ref; + WHEN("The preset file with one item is loaded") { + ref = item.load_config(); + THEN("Config is not dirty.") { + REQUIRE(item.dirty() == false); + } + THEN("adaptive_slicing = 1 in the preset config") { + REQUIRE(item.dirty_config().get("adaptive_slicing").getBool() == true); + } + } + auto config { Slic3r::Config::new_from_defaults() }; + ref = item.load_config(); + WHEN("Option is changed in the config via loading a config") { + // precondition: adaptive_slicing is still true + REQUIRE(item.dirty_config().get("adaptive_slicing").getBool() == true); + // precondition: default option for adaptice_slicing is false + REQUIRE(config->get("adaptive_slicing").getBool() == false); + ref->apply(config); + THEN("Config is dirty.") { + REQUIRE(item.dirty() == true); + } + THEN("adaptive_slicing = 0 in the preset config") { + REQUIRE(item.dirty_config().get("adaptive_slicing").getBool() == false); + } + THEN("subsequent calls yield the same reference") { + REQUIRE(item.load_config() == ref); + } + } + } + GIVEN("A preset file with a config item that has adaptive_slicing = 1 and default_preset = true") { + Preset item {std::string(testfile_dir) + "test_preset", "preset_load_numeric.ini"s, preset_t::Print}; + item.default_preset = true; + config_ptr ref; + WHEN("The preset file with one item is loaded") { + ref = item.load_config(); + THEN("Config is not dirty.") { + REQUIRE(item.dirty() == false); + } + THEN("adaptive_slicing = 1 in the preset config") { + REQUIRE(item.dirty_config().get("adaptive_slicing").getBool() == true); + } + THEN("loaded is true") { + REQUIRE(item.loaded() == true); + } + THEN("subsequent calls yield the same reference") { + REQUIRE(item.load_config() == ref); + } + } + WHEN("Option is changed in the config") { + ref = item.load_config(); + ref->set("adaptive_slicing", false); + THEN("Config is dirty.") { + REQUIRE(item.dirty() == true); + } + THEN("adaptive_slicing = 0 in the preset config") { + REQUIRE(item.dirty_config().get("adaptive_slicing").getBool() == false); + } + } + } + GIVEN("An invalid preset file") { + Preset item {std::string(testfile_dir) + "test_preset", "___invalid__preset_load_numeric.ini"s, preset_t::Print}; + THEN("file_exists() returns false") { + REQUIRE(item.file_exists() == false); + } + THEN("loaded is false") { + REQUIRE(item.loaded() == false); + } + } +} diff --git a/src/test/GUI/test_preset_chooser.cpp b/src/test/GUI/test_preset_chooser.cpp new file mode 100644 index 000000000..769d946ba --- /dev/null +++ b/src/test/GUI/test_preset_chooser.cpp @@ -0,0 +1,309 @@ +#include +#ifndef WX_PRECOMP + #include "wx/app.h" + #include "wx/sizer.h" + #include "wx/uiaction.h" +#endif // WX_PRECOMP +#include +#include "testableframe.h" + +#include "Log.hpp" +#include "Print.hpp" + +#include "Preset.hpp" +#include "Plater/PresetChooser.hpp" +#include "Config.hpp" +#include + +using namespace Slic3r::GUI; +using namespace std::literals::string_literals; + + +std::array defaults() { + std::array default_presets; + default_presets[get_preset(preset_t::Print)].push_back(Preset(true, "- default -", preset_t::Print)); + default_presets[get_preset(preset_t::Material)].push_back(Preset(true, "- default -", preset_t::Material)); + default_presets[get_preset(preset_t::Printer)].push_back(Preset(true, "- default -", preset_t::Printer)); + return default_presets; +} + +std::array sample() { + std::array preset_list; + preset_list[get_preset(preset_t::Print)].push_back(Preset(testfile_dir + "test_preset_chooser"s, "print-profile.ini", preset_t::Print)); + preset_list[get_preset(preset_t::Print)].push_back(Preset(true, "- default -", preset_t::Print)); + + preset_list[get_preset(preset_t::Material)].push_back(Preset(testfile_dir + "test_preset_chooser"s, "material-profile.ini", preset_t::Material)); + preset_list[get_preset(preset_t::Material)].push_back(Preset(true, "- default -", preset_t::Material)); + + preset_list[get_preset(preset_t::Printer)].push_back(Preset(true, "- default -", preset_t::Printer)); + preset_list[get_preset(preset_t::Printer)].push_back(Preset(testfile_dir + "test_preset_chooser"s, "printer-profile.ini", preset_t::Printer)); + + return preset_list; +} + +std::array default_compatible_reversion() { + std::array preset_list; + preset_list[get_preset(preset_t::Print)].push_back(Preset(testfile_dir + "test_preset_chooser"s, "print-profile.ini", preset_t::Print)); + preset_list[get_preset(preset_t::Print)].push_back(Preset(true, "- default -", preset_t::Print)); + + // set the material to be compatible to printer-profile-2 only + preset_list[get_preset(preset_t::Material)].push_back(Preset(testfile_dir + "test_preset_chooser"s, "incompat-material-profile.ini", preset_t::Material)); + preset_list[get_preset(preset_t::Material)].push_back(Preset(true, "- default -", preset_t::Material)); + + preset_list[get_preset(preset_t::Printer)].push_back(Preset(true, "- default -", preset_t::Printer)); + preset_list[get_preset(preset_t::Printer)].push_back(Preset(testfile_dir + "test_preset_chooser"s, "printer-profile-2.ini", preset_t::Printer)); + preset_list[get_preset(preset_t::Printer)].push_back(Preset(testfile_dir + "test_preset_chooser"s, "printer-profile.ini", preset_t::Printer)); + + return preset_list; +} + +std::array compatible_reversion() { + std::array preset_list; + preset_list[get_preset(preset_t::Print)].push_back(Preset(testfile_dir + "test_preset_chooser"s, "print-profile.ini", preset_t::Print)); + preset_list[get_preset(preset_t::Print)].push_back(Preset(true, "- default -", preset_t::Print)); + + // set the material to be compatible to printer-profile-2 only + preset_list[get_preset(preset_t::Material)].push_back(Preset(testfile_dir + "test_preset_chooser"s, "incompat-material-profile.ini", preset_t::Material)); + preset_list[get_preset(preset_t::Material)].push_back(Preset(testfile_dir + "test_preset_chooser"s, "material-profile.ini", preset_t::Material)); + preset_list[get_preset(preset_t::Material)].push_back(Preset(testfile_dir + "test_preset_chooser"s, "other-material-profile.ini", preset_t::Material)); + preset_list[get_preset(preset_t::Material)].push_back(Preset(true, "- default -", preset_t::Material)); + + preset_list[get_preset(preset_t::Printer)].push_back(Preset(true, "- default -", preset_t::Printer)); + preset_list[get_preset(preset_t::Printer)].push_back(Preset(testfile_dir + "test_preset_chooser"s, "printer-profile-2.ini", preset_t::Printer)); + preset_list[get_preset(preset_t::Printer)].push_back(Preset(testfile_dir + "test_preset_chooser"s, "printer-profile.ini", preset_t::Printer)); + + return preset_list; +} + +// Tests to cover behavior when the printer profile is changed. +// System should update its selected choosers based on changed print profile, +// update settings, etc. +SCENARIO( "PresetChooser changed printer") { + std::shared_ptr fake_print {std::make_shared()}; + Settings default_settings; + wxUIActionSimulator sim; + wxTestableFrame* old = dynamic_cast(wxTheApp->GetTopWindow()); + old->Destroy(); + wxTheApp->SetTopWindow(new wxTestableFrame()); + + GIVEN( "A PresetChooser with printer-profile selected." ) { + Settings test_settings; + test_settings.default_presets.at(get_preset(preset_t::Printer)).push_back(wxString("printer-profile")); + auto preset_list {default_compatible_reversion()}; + PresetChooser cut(wxTheApp->GetTopWindow(), fake_print, &test_settings, preset_list); + cut.load(); + + WHEN( "Printer profile is changed to printer-profile-2 via select_preset_by_name" ) { + cut.select_preset_by_name("printer-profile-2", preset_t::Printer, 0); + THEN( "Selected printer profile entry is \"printer-profile-2\"" ) { + for (auto* chooser : cut.preset_choosers[get_preset(preset_t::Printer)]) { + REQUIRE(chooser->GetString(chooser->GetSelection()) == wxString("printer-profile-2")); + } + } + THEN( "Print profile chooser has 1 entry" ) { + for (auto* chooser : cut.preset_choosers[get_preset(preset_t::Print)]) { + REQUIRE(chooser->GetCount() == 1); + } + } + THEN( "Selected print profile entry is \"print-profile\"" ) { + for (auto* chooser : cut.preset_choosers[get_preset(preset_t::Print)]) { + REQUIRE(chooser->GetString(chooser->GetSelection()) == wxString("print-profile")); + } + } + THEN( "Material profile chooser has one entry" ) { + for (auto* chooser : cut.preset_choosers[get_preset(preset_t::Material)]) { + REQUIRE(chooser->GetCount() == 1); + } + } + THEN( "Selected material profile entry is \"material-profile\"" ) { + for (auto* chooser : cut.preset_choosers[get_preset(preset_t::Material)]) { + REQUIRE(chooser->GetString(chooser->GetSelection()) == wxString("incompat-material-profile")); + } + } + } + WHEN( "Printer profile is changed to printer-profile-2 via combobox event" ) { + auto* printer_chooser = cut.preset_choosers[get_preset(preset_t::Printer)].at(0); + printer_chooser->SetSelection(0); + + auto ev {wxCommandEvent(wxEVT_COMBOBOX, printer_chooser->GetId())}; + ev.SetEventObject(printer_chooser); + printer_chooser->ProcessWindowEvent(ev); + wxYield(); + wxMilliSleep(150); + + THEN( "Selected printer profile entry is \"printer-profile-2\"" ) { + for (auto* chooser : cut.preset_choosers[get_preset(preset_t::Printer)]) { + REQUIRE(chooser->GetString(chooser->GetSelection()) == wxString("printer-profile-2")); + } + } + THEN( "Print profile chooser has 1 entry" ) { + for (auto* chooser : cut.preset_choosers[get_preset(preset_t::Print)]) { + REQUIRE(chooser->GetCount() == 1); + } + } + THEN( "Selected print profile entry is \"print-profile\"" ) { + for (auto* chooser : cut.preset_choosers[get_preset(preset_t::Print)]) { + REQUIRE(chooser->GetString(chooser->GetSelection()) == wxString("print-profile")); + } + } + THEN( "Material profile chooser has one entry" ) { + for (auto* chooser : cut.preset_choosers[get_preset(preset_t::Material)]) { + REQUIRE(chooser->GetCount() == 1); + } + } + THEN( "Selected material profile entry is \"material-profile\"" ) { + for (auto* chooser : cut.preset_choosers[get_preset(preset_t::Material)]) { + REQUIRE(chooser->GetString(chooser->GetSelection()) == wxString("incompat-material-profile")); + } + } + } + } + GIVEN( "A PresetChooser with printer-profile selected and 2+ non-default entries for material ." ) { + Settings test_settings; + test_settings.default_presets.at(get_preset(preset_t::Printer)).push_back(wxString("printer-profile")); + auto preset_list {compatible_reversion()}; + PresetChooser cut(wxTheApp->GetTopWindow(), fake_print, &test_settings, preset_list); + cut.load(); + WHEN( "Printer profile has only 2 compatible materials" ) { + THEN( "Material profile chooser has two entries" ) { + for (auto* chooser : cut.preset_choosers[get_preset(preset_t::Material)]) { + REQUIRE(chooser->GetCount() == 2); + } + } + THEN( "incompat-material-profile is not in the chooser" ) { + for (auto* chooser : cut.preset_choosers[get_preset(preset_t::Material)]) { + REQUIRE(chooser->FindString("incompat-material-profile") == wxNOT_FOUND); + } + } + } + + WHEN( "Printer profile is changed to printer-profile-2 via select_preset_by_name" ) { + cut.select_preset_by_name("printer-profile-2", preset_t::Printer, 0); + THEN( "Selected printer profile entry is \"printer-profile-2\"" ) { + for (auto* chooser : cut.preset_choosers[get_preset(preset_t::Printer)]) { + REQUIRE(chooser->GetString(chooser->GetSelection()) == wxString("printer-profile-2")); + } + } + THEN( "Print profile chooser has 1 entry" ) { + for (auto* chooser : cut.preset_choosers[get_preset(preset_t::Print)]) { + REQUIRE(chooser->GetCount() == 1); + } + } + THEN( "Selected print profile entry is \"print-profile\"" ) { + for (auto* chooser : cut.preset_choosers[get_preset(preset_t::Print)]) { + REQUIRE(chooser->GetString(chooser->GetSelection()) == wxString("print-profile")); + } + } + THEN( "Material profile chooser has one entry" ) { + for (auto* chooser : cut.preset_choosers[get_preset(preset_t::Material)]) { + REQUIRE(chooser->GetCount() == 3); + } + } + } + } +} + +SCENARIO( "PresetChooser Preset loading" ) { + std::shared_ptr fake_print {std::make_shared()}; + Settings default_settings; + auto& settings_presets = default_settings.default_presets; + wxUIActionSimulator sim; + wxTestableFrame* old = dynamic_cast(wxTheApp->GetTopWindow()); + old->Destroy(); + wxTheApp->SetTopWindow(new wxTestableFrame()); + + GIVEN( "A PresetChooser object." ) { + WHEN( "load() is called with only default presets" ) { + auto preset_list {defaults()}; + PresetChooser cut(wxTheApp->GetTopWindow(), fake_print, &default_settings, preset_list); + cut.load(); + THEN( "Number of preset choosers created is 3" ) { + REQUIRE(cut.preset_choosers.size() == 3); + } + THEN( "Each profile chooser has 1 entry" ) { + for (auto chooser_list : cut.preset_choosers) { + REQUIRE(chooser_list.size() == 1); + for (auto* chooser : chooser_list) { + REQUIRE(chooser->GetCount() == 1); + } + } + } + THEN( "Selection mapping table has 3 profile entries, all named \"- default - \"") { + for (const auto& group : { preset_t::Print, preset_t::Material, preset_t::Printer }) { + REQUIRE(cut._chooser_names()[get_preset(group)].at(0) == wxString("- default -")); + } + } + } + WHEN( "load is called with non-default presets and default presets" ) { + auto preset_list {sample()}; + PresetChooser cut(wxTheApp->GetTopWindow(), fake_print, &default_settings, preset_list); + cut.load(); + THEN( "Number of preset choosers created is 3" ) { + REQUIRE(cut.preset_choosers.size() == 3); + } + THEN( "Each profile chooser has 1 entry" ) { + for (auto chooser_list : cut.preset_choosers) { + REQUIRE(chooser_list.size() == 1); + for (auto* chooser : chooser_list) { + REQUIRE(chooser->GetCount() == 1); + } + } + } + THEN( "Selection mapping table has 3 profile entries, none named \"- default - \"") { + for (const auto& group : { preset_t::Print, preset_t::Material, preset_t::Printer }) { + REQUIRE(cut._chooser_names()[get_preset(group)].at(0) != wxString("- default -")); + } + } + THEN( "Settings are updated to match selected." ) { + REQUIRE(settings_presets[get_preset(preset_t::Print)].at(0) == wxString("print-profile")); + REQUIRE(settings_presets[get_preset(preset_t::Printer)].at(0) == wxString("printer-profile")); + REQUIRE(settings_presets[get_preset(preset_t::Material)].at(0) == wxString("material-profile")); + } + } + } + GIVEN( "A PresetChooser object and a Settings indicating that print-profile is the default option." ) { + Settings test_settings; + test_settings.default_presets.at(get_preset(preset_t::Printer)).push_back(wxString("printer-profile")); + auto preset_list {default_compatible_reversion()}; + PresetChooser cut(wxTheApp->GetTopWindow(), fake_print, &test_settings, preset_list); + WHEN( "load is called with non-default presets and default presets and the material is listed with an incompatible printer" ) { + cut.load(); + THEN( "Number of preset choosers created is 3" ) { + REQUIRE(cut.preset_choosers.size() == 3); + } + THEN( "Print profile chooser has 1 entry" ) { + for (auto* chooser : cut.preset_choosers[get_preset(preset_t::Print)]) { + REQUIRE(chooser->GetCount() == 1); + } + } + THEN( "Printer profile chooser has 2 entries" ) { + for (auto* chooser : cut.preset_choosers[get_preset(preset_t::Printer)]) { + REQUIRE(chooser->GetCount() == 2); + } + } + THEN( "Material profile chooser has one entry" ) { + for (auto* chooser : cut.preset_choosers[get_preset(preset_t::Material)]) { + REQUIRE(chooser->GetCount() == 1); + } + } + + THEN( "Selected printer profile entry is \"printer-profile\"" ) { + for (auto* chooser : cut.preset_choosers[get_preset(preset_t::Printer)]) { + REQUIRE(chooser->GetString(chooser->GetSelection()) == wxString("printer-profile")); + } + } + THEN( "Print profile entry has one entry named \"print-profile\"" ) { + REQUIRE(cut._chooser_names()[get_preset(preset_t::Print)].at(0) == wxString("print-profile")); + } + THEN( "Printer profile entry has an entry named \"printer-profile\"" ) { + REQUIRE(cut._chooser_names()[get_preset(preset_t::Printer)].at(1) == wxString("printer-profile")); + } + THEN( "Printer profile entry has an entry named \"printer-profile\"" ) { + REQUIRE(cut._chooser_names()[get_preset(preset_t::Printer)].at(0) == wxString("printer-profile-2")); + } + THEN( "Material profile entry has one entry named \"- default -\"" ) { + REQUIRE(cut._chooser_names()[get_preset(preset_t::Material)].at(0) == wxString("- default -")); + } + } + } +} diff --git a/src/test/GUI/testableframe.cpp b/src/test/GUI/testableframe.cpp new file mode 100644 index 000000000..fae750124 --- /dev/null +++ b/src/test/GUI/testableframe.cpp @@ -0,0 +1,61 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: testableframe.cpp +// Purpose: An improved wxFrame for unit-testing +// Author: Steven Lamerton +// Copyright: (c) 2010 Steven Lamerton +// Licence: wxWindows licence +/////////////////////////////////////////////////////////////////////////////// +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +#include "wx/app.h" +#include "testableframe.h" + +wxTestableFrame::wxTestableFrame() : wxFrame(NULL, wxID_ANY, "Test Frame") +{ + // Use fixed position to facilitate debugging. + Move(200, 200); + + Show(); +} + +void wxTestableFrame::OnEvent(wxEvent& evt) +{ + m_count[evt.GetEventType()]++; + + if(! evt.IsCommandEvent() ) + evt.Skip(); +} + +int wxTestableFrame::GetEventCount(wxEventType type) +{ + return m_count[type]; +} + +void wxTestableFrame::ClearEventCount(wxEventType type) +{ + m_count[type] = 0; +} + +EventCounter::EventCounter(wxWindow* win, wxEventType type) : m_type(type), + m_win(win) + +{ + m_frame = wxStaticCast(wxTheApp->GetTopWindow(), wxTestableFrame); + + m_win->Connect(m_type, wxEventHandler(wxTestableFrame::OnEvent), + NULL, m_frame); +} + +EventCounter::~EventCounter() +{ + m_win->Disconnect(m_type, wxEventHandler(wxTestableFrame::OnEvent), + NULL, m_frame); + + //This stops spurious counts from previous tests + Clear(); + + m_frame = NULL; + m_win = NULL; +} diff --git a/src/test/GUI/testableframe.h b/src/test/GUI/testableframe.h new file mode 100644 index 000000000..a0465d829 --- /dev/null +++ b/src/test/GUI/testableframe.h @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: testableframe.h +// Purpose: An improved wxFrame for unit-testing +// Author: Steven Lamerton +// Copyright: (c) 2010 Steven Lamerton +// Licence: wxWindows licence +/////////////////////////////////////////////////////////////////////////////// + +#include "wx/frame.h" +#include "wx/hashmap.h" +#include "wx/event.h" + +class wxTestableFrame : public wxFrame +{ +public: + wxTestableFrame(); + + void OnEvent(wxEvent& evt); + +private: + friend class EventCounter; + + int GetEventCount(wxEventType type); + void ClearEventCount(wxEventType type); + + wxLongToLongHashMap m_count; +}; + +class EventCounter +{ +public: + EventCounter(wxWindow* win, wxEventType type); + ~EventCounter(); + + int GetCount() { return m_frame->GetEventCount(m_type); } + void Clear() { m_frame->ClearEventCount(m_type); } + +private: + wxEventType m_type; + wxTestableFrame* m_frame; + wxWindow* m_win; +}; diff --git a/src/test/inputs/20mmbox.stl b/src/test/inputs/20mmbox.stl new file mode 100644 index 000000000..9707a0de7 Binary files /dev/null and b/src/test/inputs/20mmbox.stl differ diff --git a/src/test/inputs/README.md b/src/test/inputs/README.md new file mode 100644 index 000000000..b8e893fd4 --- /dev/null +++ b/src/test/inputs/README.md @@ -0,0 +1,21 @@ +Directory containing test-specific input files that are not source code. + +--- + +Rules +=== +* Each folder shall be named for the test that it supports. + * `test_config` directory supports `test_config.cpp`, etc. +* If a specific test file is reused across multiple tests, it should go in the `common` directory. +* Each extra input file should be named in such a way that it is relevant to the specific feature of it. +* No files should have special characters in the name (unless those special characters are part of the test). +* No spaces should be in the file paths (again, unless testing the presence of spaces is part of the test). +* Input files that are 3D models should be as small/specific as possible. + * Do not add copyrighted models without permission from the copyright holder. + * Do not add NonCommercial models, these would need to be relicensed from the original holder. + * Add any necessary licensing or attributation information as `.license` + * Example: A CC-By-SA 3D model called `cool_statue_bro.stl` should have its attributation/license information included in a file called `cool_statue_bro.license` + * Any submitted files without an accompanying `.license` file are assumed to be licensed under [CC-By-SA 3.0](https://creativecommons.org/licenses/by-sa/3.0/us/). +* The author of a commit adding any extra input files asserts that, via the process of committing those files, that they + * have abided by the terms of all licensing agreements involved with the files added or + * are the author of the files in question and submit them to the Slic3r project under the terms of [CC-By-SA 3.0](https://creativecommons.org/licenses/by-sa/3.0/us/). diff --git a/src/test/inputs/test_cli/20mmbox.stl b/src/test/inputs/test_cli/20mmbox.stl new file mode 100644 index 000000000..c7386d4af --- /dev/null +++ b/src/test/inputs/test_cli/20mmbox.stl @@ -0,0 +1,86 @@ +solid Default + facet normal 0.000000e+00 0.000000e+00 -1.000000e+00 + outer loop + vertex 1.000000e+01 1.000000e+01 0.000000e+00 + vertex -1.000000e+01 -1.000000e+01 0.000000e+00 + vertex -1.000000e+01 1.000000e+01 0.000000e+00 + endloop + endfacet + facet normal 0.000000e+00 0.000000e+00 -1.000000e+00 + outer loop + vertex -1.000000e+01 -1.000000e+01 0.000000e+00 + vertex 1.000000e+01 1.000000e+01 0.000000e+00 + vertex 1.000000e+01 -1.000000e+01 0.000000e+00 + endloop + endfacet + facet normal 0.000000e+00 0.000000e+00 1.000000e+00 + outer loop + vertex 1.000000e+01 -1.000000e+01 1.000000e+01 + vertex -1.000000e+01 1.000000e+01 1.000000e+01 + vertex -1.000000e+01 -1.000000e+01 1.000000e+01 + endloop + endfacet + facet normal 0.000000e+00 0.000000e+00 1.000000e+00 + outer loop + vertex -1.000000e+01 1.000000e+01 1.000000e+01 + vertex 1.000000e+01 -1.000000e+01 1.000000e+01 + vertex 1.000000e+01 1.000000e+01 1.000000e+01 + endloop + endfacet + facet normal 1.000000e+00 0.000000e+00 0.000000e+00 + outer loop + vertex 1.000000e+01 1.000000e+01 0.000000e+00 + vertex 1.000000e+01 -1.000000e+01 1.000000e+01 + vertex 1.000000e+01 -1.000000e+01 0.000000e+00 + endloop + endfacet + facet normal 1.000000e+00 0.000000e+00 0.000000e+00 + outer loop + vertex 1.000000e+01 -1.000000e+01 1.000000e+01 + vertex 1.000000e+01 1.000000e+01 0.000000e+00 + vertex 1.000000e+01 1.000000e+01 1.000000e+01 + endloop + endfacet + facet normal -0.000000e+00 -1.000000e+00 -0.000000e+00 + outer loop + vertex 1.000000e+01 -1.000000e+01 1.000000e+01 + vertex -1.000000e+01 -1.000000e+01 0.000000e+00 + vertex 1.000000e+01 -1.000000e+01 0.000000e+00 + endloop + endfacet + facet normal -0.000000e+00 -1.000000e+00 -0.000000e+00 + outer loop + vertex -1.000000e+01 -1.000000e+01 0.000000e+00 + vertex 1.000000e+01 -1.000000e+01 1.000000e+01 + vertex -1.000000e+01 -1.000000e+01 1.000000e+01 + endloop + endfacet + facet normal -1.000000e+00 0.000000e+00 0.000000e+00 + outer loop + vertex -1.000000e+01 1.000000e+01 1.000000e+01 + vertex -1.000000e+01 -1.000000e+01 0.000000e+00 + vertex -1.000000e+01 -1.000000e+01 1.000000e+01 + endloop + endfacet + facet normal -1.000000e+00 0.000000e+00 0.000000e+00 + outer loop + vertex -1.000000e+01 -1.000000e+01 0.000000e+00 + vertex -1.000000e+01 1.000000e+01 1.000000e+01 + vertex -1.000000e+01 1.000000e+01 0.000000e+00 + endloop + endfacet + facet normal 0.000000e+00 1.000000e+00 0.000000e+00 + outer loop + vertex -1.000000e+01 1.000000e+01 1.000000e+01 + vertex 1.000000e+01 1.000000e+01 0.000000e+00 + vertex -1.000000e+01 1.000000e+01 0.000000e+00 + endloop + endfacet + facet normal 0.000000e+00 1.000000e+00 0.000000e+00 + outer loop + vertex 1.000000e+01 1.000000e+01 0.000000e+00 + vertex -1.000000e+01 1.000000e+01 1.000000e+01 + vertex 1.000000e+01 1.000000e+01 1.000000e+01 + endloop + endfacet +endsolid Default diff --git a/src/test/inputs/test_cli/20mmbox_config.ini b/src/test/inputs/test_cli/20mmbox_config.ini new file mode 100644 index 000000000..dcaccfce4 --- /dev/null +++ b/src/test/inputs/test_cli/20mmbox_config.ini @@ -0,0 +1,40 @@ +# generated by Slic3r 1.3.1-dev on 2018-11-24 17:57:55 +bottom_solid_layers = 0 +brim_width = 0 +complete_objects = 0 +external_perimeter_extrusion_width = 0.2 +external_perimeters_first = 0 +extra_perimeters = 1 +extruder_offset = 0x0 +extrusion_axis = E +extrusion_width = 0 +fill_density = 0% +fill_gaps = 1 +first_layer_extrusion_width = 0.2 +first_layer_height = 0.3 +infill_extrusion_width = 0 +interface_shells = 0 +interior_brim_width = 0 +layer_height = 0.3 +min_shell_thickness = 0 +only_retract_when_crossing_perimeters = 1 +ooze_prevention = 0 +perimeter_extrusion_width = 0.2 +perimeters = 1 +skirts = 0 +support_material = 0 +support_material_extrusion_width = 0 +temperature = 206 +thin_walls = 1 +threads = 16 +top_infill_extrusion_width = 0 +top_infill_pattern = rectilinear +top_solid_infill_speed = 15 +solid_infill_speed = 15 +infill_speed = 15 +top_solid_layers = 0 +use_firmware_retraction = 1 +use_volumetric_e = 1 +wipe = 0 +xy_size_compensation = 0 +z_offset = 0 diff --git a/src/test/inputs/test_config/config_bundle_test.ini b/src/test/inputs/test_config/config_bundle_test.ini new file mode 100644 index 000000000..b5a7b1296 --- /dev/null +++ b/src/test/inputs/test_config/config_bundle_test.ini @@ -0,0 +1,252 @@ +# generated by Slic3r 1.3.0 on Mon Jul 16 14:02:26 2018 + +[presets] +filament = - default - +print = Untitled +print_1 = - default - +printer = Untitled + +[print:- default -] +adaptive_slicing = 0 +adaptive_slicing_quality = 75% +avoid_crossing_perimeters = 0 +bottom_infill_pattern = rectilinear +bottom_solid_layers = 3 +bridge_acceleration = 0 +bridge_flow_ratio = 1 +bridge_speed = 60 +brim_connections_width = 0 +brim_width = 0 +compatible_printers = +complete_objects = 0 +default_acceleration = 0 +dont_support_bridges = 1 +external_perimeter_extrusion_width = 0 +external_perimeter_speed = 50% +external_perimeters_first = 0 +extra_perimeters = 1 +extruder_clearance_height = 20 +extruder_clearance_radius = 20 +extrusion_width = 0 +fill_angle = 45 +fill_density = 20% +fill_gaps = 1 +fill_pattern = stars +first_layer_acceleration = 0 +first_layer_extrusion_width = 200% +first_layer_height = 0.35 +first_layer_speed = 30 +gap_fill_speed = 20 +gcode_comments = 0 +infill_acceleration = 0 +infill_every_layers = 1 +infill_extruder = 1 +infill_extrusion_width = 0 +infill_first = 0 +infill_only_where_needed = 0 +infill_overlap = 55% +infill_speed = 80 +interface_shells = 0 +interior_brim_width = 0 +layer_height = 0.3 +match_horizontal_surfaces = 0 +max_print_speed = 80 +max_volumetric_speed = 0 +min_skirt_length = 0 +notes = +only_retract_when_crossing_perimeters = 1 +ooze_prevention = 0 +output_filename_format = [input_filename_base].gcode +overhangs = 1 +perimeter_acceleration = 0 +perimeter_extruder = 1 +perimeter_extrusion_width = 0 +perimeter_speed = 60 +perimeters = 3 +post_process = +print_settings_id = +raft_layers = 0 +regions_overlap = 0 +resolution = 0 +seam_position = aligned +shortcuts = support_material +skirt_distance = 6 +skirt_height = 1 +skirts = 1 +small_perimeter_speed = 15 +solid_infill_below_area = 70 +solid_infill_every_layers = 0 +solid_infill_extruder = 1 +solid_infill_extrusion_width = 0 +solid_infill_speed = 20 +spiral_vase = 0 +standby_temperature_delta = -5 +support_material = 1 +support_material_angle = 0 +support_material_buildplate_only = 0 +support_material_contact_distance = 0.2 +support_material_enforce_layers = 0 +support_material_extruder = 1 +support_material_extrusion_width = 0 +support_material_interface_extruder = 1 +support_material_interface_extrusion_width = 0 +support_material_interface_layers = 3 +support_material_interface_spacing = 0 +support_material_interface_speed = 100% +support_material_max_layers = 0 +support_material_pattern = pillars +support_material_spacing = 2.5 +support_material_speed = 60 +support_material_threshold = 60% +thin_walls = 1 +top_infill_extrusion_width = 0 +top_infill_pattern = rectilinear +top_solid_infill_speed = 15 +top_solid_layers = 3 +travel_speed = 130 +xy_size_compensation = 0 + +[print:Untitled] +adaptive_slicing = 0 +adaptive_slicing_quality = 75% +avoid_crossing_perimeters = 0 +bottom_infill_pattern = rectilinear +bottom_solid_layers = 3 +bridge_acceleration = 0 +bridge_flow_ratio = 1 +bridge_speed = 60 +brim_connections_width = 0 +brim_width = 0 +compatible_printers = +complete_objects = 0 +default_acceleration = 0 +dont_support_bridges = 1 +external_perimeter_extrusion_width = 0 +external_perimeter_speed = 50% +external_perimeters_first = 0 +extra_perimeters = 1 +extruder_clearance_height = 20 +extruder_clearance_radius = 20 +extrusion_width = 0 +fill_angle = 45 +fill_density = 20% +fill_gaps = 1 +fill_pattern = stars +first_layer_acceleration = 0 +first_layer_extrusion_width = 200% +first_layer_height = 0.35 +first_layer_speed = 30 +gap_fill_speed = 20 +gcode_comments = 1 +infill_acceleration = 0 +infill_every_layers = 1 +infill_extruder = 1 +infill_extrusion_width = 0 +infill_first = 0 +infill_only_where_needed = 0 +infill_overlap = 55% +infill_speed = 80 +interface_shells = 0 +interior_brim_width = 0 +layer_height = 0.3 +match_horizontal_surfaces = 0 +max_print_speed = 80 +max_volumetric_speed = 0 +min_skirt_length = 0 +notes = +only_retract_when_crossing_perimeters = 1 +ooze_prevention = 0 +output_filename_format = [input_filename_base].gcode +overhangs = 1 +perimeter_acceleration = 0 +perimeter_extruder = 1 +perimeter_extrusion_width = 0 +perimeter_speed = 60 +perimeters = 3 +post_process = +print_settings_id = +raft_layers = 0 +regions_overlap = 0 +resolution = 0 +seam_position = aligned +shortcuts = support_material +skirt_distance = 6 +skirt_height = 1 +skirts = 1 +small_perimeter_speed = 15 +solid_infill_below_area = 70 +solid_infill_every_layers = 0 +solid_infill_extruder = 1 +solid_infill_extrusion_width = 0 +solid_infill_speed = 20 +spiral_vase = 0 +standby_temperature_delta = -5 +support_material = 0 +support_material_angle = 0 +support_material_buildplate_only = 0 +support_material_contact_distance = 0.2 +support_material_enforce_layers = 0 +support_material_extruder = 1 +support_material_extrusion_width = 0 +support_material_interface_extruder = 1 +support_material_interface_extrusion_width = 0 +support_material_interface_layers = 3 +support_material_interface_spacing = 0 +support_material_interface_speed = 100% +support_material_max_layers = 0 +support_material_pattern = pillars +support_material_spacing = 2.5 +support_material_speed = 60 +support_material_threshold = 60% +thin_walls = 1 +top_infill_extrusion_width = 0 +top_infill_pattern = rectilinear +top_solid_infill_speed = 15 +top_solid_layers = 3 +travel_speed = 130 +xy_size_compensation = 0 + +[printer:Untitled] +bed_shape = 0x0,200x0,200x200,0x200 +before_layer_gcode = +between_objects_gcode = +end_gcode = M104 S0 ; turn off temperature\nG28 X0 ; home X axis\nM84 ; disable motors\n +extruder_offset = 0x0 +gcode_flavor = reprap +has_heatbed = 1 +host_type = octoprint +layer_gcode = ; LAYER START [layer_z] [layer_num] +max_layer_height = 0.3 +min_layer_height = 0.15 +nozzle_diameter = 0.5 +octoprint_apikey = dsdsdsd +pressure_advance = 0 +print_host = 127.0.0.1 +printer_notes = +printer_settings_id = +retract_before_travel = 2 +retract_layer_change = 1 +retract_length = 2 +retract_length_toolchange = 10 +retract_lift = 1 +retract_lift_above = 0 +retract_lift_below = 0 +retract_restart_extra = 0 +retract_restart_extra_toolchange = 0 +retract_speed = 40 +serial_port = +serial_speed = 250000 +start_gcode = G28 ; home all axes\nG1 Z5 F5000 ; lift nozzle\n +toolchange_gcode = +use_firmware_retraction = 0 +use_relative_e_distances = 0 +use_set_and_wait_bed = 0 +use_set_and_wait_extruder = 0 +use_volumetric_e = 0 +vibration_limit = 0 +wipe = 1 +z_offset = 0 +z_steps_per_mm = 0 + +[settings] +autocenter = 1 diff --git a/src/test/inputs/test_config/new_from_ini.ini b/src/test/inputs/test_config/new_from_ini.ini new file mode 100644 index 000000000..151a42358 --- /dev/null +++ b/src/test/inputs/test_config/new_from_ini.ini @@ -0,0 +1,167 @@ +# generated by Slic3r 1.3.0 on +adaptive_slicing = 0 +adaptive_slicing_quality = 75% +avoid_crossing_perimeters = 0 +bed_shape = 0x0,200x0,200x200,0x200 +bed_temperature = 0 +before_layer_gcode = +between_objects_gcode = +bottom_infill_pattern = rectilinear +bottom_solid_layers = 3 +bridge_acceleration = 0 +bridge_fan_speed = 100 +bridge_flow_ratio = 1 +bridge_speed = 60 +brim_connections_width = 0 +brim_width = 0 +compatible_printers = +complete_objects = 0 +cooling = 1 +default_acceleration = 0 +disable_fan_first_layers = 3 +dont_support_bridges = 1 +duplicate_distance = 6 +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 +external_perimeter_extrusion_width = 0 +external_perimeter_speed = 50% +external_perimeters_first = 0 +extra_perimeters = 1 +extruder_clearance_height = 20 +extruder_clearance_radius = 20 +extruder_offset = 0x0 +extrusion_axis = E +extrusion_multiplier = 1 +extrusion_width = 0 +fan_always_on = 0 +fan_below_layer_time = 60 +filament_colour = #FFFFFF +filament_cost = 0 +filament_density = 0 +filament_diameter = 3 +filament_max_volumetric_speed = 0 +filament_notes = "" +filament_settings_id = +fill_angle = 45 +fill_density = 20% +fill_gaps = 1 +fill_pattern = stars +first_layer_acceleration = 0 +first_layer_bed_temperature = 0 +first_layer_extrusion_width = 200% +first_layer_height = 0.35 +first_layer_speed = 30 +first_layer_temperature = 200 +gap_fill_speed = 20 +gcode_arcs = 0 +gcode_comments = 1 +gcode_flavor = reprap +has_heatbed = 1 +host_type = octoprint +infill_acceleration = 0 +infill_every_layers = 1 +infill_extruder = 1 +infill_extrusion_width = 0 +infill_first = 0 +infill_only_where_needed = 0 +infill_overlap = 55% +infill_speed = 80 +interface_shells = 0 +interior_brim_width = 0 +layer_gcode = ; LAYER START [layer_z] [layer_num] +layer_height = 0.3 +match_horizontal_surfaces = 0 +max_fan_speed = 100 +max_layer_height = 0.3 +max_print_speed = 80 +max_volumetric_speed = 0 +min_fan_speed = 35 +min_layer_height = 0.15 +min_print_speed = 10 +min_skirt_length = 0 +notes = +nozzle_diameter = 0.5 +octoprint_apikey = dsdsdsd +only_retract_when_crossing_perimeters = 1 +ooze_prevention = 0 +output_filename_format = [input_filename_base].gcode +overhangs = 1 +perimeter_acceleration = 0 +perimeter_extruder = 1 +perimeter_extrusion_width = 0 +perimeter_speed = 60 +perimeters = 3 +post_process = +pressure_advance = 0 +print_host = 127.0.0.1 +print_settings_id = +printer_notes = +printer_settings_id = +raft_layers = 0 +regions_overlap = 0 +resolution = 0 +retract_before_travel = 2 +retract_layer_change = 1 +retract_length = 2 +retract_length_toolchange = 10 +retract_lift = 1 +retract_lift_above = 0 +retract_lift_below = 0 +retract_restart_extra = 0 +retract_restart_extra_toolchange = 0 +retract_speed = 40 +seam_position = aligned +sequential_print_priority = 0 +serial_port = +serial_speed = 250000 +shortcuts = support_material +skirt_distance = 6 +skirt_height = 1 +skirts = 1 +slowdown_below_layer_time = 5 +small_perimeter_speed = 15 +solid_infill_below_area = 70 +solid_infill_every_layers = 0 +solid_infill_extruder = 1 +solid_infill_extrusion_width = 0 +solid_infill_speed = 20 +spiral_vase = 0 +standby_temperature_delta = -5 +start_filament_gcode = "; Filament gcode\n" +start_gcode = G28 ; home all axes\nG1 Z5 F5000 ; lift nozzle\n +support_material = 0 +support_material_angle = 0 +support_material_buildplate_only = 0 +support_material_contact_distance = 0.2 +support_material_enforce_layers = 0 +support_material_extruder = 1 +support_material_extrusion_width = 0 +support_material_interface_extruder = 1 +support_material_interface_extrusion_width = 0 +support_material_interface_layers = 3 +support_material_interface_spacing = 0 +support_material_interface_speed = 100% +support_material_max_layers = 0 +support_material_pattern = pillars +support_material_spacing = 2.5 +support_material_speed = 60 +support_material_threshold = 60% +temperature = 200 +thin_walls = 1 +threads = 8 +toolchange_gcode = +top_infill_extrusion_width = 0 +top_infill_pattern = rectilinear +top_solid_infill_speed = 15 +top_solid_layers = 3 +travel_speed = 130 +use_firmware_retraction = 0 +use_relative_e_distances = 0 +use_set_and_wait_bed = 0 +use_set_and_wait_extruder = 0 +use_volumetric_e = 0 +vibration_limit = 0 +wipe = 1 +xy_size_compensation = 0 +z_offset = 0 +z_steps_per_mm = 0 diff --git a/src/test/inputs/test_gcodewriter/config_lift_unlift.ini b/src/test/inputs/test_gcodewriter/config_lift_unlift.ini new file mode 100644 index 000000000..3e811b663 --- /dev/null +++ b/src/test/inputs/test_gcodewriter/config_lift_unlift.ini @@ -0,0 +1,38 @@ +# generated by Slic3r 1.3.1-dev on 2018-07-27 01:40:55 +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 +filament_notes = "" +gcode_comments = 0 +gcode_flavor = reprap +label_printed_objects = 0 +layer_gcode = +max_print_speed = 80 +max_volumetric_speed = 0 +notes = +pressure_advance = 0 +printer_notes = +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_set_and_wait_bed = 0 +use_set_and_wait_extruder = 0 +use_volumetric_e = 0 diff --git a/src/test/inputs/test_preset/preset_load_numeric.ini b/src/test/inputs/test_preset/preset_load_numeric.ini new file mode 100644 index 000000000..d1e79e8e5 --- /dev/null +++ b/src/test/inputs/test_preset/preset_load_numeric.ini @@ -0,0 +1,2 @@ +# generated by joseph lenox +adaptive_slicing = 1 diff --git a/src/test/inputs/test_preset_chooser/incompat-material-profile.ini b/src/test/inputs/test_preset_chooser/incompat-material-profile.ini new file mode 100644 index 000000000..583f0ee7c --- /dev/null +++ b/src/test/inputs/test_preset_chooser/incompat-material-profile.ini @@ -0,0 +1,25 @@ +# generated by Slic3r 1.3.0-dev on 2018-03-23 03:49:17 +bed_temperature = 70 +bridge_fan_speed = 100 +compatible_printers = printer-profile-2;nothing +cooling = 1 +disable_fan_first_layers = 4 +end_filament_gcode = "; Filament-specific end gcode \n;END gcode for filament\n" +extrusion_multiplier = 1 +fan_always_on = 1 +fan_below_layer_time = 10 +filament_colour = #4EFF00 +filament_cost = 30 +filament_density = 1.25 +filament_diameter = 1.73 +filament_max_volumetric_speed = 0 +filament_notes = "" +filament_settings_id = +first_layer_bed_temperature = 70 +first_layer_temperature = 220 +max_fan_speed = 100 +min_fan_speed = 100 +min_print_speed = 10 +slowdown_below_layer_time = 60 +start_filament_gcode = "; Filament gcode\n; ATOMIC PLA" +temperature = 218 diff --git a/src/test/inputs/test_preset_chooser/material-profile.ini b/src/test/inputs/test_preset_chooser/material-profile.ini new file mode 100644 index 000000000..79b096106 --- /dev/null +++ b/src/test/inputs/test_preset_chooser/material-profile.ini @@ -0,0 +1,25 @@ +# generated by Slic3r 1.3.0-dev on 2018-03-23 03:49:17 +bed_temperature = 70 +bridge_fan_speed = 100 +compatible_printers = +cooling = 1 +disable_fan_first_layers = 4 +end_filament_gcode = "; Filament-specific end gcode \n;END gcode for filament\n" +extrusion_multiplier = 1 +fan_always_on = 1 +fan_below_layer_time = 10 +filament_colour = #4EFF00 +filament_cost = 30 +filament_density = 1.25 +filament_diameter = 1.73 +filament_max_volumetric_speed = 0 +filament_notes = "" +filament_settings_id = +first_layer_bed_temperature = 70 +first_layer_temperature = 220 +max_fan_speed = 100 +min_fan_speed = 100 +min_print_speed = 10 +slowdown_below_layer_time = 60 +start_filament_gcode = "; Filament gcode\n; ATOMIC PLA" +temperature = 218 diff --git a/src/test/inputs/test_preset_chooser/print-profile.ini b/src/test/inputs/test_preset_chooser/print-profile.ini new file mode 100644 index 000000000..ba4ac630a --- /dev/null +++ b/src/test/inputs/test_preset_chooser/print-profile.ini @@ -0,0 +1,98 @@ +# generated by Slic3r 1.3.0-dev on 2017-10-30 20:38:52 +adaptive_slicing = 0 +adaptive_slicing_quality = 75% +avoid_crossing_perimeters = 0 +bottom_infill_pattern = rectilinear +bottom_solid_layers = 2 +bridge_acceleration = 5000 +bridge_flow_ratio = 1 +bridge_speed = 60 +brim_connections_width = 0 +brim_width = 0 +compatible_printers = +complete_objects = 0 +default_acceleration = 5000 +dont_support_bridges = 1 +external_perimeter_extrusion_width = 0.8 +external_perimeter_speed = 50% +external_perimeters_first = 0 +extra_perimeters = 1 +extruder_clearance_height = 20 +extruder_clearance_radius = 20 +extrusion_width = 0 +fill_angle = 45 +fill_density = 0% +fill_gaps = 1 +fill_pattern = honeycomb +first_layer_acceleration = 5000 +first_layer_extrusion_width = 200% +first_layer_height = 0.3 +first_layer_speed = 30 +gap_fill_speed = 20 +gcode_comments = 0 +infill_acceleration = 5000 +infill_every_layers = 1 +infill_extruder = 1 +infill_extrusion_width = 0 +infill_first = 0 +infill_only_where_needed = 0 +infill_overlap = 15% +infill_speed = 80 +interface_shells = 0 +interior_brim_width = 0 +layer_height = 0.2 +match_horizontal_surfaces = 0 +max_print_speed = 80 +max_volumetric_speed = 0 +min_skirt_length = 5 +notes = +only_retract_when_crossing_perimeters = 1 +ooze_prevention = 0 +output_filename_format = [input_filename_base].gcode +overhangs = 1 +perimeter_acceleration = 450 +perimeter_extruder = 1 +perimeter_extrusion_width = 0.8 +perimeter_speed = 60 +perimeters = 2 +post_process = +print_settings_id = +raft_layers = 0 +regions_overlap = 0 +resolution = 0 +seam_position = aligned +shortcuts = support_material +skirt_distance = 2 +skirt_height = 1 +skirts = 1 +small_perimeter_speed = 15 +solid_infill_below_area = 70 +solid_infill_every_layers = 0 +solid_infill_extruder = 1 +solid_infill_extrusion_width = 0 +solid_infill_speed = 20 +spiral_vase = 0 +standby_temperature_delta = -5 +support_material = 0 +support_material_angle = 0 +support_material_buildplate_only = 0 +support_material_contact_distance = 0.2 +support_material_enforce_layers = 0 +support_material_extruder = 1 +support_material_extrusion_width = 0 +support_material_interface_extruder = 1 +support_material_interface_extrusion_width = 0 +support_material_interface_layers = 3 +support_material_interface_spacing = 0 +support_material_interface_speed = 100% +support_material_pattern = rectilinear-grid +support_material_spacing = 1 +support_material_speed = 60 +support_material_threshold = 60% +thin_walls = 1 +top_infill_extrusion_width = 0 +top_infill_pattern = archimedeanchords +top_solid_infill_speed = 15 +top_solid_layers = 0 +travel_speed = 130 +xy_size_compensation = 0 diff --git a/src/test/inputs/test_preset_chooser/printer-profile-2.ini b/src/test/inputs/test_preset_chooser/printer-profile-2.ini new file mode 100644 index 000000000..59e8bf968 --- /dev/null +++ b/src/test/inputs/test_preset_chooser/printer-profile-2.ini @@ -0,0 +1,37 @@ +# generated by Slic3r 1.3.0-dev on 2017-06-25 14:39:57 +bed_shape = 79.5618x8.36228,78.2518x16.6329,76.0845x24.7214,73.0836x32.5389,69.282x40,64.7214x47.0228,59.4516x53.5304,53.5304x59.4516,47.0228x64.7214,40x69.282,32.5389x73.0836,24.7214x76.0845,16.6329x78.2518,8.36228x79.5618,0x80,-8.36228x79.5618,-16.6329x78.2518,-24.7214x76.0845,-32.5389x73.0836,-40x69.282,-47.0228x64.7214,-53.5304x59.4516,-59.4516x53.5304,-64.7214x47.0228,-69.282x40,-73.0836x32.5389,-76.0845x24.7214,-78.2518x16.6329,-79.5618x8.36228,-80x0,-79.5618x-8.36228,-78.2518x-16.6329,-76.0845x-24.7214,-73.0836x-32.5389,-69.282x-40,-64.7214x-47.0228,-59.4516x-53.5304,-53.5304x-59.4516,-47.0228x-64.7214,-40x-69.282,-32.5389x-73.0836,-24.7214x-76.0845,-16.6329x-78.2518,-8.36228x-79.5618,0x-80,8.36228x-79.5618,16.6329x-78.2518,24.7214x-76.0845,32.5389x-73.0836,40x-69.282,47.0228x-64.7214,53.5304x-59.4516,59.4516x-53.5304,64.7214x-47.0228,69.282x-40,73.0836x-32.5389,76.0845x-24.7214,78.2518x-16.6329,79.5618x-8.36228,80x0 +before_layer_gcode = +between_objects_gcode = +end_gcode = M104 S0 ; turn off temperature\nM140 S0;\nG28 ;home all axis\nM84; disable motors\n +extruder_offset = 0x0 +gcode_flavor = smoothie +has_heatbed = 1 +host_type = octoprint +layer_gcode = +nozzle_diameter = 0.35 +octoprint_apikey = +pressure_advance = 0 +print_host = +printer_notes = +printer_settings_id = +retract_before_travel = 1 +retract_layer_change = 1 +retract_length = 2 +retract_length_toolchange = 10 +retract_lift = 0 +retract_lift_above = 0 +retract_lift_below = 0 +retract_restart_extra = 0 +retract_restart_extra_toolchange = 0 +retract_speed = 40 +serial_port = +serial_speed = 250000 +start_gcode = M106 S0\nG90; set to absolute position\nG28\nM190 S60; preheat bed\nM104 S100; preheat nozzle.\nM190 S[first_layer_bed_temperature]; Wait until heatbed hits [first_layer_bed_temperature] \nG0 X0 Y0 Z0\nM109 S[first_layer_temperature]; heat nozzle to [first_layer_temperature]C; +toolchange_gcode = +use_firmware_retraction = 1 +use_relative_e_distances = 0 +use_volumetric_e = 1 +vibration_limit = 0 +wipe = 0 +z_offset = 0 +z_steps_per_mm = 0 diff --git a/src/test/inputs/test_preset_chooser/printer-profile.ini b/src/test/inputs/test_preset_chooser/printer-profile.ini new file mode 100644 index 000000000..59e8bf968 --- /dev/null +++ b/src/test/inputs/test_preset_chooser/printer-profile.ini @@ -0,0 +1,37 @@ +# generated by Slic3r 1.3.0-dev on 2017-06-25 14:39:57 +bed_shape = 79.5618x8.36228,78.2518x16.6329,76.0845x24.7214,73.0836x32.5389,69.282x40,64.7214x47.0228,59.4516x53.5304,53.5304x59.4516,47.0228x64.7214,40x69.282,32.5389x73.0836,24.7214x76.0845,16.6329x78.2518,8.36228x79.5618,0x80,-8.36228x79.5618,-16.6329x78.2518,-24.7214x76.0845,-32.5389x73.0836,-40x69.282,-47.0228x64.7214,-53.5304x59.4516,-59.4516x53.5304,-64.7214x47.0228,-69.282x40,-73.0836x32.5389,-76.0845x24.7214,-78.2518x16.6329,-79.5618x8.36228,-80x0,-79.5618x-8.36228,-78.2518x-16.6329,-76.0845x-24.7214,-73.0836x-32.5389,-69.282x-40,-64.7214x-47.0228,-59.4516x-53.5304,-53.5304x-59.4516,-47.0228x-64.7214,-40x-69.282,-32.5389x-73.0836,-24.7214x-76.0845,-16.6329x-78.2518,-8.36228x-79.5618,0x-80,8.36228x-79.5618,16.6329x-78.2518,24.7214x-76.0845,32.5389x-73.0836,40x-69.282,47.0228x-64.7214,53.5304x-59.4516,59.4516x-53.5304,64.7214x-47.0228,69.282x-40,73.0836x-32.5389,76.0845x-24.7214,78.2518x-16.6329,79.5618x-8.36228,80x0 +before_layer_gcode = +between_objects_gcode = +end_gcode = M104 S0 ; turn off temperature\nM140 S0;\nG28 ;home all axis\nM84; disable motors\n +extruder_offset = 0x0 +gcode_flavor = smoothie +has_heatbed = 1 +host_type = octoprint +layer_gcode = +nozzle_diameter = 0.35 +octoprint_apikey = +pressure_advance = 0 +print_host = +printer_notes = +printer_settings_id = +retract_before_travel = 1 +retract_layer_change = 1 +retract_length = 2 +retract_length_toolchange = 10 +retract_lift = 0 +retract_lift_above = 0 +retract_lift_below = 0 +retract_restart_extra = 0 +retract_restart_extra_toolchange = 0 +retract_speed = 40 +serial_port = +serial_speed = 250000 +start_gcode = M106 S0\nG90; set to absolute position\nG28\nM190 S60; preheat bed\nM104 S100; preheat nozzle.\nM190 S[first_layer_bed_temperature]; Wait until heatbed hits [first_layer_bed_temperature] \nG0 X0 Y0 Z0\nM109 S[first_layer_temperature]; heat nozzle to [first_layer_temperature]C; +toolchange_gcode = +use_firmware_retraction = 1 +use_relative_e_distances = 0 +use_volumetric_e = 1 +vibration_limit = 0 +wipe = 0 +z_offset = 0 +z_steps_per_mm = 0 diff --git a/src/test/inputs/test_trianglemesh/4486/100_000.stl b/src/test/inputs/test_trianglemesh/4486/100_000.stl new file mode 100644 index 000000000..aebcb5e02 --- /dev/null +++ b/src/test/inputs/test_trianglemesh/4486/100_000.stl @@ -0,0 +1,422 @@ +solid stdin + facet normal 0.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 2.000000938e+05 1.450000938e+05 1.000001016e+05 + vertex 2.000000938e+05 5.500010156e+04 1.000001016e+05 + vertex 2.000000938e+05 5.500010156e+04 1.000001016e+05 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 -1.000000000e+00 + outer loop + vertex 2.000000938e+05 1.450000938e+05 1.000001016e+05 + vertex 1.500000938e+05 1.450000938e+05 1.000001016e+05 + vertex 1.500000938e+05 5.500010156e+04 1.000001016e+05 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 1.000000000e+00 + outer loop + vertex 2.000000938e+05 5.500010156e+04 1.000001016e+05 + vertex 1.500000938e+05 5.500010156e+04 1.000001016e+05 + vertex 1.500000938e+05 1.450000938e+05 1.000001016e+05 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 2.000000938e+05 5.500010156e+04 1.000001016e+05 + vertex 2.000000938e+05 5.500010156e+04 1.000001016e+05 + vertex 1.500000938e+05 5.500010156e+04 1.000001016e+05 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 2.000000938e+05 1.450000938e+05 1.000001016e+05 + vertex 2.000000938e+05 1.450000938e+05 1.000001016e+05 + vertex 1.500000938e+05 1.450000938e+05 1.000001016e+05 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.000001016e+05 1.450000938e+05 1.015625000e-01 + vertex 1.000001016e+05 5.500010156e+04 1.015625000e-01 + vertex 1.000001016e+05 5.500010156e+04 1.015625000e-01 + endloop + endfacet + facet normal -1.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.000001016e+05 1.450000938e+05 1.015625000e-01 + vertex 1.000001016e+05 1.450000938e+05 5.000010156e+04 + vertex 1.000001016e+05 5.500010156e+04 5.000010156e+04 + endloop + endfacet + facet normal 1.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.000001016e+05 5.500010156e+04 1.015625000e-01 + vertex 1.000001016e+05 5.500010156e+04 5.000010156e+04 + vertex 1.000001016e+05 1.450000938e+05 5.000010156e+04 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.000001016e+05 5.500010156e+04 1.015625000e-01 + vertex 1.000001016e+05 5.500010156e+04 1.015625000e-01 + vertex 1.000001016e+05 5.500010156e+04 5.000010156e+04 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.000001016e+05 1.450000938e+05 1.015625000e-01 + vertex 1.000001016e+05 1.450000938e+05 1.015625000e-01 + vertex 1.000001016e+05 1.450000938e+05 5.000010156e+04 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.000001016e+05 2.000000938e+05 1.450000938e+05 + vertex 1.000001016e+05 2.000000938e+05 5.500010156e+04 + vertex 1.000001016e+05 2.000000938e+05 5.500010156e+04 + endloop + endfacet + facet normal -1.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.000001016e+05 2.000000938e+05 1.450000938e+05 + vertex 1.000001016e+05 1.500000938e+05 1.450000938e+05 + vertex 1.000001016e+05 1.500000938e+05 5.500010156e+04 + endloop + endfacet + facet normal 1.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.000001016e+05 2.000000938e+05 5.500010156e+04 + vertex 1.000001016e+05 1.500000938e+05 5.500010156e+04 + vertex 1.000001016e+05 1.500000938e+05 1.450000938e+05 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.000001016e+05 2.000000938e+05 5.500010156e+04 + vertex 1.000001016e+05 2.000000938e+05 5.500010156e+04 + vertex 1.000001016e+05 1.500000938e+05 5.500010156e+04 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.000001016e+05 2.000000938e+05 1.450000938e+05 + vertex 1.000001016e+05 2.000000938e+05 1.450000938e+05 + vertex 1.000001016e+05 1.500000938e+05 1.450000938e+05 + endloop + endfacet + facet normal -1.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 5.000010156e+04 5.000010156e+04 5.000010156e+04 + vertex 5.000010156e+04 1.500000938e+05 5.000010156e+04 + vertex 5.000010156e+04 1.500000938e+05 1.500000938e+05 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.500000938e+05 1.450000938e+05 1.000001016e+05 + vertex 1.500000938e+05 1.450000938e+05 1.000001016e+05 + vertex 1.500000938e+05 1.500000938e+05 5.000010156e+04 + endloop + endfacet + facet normal 1.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.500000938e+05 5.500010156e+04 1.000001016e+05 + vertex 1.500000938e+05 1.450000938e+05 1.000001016e+05 + vertex 1.500000938e+05 1.500000938e+05 5.000010156e+04 + endloop + endfacet + facet normal 1.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.500000938e+05 5.500010156e+04 1.000001016e+05 + vertex 1.500000938e+05 1.500000938e+05 5.000010156e+04 + vertex 1.500000938e+05 5.000010156e+04 5.000010156e+04 + endloop + endfacet + facet normal 1.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.500000938e+05 5.500010156e+04 1.000001016e+05 + vertex 1.500000938e+05 5.000010156e+04 5.000010156e+04 + vertex 1.500000938e+05 5.000010156e+04 1.500000938e+05 + endloop + endfacet + facet normal 1.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.500000938e+05 1.500000938e+05 1.500000938e+05 + vertex 1.500000938e+05 1.450000938e+05 1.000001016e+05 + vertex 1.500000938e+05 5.500010156e+04 1.000001016e+05 + endloop + endfacet + facet normal 1.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.500000938e+05 5.000010156e+04 1.500000938e+05 + vertex 1.500000938e+05 1.500000938e+05 1.500000938e+05 + vertex 1.500000938e+05 5.500010156e+04 1.000001016e+05 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.500000938e+05 5.500010156e+04 1.000001016e+05 + vertex 1.500000938e+05 5.000010156e+04 1.500000938e+05 + vertex 1.500000938e+05 5.500010156e+04 1.000001016e+05 + endloop + endfacet + facet normal 0.000000000e+00 -1.000000000e+00 0.000000000e+00 + outer loop + vertex 1.500000938e+05 5.000010156e+04 1.500000938e+05 + vertex 1.500000938e+05 5.000010156e+04 5.000010156e+04 + vertex 5.000010156e+04 5.000010156e+04 5.000010156e+04 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 -9.999999404e-01 + outer loop + vertex 1.000001016e+05 1.450000938e+05 5.000010156e+04 + vertex 1.500000938e+05 1.500000938e+05 5.000010156e+04 + vertex 5.000010156e+04 1.500000938e+05 5.000010156e+04 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 -1.000000000e+00 + outer loop + vertex 1.000001016e+05 1.450000938e+05 5.000010156e+04 + vertex 5.000010156e+04 1.500000938e+05 5.000010156e+04 + vertex 5.000010156e+04 5.000010156e+04 5.000010156e+04 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 -1.000000000e+00 + outer loop + vertex 1.000001016e+05 5.500010156e+04 5.000010156e+04 + vertex 1.000001016e+05 1.450000938e+05 5.000010156e+04 + vertex 5.000010156e+04 5.000010156e+04 5.000010156e+04 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 -1.000000000e+00 + outer loop + vertex 1.000001016e+05 5.500010156e+04 5.000010156e+04 + vertex 5.000010156e+04 5.000010156e+04 5.000010156e+04 + vertex 1.500000938e+05 5.000010156e+04 5.000010156e+04 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 -1.000000000e+00 + outer loop + vertex 1.500000938e+05 1.500000938e+05 5.000010156e+04 + vertex 1.000001016e+05 1.450000938e+05 5.000010156e+04 + vertex 1.000001016e+05 5.500010156e+04 5.000010156e+04 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 -1.000000000e+00 + outer loop + vertex 1.500000938e+05 5.000010156e+04 5.000010156e+04 + vertex 1.500000938e+05 1.500000938e+05 5.000010156e+04 + vertex 1.000001016e+05 5.500010156e+04 5.000010156e+04 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.000001016e+05 5.500010156e+04 5.000010156e+04 + vertex 1.500000938e+05 5.000010156e+04 5.000010156e+04 + vertex 1.000001016e+05 5.500010156e+04 5.000010156e+04 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 1.000000000e+00 + outer loop + vertex 5.000010156e+04 5.000010156e+04 1.500000938e+05 + vertex 5.000010156e+04 1.500000938e+05 1.500000938e+05 + vertex 1.500000938e+05 1.500000938e+05 1.500000938e+05 + endloop + endfacet + facet normal 0.000000000e+00 1.000000000e+00 -2.048000624e-06 + outer loop + vertex 1.500000938e+05 1.500000938e+05 1.500000938e+05 + vertex 5.000010156e+04 1.500000938e+05 1.500000938e+05 + vertex 1.000001016e+05 1.500000938e+05 1.450000938e+05 + endloop + endfacet + facet normal 0.000000000e+00 1.000000000e+00 0.000000000e+00 + outer loop + vertex 1.000001016e+05 1.500000938e+05 5.500010156e+04 + vertex 1.000001016e+05 1.500000938e+05 1.450000938e+05 + vertex 5.000010156e+04 1.500000938e+05 5.000010156e+04 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.500000938e+05 1.500000938e+05 1.500000938e+05 + vertex 1.000001016e+05 1.500000938e+05 1.450000938e+05 + vertex 1.000001016e+05 1.500000938e+05 1.450000938e+05 + endloop + endfacet + facet normal 0.000000000e+00 1.000000000e+00 0.000000000e+00 + outer loop + vertex 1.500000938e+05 1.500000938e+05 1.500000938e+05 + vertex 1.000001016e+05 1.500000938e+05 1.450000938e+05 + vertex 1.000001016e+05 1.500000938e+05 5.500010156e+04 + endloop + endfacet + facet normal 0.000000000e+00 1.000000000e+00 0.000000000e+00 + outer loop + vertex 1.500000938e+05 1.500000938e+05 5.000010156e+04 + vertex 1.500000938e+05 1.500000938e+05 1.500000938e+05 + vertex 1.000001016e+05 1.500000938e+05 5.500010156e+04 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.500000938e+05 1.500000938e+05 5.000010156e+04 + vertex 1.000001016e+05 1.500000938e+05 5.500010156e+04 + vertex 1.000001016e+05 1.500000938e+05 5.500010156e+04 + endloop + endfacet + facet normal 0.000000000e+00 1.000000000e+00 2.048000624e-06 + outer loop + vertex 1.500000938e+05 1.500000938e+05 5.000010156e+04 + vertex 1.000001016e+05 1.500000938e+05 5.500010156e+04 + vertex 5.000010156e+04 1.500000938e+05 5.000010156e+04 + endloop + endfacet + facet normal 0.000000000e+00 1.000000000e+00 0.000000000e+00 + outer loop + vertex 1.000001016e+05 1.500000938e+05 1.450000938e+05 + vertex 5.000010156e+04 1.500000938e+05 1.500000938e+05 + vertex 5.000010156e+04 1.500000938e+05 5.000010156e+04 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 1.000000000e+00 + outer loop + vertex 1.500000938e+05 1.500000938e+05 1.500000938e+05 + vertex 1.500000938e+05 5.000010156e+04 1.500000938e+05 + vertex 5.000010156e+04 5.000010156e+04 1.500000938e+05 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.000001016e+05 1.450000938e+05 5.000010156e+04 + vertex 1.000001016e+05 1.450000938e+05 5.000010156e+04 + vertex 1.500000938e+05 1.500000938e+05 5.000010156e+04 + endloop + endfacet + facet normal 0.000000000e+00 -1.000000000e+00 0.000000000e+00 + outer loop + vertex 5.000010156e+04 5.000010156e+04 5.000010156e+04 + vertex 5.000010156e+04 5.000010156e+04 1.500000938e+05 + vertex 1.500000938e+05 5.000010156e+04 1.500000938e+05 + endloop + endfacet + facet normal 1.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.500000938e+05 1.450000938e+05 1.000001016e+05 + vertex 1.500000938e+05 1.500000938e+05 1.500000938e+05 + vertex 1.500000938e+05 1.500000938e+05 5.000010156e+04 + endloop + endfacet + facet normal -1.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 5.000010156e+04 1.500000938e+05 1.500000938e+05 + vertex 5.000010156e+04 5.000010156e+04 1.500000938e+05 + vertex 5.000010156e+04 5.000010156e+04 5.000010156e+04 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.000001016e+05 1.500000938e+05 1.450000938e+05 + vertex 1.000001016e+05 1.500000938e+05 1.450000938e+05 + vertex 1.000001016e+05 2.000000938e+05 1.450000938e+05 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.000001016e+05 1.500000938e+05 5.500010156e+04 + vertex 1.000001016e+05 1.500000938e+05 5.500010156e+04 + vertex 1.000001016e+05 2.000000938e+05 5.500010156e+04 + endloop + endfacet + facet normal 1.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.000001016e+05 1.500000938e+05 1.450000938e+05 + vertex 1.000001016e+05 2.000000938e+05 1.450000938e+05 + vertex 1.000001016e+05 2.000000938e+05 5.500010156e+04 + endloop + endfacet + facet normal -1.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.000001016e+05 1.500000938e+05 5.500010156e+04 + vertex 1.000001016e+05 2.000000938e+05 5.500010156e+04 + vertex 1.000001016e+05 2.000000938e+05 1.450000938e+05 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.000001016e+05 2.000000938e+05 5.500010156e+04 + vertex 1.000001016e+05 2.000000938e+05 1.450000938e+05 + vertex 1.000001016e+05 2.000000938e+05 1.450000938e+05 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.000001016e+05 1.450000938e+05 5.000010156e+04 + vertex 1.000001016e+05 1.450000938e+05 5.000010156e+04 + vertex 1.000001016e+05 1.450000938e+05 1.015625000e-01 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.000001016e+05 5.500010156e+04 5.000010156e+04 + vertex 1.000001016e+05 5.500010156e+04 5.000010156e+04 + vertex 1.000001016e+05 5.500010156e+04 1.015625000e-01 + endloop + endfacet + facet normal 1.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.000001016e+05 1.450000938e+05 5.000010156e+04 + vertex 1.000001016e+05 1.450000938e+05 1.015625000e-01 + vertex 1.000001016e+05 5.500010156e+04 1.015625000e-01 + endloop + endfacet + facet normal -1.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.000001016e+05 5.500010156e+04 5.000010156e+04 + vertex 1.000001016e+05 5.500010156e+04 1.015625000e-01 + vertex 1.000001016e+05 1.450000938e+05 1.015625000e-01 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.000001016e+05 5.500010156e+04 1.015625000e-01 + vertex 1.000001016e+05 1.450000938e+05 1.015625000e-01 + vertex 1.000001016e+05 1.450000938e+05 1.015625000e-01 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.500000938e+05 1.450000938e+05 1.000001016e+05 + vertex 1.500000938e+05 1.450000938e+05 1.000001016e+05 + vertex 2.000000938e+05 1.450000938e+05 1.000001016e+05 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.500000938e+05 5.500010156e+04 1.000001016e+05 + vertex 1.500000938e+05 5.500010156e+04 1.000001016e+05 + vertex 2.000000938e+05 5.500010156e+04 1.000001016e+05 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 1.000000000e+00 + outer loop + vertex 1.500000938e+05 1.450000938e+05 1.000001016e+05 + vertex 2.000000938e+05 1.450000938e+05 1.000001016e+05 + vertex 2.000000938e+05 5.500010156e+04 1.000001016e+05 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 -1.000000000e+00 + outer loop + vertex 1.500000938e+05 5.500010156e+04 1.000001016e+05 + vertex 2.000000938e+05 5.500010156e+04 1.000001016e+05 + vertex 2.000000938e+05 1.450000938e+05 1.000001016e+05 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 2.000000938e+05 5.500010156e+04 1.000001016e+05 + vertex 2.000000938e+05 1.450000938e+05 1.000001016e+05 + vertex 2.000000938e+05 1.450000938e+05 1.000001016e+05 + endloop + endfacet +endsolid diff --git a/src/test/inputs/test_trianglemesh/4486/10_000.stl b/src/test/inputs/test_trianglemesh/4486/10_000.stl new file mode 100644 index 000000000..2ca3a0bf7 --- /dev/null +++ b/src/test/inputs/test_trianglemesh/4486/10_000.stl @@ -0,0 +1,422 @@ +solid stdin + facet normal 1.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 2.000009961e+04 1.450009961e+04 1.000009863e+04 + vertex 2.000009961e+04 5.500099609e+03 1.000009863e+04 + vertex 2.000009961e+04 5.500099609e+03 1.000010059e+04 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 -1.000000000e+00 + outer loop + vertex 2.000009961e+04 1.450009961e+04 1.000009863e+04 + vertex 1.500009961e+04 1.450009961e+04 1.000009863e+04 + vertex 1.500009961e+04 5.500099609e+03 1.000009863e+04 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 1.000000000e+00 + outer loop + vertex 2.000009961e+04 5.500099609e+03 1.000010059e+04 + vertex 1.500009961e+04 5.500099609e+03 1.000010059e+04 + vertex 1.500009961e+04 1.450009961e+04 1.000010059e+04 + endloop + endfacet + facet normal 0.000000000e+00 -1.000000000e+00 0.000000000e+00 + outer loop + vertex 2.000009961e+04 5.500099609e+03 1.000010059e+04 + vertex 2.000009961e+04 5.500099609e+03 1.000009863e+04 + vertex 1.500009961e+04 5.500099609e+03 1.000009863e+04 + endloop + endfacet + facet normal 0.000000000e+00 1.000000000e+00 0.000000000e+00 + outer loop + vertex 2.000009961e+04 1.450009961e+04 1.000009863e+04 + vertex 2.000009961e+04 1.450009961e+04 1.000010059e+04 + vertex 1.500009961e+04 1.450009961e+04 1.000010059e+04 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 -1.000000000e+00 + outer loop + vertex 1.000009863e+04 1.450009961e+04 9.960937500e-02 + vertex 1.000009863e+04 5.500099609e+03 9.960937500e-02 + vertex 1.000010059e+04 5.500099609e+03 9.960937500e-02 + endloop + endfacet + facet normal -1.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.000009863e+04 1.450009961e+04 9.960937500e-02 + vertex 1.000009863e+04 1.450009961e+04 5.000099609e+03 + vertex 1.000009863e+04 5.500099609e+03 5.000099609e+03 + endloop + endfacet + facet normal 1.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.000010059e+04 5.500099609e+03 9.960937500e-02 + vertex 1.000010059e+04 5.500099609e+03 5.000099609e+03 + vertex 1.000010059e+04 1.450009961e+04 5.000099609e+03 + endloop + endfacet + facet normal 0.000000000e+00 -1.000000000e+00 0.000000000e+00 + outer loop + vertex 1.000010059e+04 5.500099609e+03 9.960937500e-02 + vertex 1.000009863e+04 5.500099609e+03 9.960937500e-02 + vertex 1.000009863e+04 5.500099609e+03 5.000099609e+03 + endloop + endfacet + facet normal 0.000000000e+00 1.000000000e+00 0.000000000e+00 + outer loop + vertex 1.000009863e+04 1.450009961e+04 9.960937500e-02 + vertex 1.000010059e+04 1.450009961e+04 9.960937500e-02 + vertex 1.000010059e+04 1.450009961e+04 5.000099609e+03 + endloop + endfacet + facet normal 0.000000000e+00 1.000000000e+00 0.000000000e+00 + outer loop + vertex 1.000009863e+04 2.000009961e+04 1.450009961e+04 + vertex 1.000009863e+04 2.000009961e+04 5.500099609e+03 + vertex 1.000010059e+04 2.000009961e+04 5.500099609e+03 + endloop + endfacet + facet normal -1.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.000009863e+04 2.000009961e+04 1.450009961e+04 + vertex 1.000009863e+04 1.500009961e+04 1.450009961e+04 + vertex 1.000009863e+04 1.500009961e+04 5.500099609e+03 + endloop + endfacet + facet normal 1.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.000010059e+04 2.000009961e+04 5.500099609e+03 + vertex 1.000010059e+04 1.500009961e+04 5.500099609e+03 + vertex 1.000010059e+04 1.500009961e+04 1.450009961e+04 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 -1.000000000e+00 + outer loop + vertex 1.000010059e+04 2.000009961e+04 5.500099609e+03 + vertex 1.000009863e+04 2.000009961e+04 5.500099609e+03 + vertex 1.000009863e+04 1.500009961e+04 5.500099609e+03 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 1.000000000e+00 + outer loop + vertex 1.000009863e+04 2.000009961e+04 1.450009961e+04 + vertex 1.000010059e+04 2.000009961e+04 1.450009961e+04 + vertex 1.000010059e+04 1.500009961e+04 1.450009961e+04 + endloop + endfacet + facet normal -1.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 5.000099609e+03 5.000099609e+03 5.000099609e+03 + vertex 5.000099609e+03 1.500009961e+04 5.000099609e+03 + vertex 5.000099609e+03 1.500009961e+04 1.500009961e+04 + endloop + endfacet + facet normal 1.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.500009961e+04 1.450009961e+04 1.000009863e+04 + vertex 1.500009961e+04 1.450009961e+04 1.000010059e+04 + vertex 1.500009961e+04 1.500009961e+04 5.000099609e+03 + endloop + endfacet + facet normal 1.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.500009961e+04 5.500099609e+03 1.000009863e+04 + vertex 1.500009961e+04 1.450009961e+04 1.000009863e+04 + vertex 1.500009961e+04 1.500009961e+04 5.000099609e+03 + endloop + endfacet + facet normal 1.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.500009961e+04 5.500099609e+03 1.000009863e+04 + vertex 1.500009961e+04 1.500009961e+04 5.000099609e+03 + vertex 1.500009961e+04 5.000099609e+03 5.000099609e+03 + endloop + endfacet + facet normal 1.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.500009961e+04 5.500099609e+03 1.000009863e+04 + vertex 1.500009961e+04 5.000099609e+03 5.000099609e+03 + vertex 1.500009961e+04 5.000099609e+03 1.500009961e+04 + endloop + endfacet + facet normal 9.999999404e-01 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.500009961e+04 1.500009961e+04 1.500009961e+04 + vertex 1.500009961e+04 1.450009961e+04 1.000010059e+04 + vertex 1.500009961e+04 5.500099609e+03 1.000010059e+04 + endloop + endfacet + facet normal 1.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.500009961e+04 5.000099609e+03 1.500009961e+04 + vertex 1.500009961e+04 1.500009961e+04 1.500009961e+04 + vertex 1.500009961e+04 5.500099609e+03 1.000010059e+04 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.500009961e+04 5.500099609e+03 1.000009863e+04 + vertex 1.500009961e+04 5.000099609e+03 1.500009961e+04 + vertex 1.500009961e+04 5.500099609e+03 1.000010059e+04 + endloop + endfacet + facet normal 0.000000000e+00 -1.000000000e+00 0.000000000e+00 + outer loop + vertex 1.500009961e+04 5.000099609e+03 1.500009961e+04 + vertex 1.500009961e+04 5.000099609e+03 5.000099609e+03 + vertex 5.000099609e+03 5.000099609e+03 5.000099609e+03 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 -1.000000000e+00 + outer loop + vertex 1.000009863e+04 1.450009961e+04 5.000099609e+03 + vertex 1.500009961e+04 1.500009961e+04 5.000099609e+03 + vertex 5.000099609e+03 1.500009961e+04 5.000099609e+03 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 -1.000000000e+00 + outer loop + vertex 1.000009863e+04 1.450009961e+04 5.000099609e+03 + vertex 5.000099609e+03 1.500009961e+04 5.000099609e+03 + vertex 5.000099609e+03 5.000099609e+03 5.000099609e+03 + endloop + endfacet + facet normal -1.777778067e-07 0.000000000e+00 -1.000000000e+00 + outer loop + vertex 1.000009863e+04 5.500099609e+03 5.000099609e+03 + vertex 1.000009863e+04 1.450009961e+04 5.000099609e+03 + vertex 5.000099609e+03 5.000099609e+03 5.000099609e+03 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 -1.000000000e+00 + outer loop + vertex 1.000009863e+04 5.500099609e+03 5.000099609e+03 + vertex 5.000099609e+03 5.000099609e+03 5.000099609e+03 + vertex 1.500009961e+04 5.000099609e+03 5.000099609e+03 + endloop + endfacet + facet normal 1.777778351e-07 0.000000000e+00 -9.999999404e-01 + outer loop + vertex 1.500009961e+04 1.500009961e+04 5.000099609e+03 + vertex 1.000010059e+04 1.450009961e+04 5.000099609e+03 + vertex 1.000010059e+04 5.500099609e+03 5.000099609e+03 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 -1.000000000e+00 + outer loop + vertex 1.500009961e+04 5.000099609e+03 5.000099609e+03 + vertex 1.500009961e+04 1.500009961e+04 5.000099609e+03 + vertex 1.000010059e+04 5.500099609e+03 5.000099609e+03 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 1.000000000e+00 + outer loop + vertex 1.000009863e+04 5.500099609e+03 5.000099609e+03 + vertex 1.500009961e+04 5.000099609e+03 5.000099609e+03 + vertex 1.000010059e+04 5.500099609e+03 5.000099609e+03 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 1.000000000e+00 + outer loop + vertex 5.000099609e+03 5.000099609e+03 1.500009961e+04 + vertex 5.000099609e+03 1.500009961e+04 1.500009961e+04 + vertex 1.500009961e+04 1.500009961e+04 1.500009961e+04 + endloop + endfacet + facet normal 0.000000000e+00 1.000000000e+00 0.000000000e+00 + outer loop + vertex 1.500009961e+04 1.500009961e+04 1.500009961e+04 + vertex 5.000099609e+03 1.500009961e+04 1.500009961e+04 + vertex 1.000009863e+04 1.500009961e+04 1.450009961e+04 + endloop + endfacet + facet normal 0.000000000e+00 9.999999404e-01 0.000000000e+00 + outer loop + vertex 1.000009863e+04 1.500009961e+04 5.500099609e+03 + vertex 1.000009863e+04 1.500009961e+04 1.450009961e+04 + vertex 5.000099609e+03 1.500009961e+04 5.000099609e+03 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 -1.000000000e+00 + outer loop + vertex 1.500009961e+04 1.500009961e+04 1.500009961e+04 + vertex 1.000009863e+04 1.500009961e+04 1.450009961e+04 + vertex 1.000010059e+04 1.500009961e+04 1.450009961e+04 + endloop + endfacet + facet normal 0.000000000e+00 9.999999404e-01 0.000000000e+00 + outer loop + vertex 1.500009961e+04 1.500009961e+04 1.500009961e+04 + vertex 1.000010059e+04 1.500009961e+04 1.450009961e+04 + vertex 1.000010059e+04 1.500009961e+04 5.500099609e+03 + endloop + endfacet + facet normal 0.000000000e+00 1.000000000e+00 0.000000000e+00 + outer loop + vertex 1.500009961e+04 1.500009961e+04 5.000099609e+03 + vertex 1.500009961e+04 1.500009961e+04 1.500009961e+04 + vertex 1.000010059e+04 1.500009961e+04 5.500099609e+03 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 1.000000000e+00 + outer loop + vertex 1.500009961e+04 1.500009961e+04 5.000099609e+03 + vertex 1.000010059e+04 1.500009961e+04 5.500099609e+03 + vertex 1.000009863e+04 1.500009961e+04 5.500099609e+03 + endloop + endfacet + facet normal 0.000000000e+00 1.000000000e+00 0.000000000e+00 + outer loop + vertex 1.500009961e+04 1.500009961e+04 5.000099609e+03 + vertex 1.000009863e+04 1.500009961e+04 5.500099609e+03 + vertex 5.000099609e+03 1.500009961e+04 5.000099609e+03 + endloop + endfacet + facet normal 0.000000000e+00 1.000000000e+00 0.000000000e+00 + outer loop + vertex 1.000009863e+04 1.500009961e+04 1.450009961e+04 + vertex 5.000099609e+03 1.500009961e+04 1.500009961e+04 + vertex 5.000099609e+03 1.500009961e+04 5.000099609e+03 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 1.000000000e+00 + outer loop + vertex 1.500009961e+04 1.500009961e+04 1.500009961e+04 + vertex 1.500009961e+04 5.000099609e+03 1.500009961e+04 + vertex 5.000099609e+03 5.000099609e+03 1.500009961e+04 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.000009863e+04 1.450009961e+04 5.000099609e+03 + vertex 1.000010059e+04 1.450009961e+04 5.000099609e+03 + vertex 1.500009961e+04 1.500009961e+04 5.000099609e+03 + endloop + endfacet + facet normal 0.000000000e+00 -1.000000000e+00 0.000000000e+00 + outer loop + vertex 5.000099609e+03 5.000099609e+03 5.000099609e+03 + vertex 5.000099609e+03 5.000099609e+03 1.500009961e+04 + vertex 1.500009961e+04 5.000099609e+03 1.500009961e+04 + endloop + endfacet + facet normal 1.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.500009961e+04 1.450009961e+04 1.000010059e+04 + vertex 1.500009961e+04 1.500009961e+04 1.500009961e+04 + vertex 1.500009961e+04 1.500009961e+04 5.000099609e+03 + endloop + endfacet + facet normal -1.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 5.000099609e+03 1.500009961e+04 1.500009961e+04 + vertex 5.000099609e+03 5.000099609e+03 1.500009961e+04 + vertex 5.000099609e+03 5.000099609e+03 5.000099609e+03 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 1.000000000e+00 + outer loop + vertex 1.000010059e+04 1.500009961e+04 1.450009961e+04 + vertex 1.000009863e+04 1.500009961e+04 1.450009961e+04 + vertex 1.000009863e+04 2.000009961e+04 1.450009961e+04 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 -1.000000000e+00 + outer loop + vertex 1.000009863e+04 1.500009961e+04 5.500099609e+03 + vertex 1.000010059e+04 1.500009961e+04 5.500099609e+03 + vertex 1.000010059e+04 2.000009961e+04 5.500099609e+03 + endloop + endfacet + facet normal 1.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.000010059e+04 1.500009961e+04 1.450009961e+04 + vertex 1.000010059e+04 2.000009961e+04 1.450009961e+04 + vertex 1.000010059e+04 2.000009961e+04 5.500099609e+03 + endloop + endfacet + facet normal -1.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.000009863e+04 1.500009961e+04 5.500099609e+03 + vertex 1.000009863e+04 2.000009961e+04 5.500099609e+03 + vertex 1.000009863e+04 2.000009961e+04 1.450009961e+04 + endloop + endfacet + facet normal 0.000000000e+00 1.000000000e+00 0.000000000e+00 + outer loop + vertex 1.000010059e+04 2.000009961e+04 5.500099609e+03 + vertex 1.000010059e+04 2.000009961e+04 1.450009961e+04 + vertex 1.000009863e+04 2.000009961e+04 1.450009961e+04 + endloop + endfacet + facet normal 0.000000000e+00 1.000000000e+00 0.000000000e+00 + outer loop + vertex 1.000010059e+04 1.450009961e+04 5.000099609e+03 + vertex 1.000009863e+04 1.450009961e+04 5.000099609e+03 + vertex 1.000009863e+04 1.450009961e+04 9.960937500e-02 + endloop + endfacet + facet normal 0.000000000e+00 -1.000000000e+00 0.000000000e+00 + outer loop + vertex 1.000009863e+04 5.500099609e+03 5.000099609e+03 + vertex 1.000010059e+04 5.500099609e+03 5.000099609e+03 + vertex 1.000010059e+04 5.500099609e+03 9.960937500e-02 + endloop + endfacet + facet normal 1.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.000010059e+04 1.450009961e+04 5.000099609e+03 + vertex 1.000010059e+04 1.450009961e+04 9.960937500e-02 + vertex 1.000010059e+04 5.500099609e+03 9.960937500e-02 + endloop + endfacet + facet normal -1.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 1.000009863e+04 5.500099609e+03 5.000099609e+03 + vertex 1.000009863e+04 5.500099609e+03 9.960937500e-02 + vertex 1.000009863e+04 1.450009961e+04 9.960937500e-02 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 -1.000000000e+00 + outer loop + vertex 1.000010059e+04 5.500099609e+03 9.960937500e-02 + vertex 1.000010059e+04 1.450009961e+04 9.960937500e-02 + vertex 1.000009863e+04 1.450009961e+04 9.960937500e-02 + endloop + endfacet + facet normal 0.000000000e+00 1.000000000e+00 0.000000000e+00 + outer loop + vertex 1.500009961e+04 1.450009961e+04 1.000010059e+04 + vertex 1.500009961e+04 1.450009961e+04 1.000009863e+04 + vertex 2.000009961e+04 1.450009961e+04 1.000009863e+04 + endloop + endfacet + facet normal 0.000000000e+00 -1.000000000e+00 0.000000000e+00 + outer loop + vertex 1.500009961e+04 5.500099609e+03 1.000009863e+04 + vertex 1.500009961e+04 5.500099609e+03 1.000010059e+04 + vertex 2.000009961e+04 5.500099609e+03 1.000010059e+04 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 1.000000000e+00 + outer loop + vertex 1.500009961e+04 1.450009961e+04 1.000010059e+04 + vertex 2.000009961e+04 1.450009961e+04 1.000010059e+04 + vertex 2.000009961e+04 5.500099609e+03 1.000010059e+04 + endloop + endfacet + facet normal 0.000000000e+00 0.000000000e+00 -1.000000000e+00 + outer loop + vertex 1.500009961e+04 5.500099609e+03 1.000009863e+04 + vertex 2.000009961e+04 5.500099609e+03 1.000009863e+04 + vertex 2.000009961e+04 1.450009961e+04 1.000009863e+04 + endloop + endfacet + facet normal 1.000000000e+00 0.000000000e+00 0.000000000e+00 + outer loop + vertex 2.000009961e+04 5.500099609e+03 1.000010059e+04 + vertex 2.000009961e+04 1.450009961e+04 1.000010059e+04 + vertex 2.000009961e+04 1.450009961e+04 1.000009863e+04 + endloop + endfacet +endsolid diff --git a/src/test/inputs/test_trianglemesh/4486/config_4486.ini b/src/test/inputs/test_trianglemesh/4486/config_4486.ini new file mode 100644 index 000000000..e931e7465 --- /dev/null +++ b/src/test/inputs/test_trianglemesh/4486/config_4486.ini @@ -0,0 +1,172 @@ +# generated by Slic3r 1.3.1-dev on +adaptive_slicing = 0 +adaptive_slicing_quality = 75% +avoid_crossing_perimeters = 0 +bed_shape = 0x0,200x0,200x200,0x200 +bed_temperature = 0 +before_layer_gcode = +between_objects_gcode = +bottom_infill_pattern = rectilinear +bottom_solid_layers = 3 +bridge_acceleration = 0 +bridge_fan_speed = 100 +bridge_flow_ratio = 1 +bridge_speed = 60 +brim_connections_width = 0 +brim_width = 0 +compatible_printers = +complete_objects = 0 +cooling = 1 +default_acceleration = 0 +disable_fan_first_layers = 3 +dont_support_bridges = 1 +duplicate_distance = 6 +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 +external_perimeter_extrusion_width = 0 +external_perimeter_speed = 50% +external_perimeters_first = 0 +extra_perimeters = 1 +extruder_clearance_height = 20 +extruder_clearance_radius = 20 +extruder_offset = 0x0 +extrusion_axis = E +extrusion_multiplier = 1 +extrusion_width = 0 +fan_always_on = 0 +fan_below_layer_time = 60 +filament_colour = #FFFFFF +filament_cost = 0 +filament_density = 0 +filament_diameter = 3 +filament_max_volumetric_speed = 0 +filament_notes = "" +filament_settings_id = +fill_angle = 45 +fill_density = 20% +fill_gaps = 1 +fill_pattern = stars +first_layer_acceleration = 0 +first_layer_bed_temperature = 0 +first_layer_extrusion_width = 200% +first_layer_height = 0.35 +first_layer_speed = 30 +first_layer_temperature = 200 +gap_fill_speed = 20 +gcode_arcs = 0 +gcode_comments = 0 +gcode_flavor = reprap +has_heatbed = 1 +host_type = octoprint +infill_acceleration = 0 +infill_every_layers = 1 +infill_extruder = 1 +infill_extrusion_width = 0 +infill_first = 0 +infill_only_where_needed = 0 +infill_overlap = 55% +infill_speed = 80 +interface_shells = 0 +interior_brim_width = 0 +label_printed_objects = 0 +layer_gcode = +layer_height = 0.3 +match_horizontal_surfaces = 0 +max_fan_speed = 100 +max_layer_height = 0.3 +max_print_speed = 80 +max_volumetric_speed = 0 +min_fan_speed = 35 +min_layer_height = 0.15 +min_print_speed = 10 +min_shell_thickness = 0 +min_skirt_length = 0 +min_top_bottom_shell_thickness = 0 +notes = +nozzle_diameter = 0.5 +octoprint_apikey = +only_retract_when_crossing_perimeters = 1 +ooze_prevention = 0 +output_filename_format = [input_filename_base].gcode +overhangs = 1 +perimeter_acceleration = 0 +perimeter_extruder = 1 +perimeter_extrusion_width = 0 +perimeter_speed = 60 +perimeters = 3 +post_process = +pressure_advance = 0 +print_host = +print_settings_id = +printer_notes = +printer_settings_id = +raft_layers = 0 +regions_overlap = 0 +resolution = 0 +retract_before_travel = 2 +retract_layer_change = 0 +retract_length = 2 +retract_length_toolchange = 10 +retract_lift = 0 +retract_lift_above = 0 +retract_lift_below = 0 +retract_restart_extra = 0 +retract_restart_extra_toolchange = 0 +retract_speed = 40 +seam_position = aligned +sequential_print_priority = 0 +serial_port = +serial_speed = 250000 +shortcuts = support_material +skirt_distance = 6 +skirt_height = 1 +skirts = 1 +slowdown_below_layer_time = 5 +small_perimeter_speed = 15 +solid_infill_below_area = 70 +solid_infill_every_layers = 0 +solid_infill_extruder = 1 +solid_infill_extrusion_width = 0 +solid_infill_speed = 20 +spiral_vase = 0 +standby_temperature_delta = -5 +start_filament_gcode = "; Filament gcode\n" +start_gcode = G28 ; home all axes\nG1 Z5 F5000 ; lift nozzle\n +support_material = 0 +support_material_angle = 0 +support_material_buildplate_only = 0 +support_material_contact_distance = 0.2 +support_material_enforce_layers = 0 +support_material_extruder = 1 +support_material_extrusion_width = 0 +support_material_interface_extruder = 1 +support_material_interface_extrusion_width = 0 +support_material_interface_layers = 3 +support_material_interface_spacing = 0 +support_material_interface_speed = 100% +support_material_max_layers = 0 +support_material_pattern = pillars +support_material_pillar_size = 2.5 +support_material_pillar_spacing = 10 +support_material_spacing = 2.5 +support_material_speed = 60 +support_material_threshold = 60% +temperature = 200 +thin_walls = 1 +threads = 4 +toolchange_gcode = +top_infill_extrusion_width = 0 +top_infill_pattern = rectilinear +top_solid_infill_speed = 15 +top_solid_layers = 3 +travel_speed = 130 +use_firmware_retraction = 0 +use_relative_e_distances = 0 +use_set_and_wait_bed = 0 +use_set_and_wait_extruder = 0 +use_volumetric_e = 0 +vibration_limit = 0 +wipe = 0 +xy_size_compensation = 0 +z_offset = 0 +z_steps_per_mm = 0 diff --git a/src/test/libslic3r/test_config.cpp b/src/test/libslic3r/test_config.cpp new file mode 100644 index 000000000..d3cc888a0 --- /dev/null +++ b/src/test/libslic3r/test_config.cpp @@ -0,0 +1,214 @@ +#include + +#include "../../libslic3r/Config.hpp" +#include "../test_options.hpp" + +#include +// +//using namespace Slic3r; +//using namespace std::literals::string_literals; +// +//SCENARIO("Generic config validation performs as expected.") { +// GIVEN("A config generated from default options") { +// auto config {Slic3r::DynamicPrintConfig::new_from_defaults()}; +// WHEN( "perimeter_extrusion_width is set to 250%, a valid value") { +// config->set("perimeter_extrusion_width", "250%"); +// THEN( "The config is read as valid.") { +// REQUIRE_NOTHROW(config->validate()); +// } +// } +// WHEN( "perimeter_extrusion_width is set to -10, an invalid value") { +// config->set("perimeter_extrusion_width", -10); +// THEN( "An InvalidOptionException exception is thrown.") { +// auto except_thrown {false}; +// try { +// config->validate(); +// } catch (const InvalidOptionException& e) { +// except_thrown = true; +// } +// REQUIRE(except_thrown == true); +// } +// } +// +// WHEN( "perimeters is set to -10, an invalid value") { +// config->set("perimeters", -10); +// THEN( "An InvalidOptionException exception is thrown.") { +// auto except_thrown {false}; +// try { +// config->validate(); +// } catch (const InvalidOptionException& e) { +// except_thrown = true; +// } +// REQUIRE(except_thrown == true); +// } +// } +// } +//} +// +//SCENARIO("Config accessor functions perform as expected.") { +// GIVEN("A config generated from default options") { +// auto config {Slic3r::DynamicPrintConfig::new_from_defaults()}; +// WHEN("A boolean option is set to a boolean value") { +// REQUIRE_NOTHROW(config->set("gcode_comments", true)); +// THEN("The underlying value is set correctly.") { +// REQUIRE(config->get("gcode_comments").getBool() == true); +// } +// } +// WHEN("A boolean option is set to a string value representing a 0 or 1") { +// CHECK_NOTHROW(config->set("gcode_comments", "1")); +// THEN("The underlying value is set correctly.") { +// REQUIRE(config->get("gcode_comments").getBool() == true); +// } +// } +// WHEN("A boolean option is set to a string value representing something other than 0 or 1") { +// THEN("A BadOptionTypeException exception is thrown.") { +// REQUIRE_THROWS_AS(config->set("gcode_comments", "Z"), BadOptionTypeException); +// } +// AND_THEN("Value is unchanged.") { +// REQUIRE(config->get("gcode_comments").getBool() == false); +// } +// } +// WHEN("A string option is set to an int value") { +// THEN("A BadOptionTypeException exception is thrown.") { +// REQUIRE_THROWS_AS(config->set("gcode_comments", 1), BadOptionTypeException); +// } +// } +// WHEN("A numeric option is set from serialized string") { +// config->set("bed_temperature", "100"); +// THEN("The underlying value is set correctly.") { +// REQUIRE(config->get("bed_temperature").getInt() == 100); +// } +// } +// +// WHEN("An integer-based option is set through the integer interface") { +// config->set("bed_temperature", 100); +// THEN("The underlying value is set correctly.") { +// REQUIRE(config->get("bed_temperature").getInt() == 100); +// } +// } +// WHEN("An floating-point option is set through the integer interface") { +// config->set("perimeter_speed", 10); +// THEN("The underlying value is set correctly.") { +// REQUIRE(config->get("perimeter_speed").getFloat() == 10.0); +// } +// } +// WHEN("A floating-point option is set through the double interface") { +// config->set("perimeter_speed", 5.5); +// THEN("The underlying value is set correctly.") { +// REQUIRE(config->get("perimeter_speed").getFloat() == 5.5); +// } +// } +// WHEN("An integer-based option is set through the double interface") { +// THEN("A BadOptionTypeException exception is thrown.") { +// REQUIRE_THROWS_AS(config->set("bed_temperature", 5.5), BadOptionTypeException); +// } +// } +// WHEN("A numeric option is set to a non-numeric value.") { +// THEN("A BadOptionTypeException exception is thown.") { +// REQUIRE_THROWS_AS(config->set("perimeter_speed", "zzzz"), BadOptionTypeException); +// } +// THEN("The value does not change.") { +// REQUIRE(config->get("perimeter_speed").getFloat() == 60.0); +// } +// } +// WHEN("A string option is set through the string interface") { +// config->set("octoprint_apikey", "100"); +// THEN("The underlying value is set correctly.") { +// REQUIRE(config->get("octoprint_apikey").getString() == "100"); +// } +// } +// WHEN("A string option is set through the integer interface") { +// config->set("octoprint_apikey", 100); +// THEN("The underlying value is set correctly.") { +// REQUIRE(config->get("octoprint_apikey").getString() == "100"); +// } +// } +// WHEN("A string option is set through the double interface") { +// config->set("octoprint_apikey", 100.5); +// THEN("The underlying value is set correctly.") { +// REQUIRE(config->get("octoprint_apikey").getString() == std::to_string(100.5)); +// } +// } +// WHEN("A float or percent is set as a percent through the string interface.") { +// config->set("first_layer_extrusion_width", "100%"); +// THEN("Value and percent flag are 100/true") { +// auto tmp {config->get("first_layer_extrusion_width")}; +// REQUIRE(tmp.percent == true); +// REQUIRE(tmp.value == 100); +// } +// } +// WHEN("A float or percent is set as a float through the string interface.") { +// config->set("first_layer_extrusion_width", "100"); +// THEN("Value and percent flag are 100/false") { +// auto tmp {config->get("first_layer_extrusion_width")}; +// REQUIRE(tmp.percent == false); +// REQUIRE(tmp.value == 100); +// } +// } +// WHEN("A float or percent is set as a float through the int interface.") { +// config->set("first_layer_extrusion_width", 100); +// THEN("Value and percent flag are 100/false") { +// auto tmp {config->get("first_layer_extrusion_width")}; +// REQUIRE(tmp.percent == false); +// REQUIRE(tmp.value == 100); +// } +// } +// WHEN("A float or percent is set as a float through the double interface.") { +// config->set("first_layer_extrusion_width", 100.5); +// THEN("Value and percent flag are 100.5/false") { +// auto tmp {config->get("first_layer_extrusion_width")}; +// REQUIRE(tmp.percent == false); +// REQUIRE(tmp.value == 100.5); +// } +// } +// WHEN("An invalid option is requested during set.") { +// THEN("A BadOptionTypeException exception is thrown.") { +// REQUIRE_THROWS_AS(config->set("deadbeef_invalid_option", 1), UnknownOptionException); +// REQUIRE_THROWS_AS(config->set("deadbeef_invalid_option", 1.0), UnknownOptionException); +// REQUIRE_THROWS_AS(config->set("deadbeef_invalid_option", "1"), UnknownOptionException); +// REQUIRE_THROWS_AS(config->set("deadbeef_invalid_option", true), UnknownOptionException); +// } +// } +// +// WHEN("An invalid option is requested during get.") { +// THEN("A UnknownOptionException exception is thrown.") { +// REQUIRE_THROWS_AS(config->get("deadbeef_invalid_option", false), UnknownOptionException); +// REQUIRE_THROWS_AS(config->get("deadbeef_invalid_option", false), UnknownOptionException); +// REQUIRE_THROWS_AS(config->get("deadbeef_invalid_option", false), UnknownOptionException); +// REQUIRE_THROWS_AS(config->get("deadbeef_invalid_option", false), UnknownOptionException); +// } +// } +// WHEN("An invalid option is requested during get_ptr.") { +// THEN("A UnknownOptionException exception is thrown.") { +// REQUIRE_THROWS_AS(config->get_ptr("deadbeef_invalid_option", false), UnknownOptionException); +// REQUIRE_THROWS_AS(config->get_ptr("deadbeef_invalid_option", false), UnknownOptionException); +// REQUIRE_THROWS_AS(config->get_ptr("deadbeef_invalid_option", false), UnknownOptionException); +// REQUIRE_THROWS_AS(config->get_ptr("deadbeef_invalid_option", false), UnknownOptionException); +// } +// } +// +// WHEN("getX called on an unset option.") { +// THEN("The default is returned.") { +// REQUIRE(config->getFloat("layer_height") == 0.3); +// REQUIRE(config->getInt("raft_layers") == 0); +// REQUIRE(config->getBool("support_material") == false); +// } +// } +// +// WHEN("getFloat called on an option that has been set.") { +// config->set("layer_height", 0.5); +// THEN("The set value is returned.") { +// REQUIRE(config->getFloat("layer_height") == 0.5); +// } +// } +// } +//} +// +//SCENARIO("Config ini load/save interface", "[!mayfail]") { +// WHEN("new_from_ini is called") { +// auto config {Slic3r::DynamicPrintConfig::new_from_ini(std::string(testfile_dir) + "test_config/new_from_ini.ini"s) }; +// THEN("Config object contains ini file options.") { +// } +// } +// REQUIRE(false); +//} diff --git a/src/test/libslic3r/test_fill.cpp b/src/test/libslic3r/test_fill.cpp new file mode 100644 index 000000000..17ef2d60a --- /dev/null +++ b/src/test/libslic3r/test_fill.cpp @@ -0,0 +1,484 @@ +#include +#include "../test_data.hpp" +#include "../../libslic3r/Fill/Fill.hpp" +#include "../../libslic3r/Print.hpp" +#include "../../libslic3r/Geometry.hpp" +#include "../../libslic3r/Flow.hpp" +#include "../../libslic3r/ClipperUtils.hpp" +#include "../../libslic3r/SVG.hpp" + +using namespace Slic3r; +using namespace Slic3r::Geometry; + +bool test_if_solid_surface_filled(const ExPolygon& expolygon, double flow_spacing, double angle = 0, double density = 1.0); + +//TEST_CASE("Fill: adjusted solid distance") { +// Print print; +// int surface_width {250}; +// +// int distance {Slic3r::Flow::solid_spacing(surface_width, 47)}; +// +// REQUIRE(distance == Approx(50)); +// REQUIRE(surface_width % distance == 0); +//} +Polylines test(const ExPolygon& poly, Fill &filler, const FillParams ¶ms){ + std::cout << "don't connect? " << (params.dont_connect ? "true" : "false") << "\n"; + Surface surface{ Slic3r::Surface((stPosTop | stDensSolid), poly) }; + return filler.fill_surface(&surface, params); +} + +TEST_CASE("Fill: Pattern Path Length") { + Fill* filler = {Slic3r::Fill::new_from_type("rectilinear")}; + filler->angle = -(PI) / 2.0; + filler->spacing = 5; + FillParams params; + params.dont_adjust = true; + params.density = filler->spacing / 50.0; + //params.endpoints_overlap = false; + + + + SECTION("Square") { + Points test_set; + test_set.reserve(4); + Points points {Point(0,0), Point(100,0), Point(100,100), Point(0,100)}; + for (size_t i = 0; i < 4; ++i) { + std::transform(points.cbegin()+i, points.cend(), std::back_inserter(test_set), [] (const Point& a) -> Point { return Point::new_scale(a); } ); + std::transform(points.cbegin(), points.cbegin()+i, std::back_inserter(test_set), [] (const Point& a) -> Point { return Point::new_scale(a); } ); + Slic3r::ExPolygon expoly; + expoly.contour = Slic3r::Polygon(test_set); + Polylines paths {test(expoly, *filler, params)}; + 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(scale_(3*100 + 2*50))) - SCALED_EPSILON > 0); // path has expected length + + test_set.clear(); + } + } + SECTION("Diamond with endpoints on grid") { + Points points {Point(0,0), Point(100,0), Point(150,50), Point(100,100), Point(0,100), Point(-50,50)}; + Points test_set; + test_set.reserve(6); + std::transform(points.cbegin(), points.cend(), std::back_inserter(test_set), [] (const Point& a) -> Point { return Point::new_scale(a); } ); + Slic3r::ExPolygon expoly; + expoly.contour = Slic3r::Polygon(test_set); + Polylines paths {test(expoly, *filler, params)}; + REQUIRE(paths.size() == 1); // one continuous path + } + + SECTION("Square with hole") { + Points square { Point(0,0), Point(100,0), Point(100,100), Point(0,100)}; + Points hole {Point(25,25), Point(75,25), Point(75,75), Point(25,75) }; + std::reverse(hole.begin(), hole.end()); + + Points test_hole; + Points test_square; + + std::transform(square.cbegin(), square.cend(), std::back_inserter(test_square), [] (const Point& a) -> Point { return Point::new_scale(a); } ); + std::transform(hole.cbegin(), hole.cend(), std::back_inserter(test_hole), [] (const Point& a) -> Point { return Point::new_scale(a); } ); + + 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}) { + FillParams params_local = params; + params_local.density = filler->spacing / spacing; + filler->angle = angle; + Slic3r::ExPolygon e; + e.contour = Slic3r::Polygon(test_square); + e.holes = Slic3r::Polygons(Slic3r::Polygon(test_hole)); + Polylines paths {test(e, *filler, params_local)}; + std::cout << "paths.size="<= 2 && 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") { + FillParams params_local = params; + Fill* filler_local = { Slic3r::Fill::new_from_type("rectilinear") }; + filler_local->angle = (PI/4.0); + params_local.dont_adjust = false; + filler_local->spacing = 0.654498; + //filler_local->endpoints_overlap = unscale(359974); + params_local.density = 1; + filler_local->layer_id = 66; + filler_local->z = 20.15; + + Points points {Point(25771516,14142125),Point(14142138,25771515),Point(2512749,14142131),Point(14142125,2512749)}; + Slic3r::ExPolygon expoly; + expoly.contour = Slic3r::Polygon(points); + Polylines paths {test(expoly, *filler_local, params_local)}; + 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(scale_(3*100 + 2*50))) - SCALED_EPSILON > 0); // path has expected length + } + + SECTION("Rotated Square") { + Points square { Point::new_scale(0,0), Point::new_scale(50,0), Point::new_scale(50,50), Point::new_scale(0,50)}; + ExPolygon expolygon; + expolygon.contour = Slic3r::Polygon(square); + auto filler {Slic3r::Fill::new_from_type("rectilinear")}; + filler->bounding_box = expolygon.contour.bounding_box(); + filler->angle = 0; + + auto surface {Surface((stPosTop|stDensSolid), expolygon)}; + auto flow {Slic3r::Flow(0.69, 0.4, 0.50)}; + + filler->spacing = flow.spacing(); + params.density = 1.0; + + for (auto angle : { 0.0, 45.0}) { + surface.expolygon.rotate(angle, Point(0,0)); + auto paths {filler->fill_surface(&surface, params)}; + REQUIRE(paths.size() == 1); + } + } + SECTION("Solid surface fill") { + 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; + expolygon.contour = Slic3r::Polygon(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") { + Points points { + Point(59515297,5422499),Point(59531249,5578697),Point(59695801,6123186), + Point(59965713,6630228),Point(60328214,7070685),Point(60773285,7434379), + Point(61274561,7702115),Point(61819378,7866770),Point(62390306,7924789), + Point(62958700,7866744),Point(63503012,7702244),Point(64007365,7434357), + Point(64449960,7070398),Point(64809327,6634999),Point(65082143,6123325), + Point(65245005,5584454),Point(65266967,5422499),Point(66267307,5422499), + Point(66269190,8310081),Point(66275379,17810072),Point(66277259,20697500), + Point(65267237,20697500),Point(65245004,20533538),Point(65082082,19994444), + Point(64811462,19488579),Point(64450624,19048208),Point(64012101,18686514), + Point(63503122,18415781),Point(62959151,18251378),Point(62453416,18198442), + Point(62390147,18197355),Point(62200087,18200576),Point(61813519,18252990), + Point(61274433,18415918),Point(60768598,18686517),Point(60327567,19047892), + Point(59963609,19493297),Point(59695865,19994587),Point(59531222,20539379), + Point(59515153,20697500),Point(58502480,20697500),Point(58502480,5422499) + }; + Slic3r::ExPolygon expolygon; + expolygon.contour = Slic3r::Polygon(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") { + 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; + expolygon.contour = Slic3r::Polygon(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'; +} +*/ + +//TODO: also check by volume extruded +//TODO: replace the simple area coverage check by one that takes into account the width of the path, not only the default flow spacing +//TODO: test more fills +bool test_if_solid_surface_filled(const ExPolygon& expolygon, double flow_width, double angle, double density) { + auto* filler {Slic3r::Fill::new_from_type("concentricgapfill")}; + filler->bounding_box = expolygon.contour.bounding_box(); + filler->angle = angle; + FillParams params; + params.dont_adjust = false; + + Surface surface((stPosBottom | stDensSolid), expolygon); + //note: here we do flow.width = flow_width , flow.gheight = 0.4, flow.nozzle_size = flow_width; + Flow flow(flow_width, 0.4, flow_width); + + filler->spacing = flow.spacing(); + params.density = density; + + Polylines paths {filler->fill_surface(&surface, 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 + std::for_each(paths.begin(), paths.end(), [filler, &grown_paths] (const Slic3r::Polyline& p) { + polygons_append(grown_paths, offset(p, scale_(filler->spacing / 2.0))); + }); + + ExPolygons uncovered = diff_ex(expolygon, grown_paths, true); + + // ignore very small dots + const auto scaled_flow_width { std::pow(scale_(flow_width), 2) }; + auto iter {std::remove_if(uncovered.begin(), uncovered.end(), [scaled_flow_width] (const ExPolygon& poly) { + return poly.area() > scaled_flow_width; + }) }; + uncovered.erase(iter, uncovered.end()); + + double uncovered_area = 0; + for (ExPolygon &p : uncovered) uncovered_area += unscaled(unscaled(p.area())); + std::cout << "uncovered size =" << uncovered_area << " / "<< unscaled(unscaled(expolygon.area()))<<"\n"; + return uncovered.size() == 0; // solid surface is fully filled + +} diff --git a/src/test/libslic3r/test_flow.cpp b/src/test/libslic3r/test_flow.cpp new file mode 100644 index 000000000..65f0287aa --- /dev/null +++ b/src/test/libslic3r/test_flow.cpp @@ -0,0 +1,220 @@ +#include + +#include +#include + +#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::new_from_defaults()}; + config->set_key_value("skirts", new ConfigOptionInt(1)); + config->set_key_value("brim_width", new ConfigOptionFloat(2)); + config->set_key_value("perimeters", new ConfigOptionInt(3)); + config->set_key_value("fill_density", new ConfigOptionPercent(40)); + config->set_key_value("first_layer_height", new ConfigOptionFloatOrPercent(100, true)); + + WHEN("first layer width set to 2mm") { + Slic3r::Model model; + config->set_key_value("first_layer_extrusion_width", new ConfigOptionFloatOrPercent(2.0, false)); + Print print; + Slic3r::Test::init_print(print, { TestMesh::cube_20x20x20 }, model, config); + //std::cout << "model pos: " << model.objects.front()->instances.front()->get_offset().x() << ": " << model.objects.front()->instances.front()->get_offset().x() << "\n"; + //Print print; + //for (auto* mo : model.objects) + // print.auto_assign_extruders(mo); + //print.apply(model, *config); + ////std::cout << "print volume: " << print.<< ": " << model.objects().front()->copies().front().x() << "\n"; + //std::string err = print.validate(); + + std::vector E_per_mm_bottom; + std::string gcode_filepath(""); + Slic3r::Test::gcode(gcode_filepath, print); + GCodeReader parser {Slic3r::GCodeReader()}; + const auto layer_height { config->opt_float("layer_height") }; + std::string gcode_from_file{ read_to_string(gcode_filepath) }; + parser.parse_buffer(gcode_from_file, [&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 + std::cout << "line on first layer : " << line.raw()<<"\n"; + if (line.extruding(self) && line.dist_XY(self) > 0) { + std::cout << "line sextrusion : " << line.dist_E(self) << "\n"; + 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; + auto avg_E {std::accumulate(E_per_mm_bottom.cbegin(), E_per_mm_bottom.cend(), 0.0) / static_cast(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.") { + } + clean_file(gcode_filepath, "gcode"); + } + } +} +// 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") { + auto width {ConfigOptionFloatOrPercent(1.0, false)}; + float spacing {0.4f}; + float nozzle_diameter {0.4f}; + float bridge_flow {1.0f}; + float layer_height {0.5f}; + + // Spacing for non-bridges is has some overlap + THEN("External perimeter flow has a default spacing fixed to 1.05*nozzle_diameter") { + Flow flow {Flow::new_from_config_width(frExternalPerimeter, ConfigOptionFloatOrPercent(0, false), nozzle_diameter, layer_height, 0.0f)}; + REQUIRE(flow.spacing() == Approx((1.05f*nozzle_diameter) - layer_height * (1.0 - PI / 4.0))); + } + + THEN("Internal perimeter flow has a default spacing fixed to 1.125*nozzle_diameter") { + Flow flow {Flow::new_from_config_width(frPerimeter, ConfigOptionFloatOrPercent(0, false), nozzle_diameter, layer_height, 0.0f)}; + REQUIRE(flow.spacing() == Approx((1.125*nozzle_diameter) - layer_height * (1.0 - PI / 4.0))); + } + THEN("Spacing for supplied width is 0.8927f") { + Flow flow {Flow::new_from_config_width(frExternalPerimeter, width, nozzle_diameter, layer_height, 0.0f)}; + REQUIRE(flow.spacing() == Approx(width - 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 - layer_height * (1.0 - PI / 4.0))); + } + } + /// Check the min/max + GIVEN("Nozzle Diameter of 0.25 with extreme width") { + float nozzle_diameter {0.25f}; + float layer_height {0.5f}; + WHEN("layer height is set to 0.15") { + layer_height = 5.f; + THEN("Max width is respected.") { + 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)); + } + THEN("Min width is respected") { + 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)); + } + } + WHEN("Layer height is set to 0.3") { + layer_height = 0.01f; + THEN("Max width is respected.") { + 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)); + } + THEN("Min width is respected.") { + 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)); + } + } + } + + + ///// 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); + // } + // } + //} + +} + +/// 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)}; + auto spacing {0.4}; + auto nozzle_diameter {0.4}; + auto bridge_flow {1.0}; + auto 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/src/test/libslic3r/test_gcodewriter.cpp b/src/test/libslic3r/test_gcodewriter.cpp new file mode 100644 index 000000000..1273a5bc1 --- /dev/null +++ b/src/test/libslic3r/test_gcodewriter.cpp @@ -0,0 +1,100 @@ +#include +#include + +#include "GCodeWriter.hpp" +#include "test_options.hpp" + +using namespace Slic3r; +using namespace std::literals::string_literals; + +SCENARIO("lift() and unlift() behavior with large values of Z", "[!shouldfail]") { + GIVEN("A config from a file and a single extruder.") { + GCodeWriter writer; + auto& config {writer.config}; + config.set_defaults(); + config.load(std::string(testfile_dir) + "test_gcodewriter/config_lift_unlift.ini"s); + + std::vector 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; + auto& config {writer.config}; + config.set_defaults(); + config.load(std::string(testfile_dir) + "test_gcodewriter/config_lift_unlift.ini"s); + + std::vector 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); + } + } + } + } + } + } +} diff --git a/src/test/libslic3r/test_geometry.cpp b/src/test/libslic3r/test_geometry.cpp new file mode 100644 index 000000000..a65e85313 --- /dev/null +++ b/src/test/libslic3r/test_geometry.cpp @@ -0,0 +1,378 @@ + +#include + +#include "Point.hpp" +#include "BoundingBox.hpp" +#include "Polygon.hpp" +#include "Polyline.hpp" +#include "Line.hpp" +#include "Geometry.hpp" +#include "ClipperUtils.hpp" + +using namespace Slic3r; + +TEST_CASE("Polygon::contains works properly", ""){ + // this test was failing on Windows (GH #1950) + auto polygon = Polygon(std::vector({ + 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) + })); + auto point = Point(95706562, -57294774); + REQUIRE(polygon.contains(point)); +} + +SCENARIO("Intersections of line segments"){ + GIVEN("Integer coordinates"){ + auto line1 = Line(Point(5,15),Point(30,15)); + auto line2 = Line(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"){ + auto line1 = Line(Point(73.6310778185108/0.0000001, 371.74239268924/0.0000001), Point(73.6310778185108/0.0000001, 501.74239268924/0.0000001)); + auto line2 = Line(Point(75/0.0000001, 437.9853/0.0000001), Point(62.7484/0.0000001, 440.4223/0.0000001)); + 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"){ + auto polyline = Polyline(); + polyline.points = std::vector({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"){ + auto polygon = Polygon(std::vector({Point(0, 0), Point(10, 0), Point(5, 5)})); + auto 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"){ + auto bb = BoundingBox(std::vector({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"){ + auto line = Line(Point(10,10), Point(20,10)); + Polyline tmp(line); + Polygon area = offset(tmp,5).at(0); + REQUIRE(area.area() == Polygon(std::vector({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 Pointfs arranged in a half-circle with approximately the same distance R from some point") { + Pointf expected_center(-6, 0); + Pointfs sample {Pointf(6.0, 0), Pointf(5.1961524, 3), Pointf(3 ,5.1961524), Pointf(0, 6.0), Pointf(3, 5.1961524), Pointf(-5.1961524, 3), Pointf(-6.0, 0)}; + std::transform(sample.begin(), sample.end(), sample.begin(), [expected_center] (const Pointf& a) { return a + expected_center;}); + + WHEN("Circle fit is called on the entire array") { + Pointf result_center(0,0); + result_center = Geometry::circle_taubin_newton(sample); + THEN("A center point of -6,0 is returned.") { + REQUIRE(result_center == expected_center); + } + } + WHEN("Circle fit is called on the first four points") { + Pointf 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(result_center == expected_center); + } + } + WHEN("Circle fit is called on the middle four points") { + Pointf 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(result_center == expected_center); + } + } + } + GIVEN("A vector of Pointfs arranged in a half-circle with approximately the same distance R from some point") { + Pointf expected_center(-3, 9); + Pointfs sample {Pointf(6.0, 0), Pointf(5.1961524, 3), Pointf(3 ,5.1961524), + Pointf(0, 6.0), + Pointf(3, 5.1961524), Pointf(-5.1961524, 3), Pointf(-6.0, 0)}; + + std::transform(sample.begin(), sample.end(), sample.begin(), [expected_center] (const Pointf& a) { return a + expected_center;}); + + + WHEN("Circle fit is called on the entire array") { + Pointf result_center(0,0); + result_center = Geometry::circle_taubin_newton(sample); + THEN("A center point of 3,9 is returned.") { + REQUIRE(result_center == expected_center); + } + } + WHEN("Circle fit is called on the first four points") { + Pointf 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(result_center == expected_center); + } + } + WHEN("Circle fit is called on the middle four points") { + Pointf 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(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(result_center.coincides_with_epsilon(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(result_center.coincides_with_epsilon(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(result_center.coincides_with_epsilon(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 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 indices; + Geometry::chained_path(points,indices); + for(Points::size_type i = 0; i < indices.size()-1;i++){ + double dist = points.at(indices.at(i)).distance_to(points.at(indices.at(i+1))); + REQUIRE(abs(dist-26) <= Geometry::epsilon); + } +} + +SCENARIO("Line distances"){ + GIVEN("A line"){ + auto line = Line(Point(0, 0), Point(20, 0)); + THEN("Points on the line segment have 0 distance"){ + REQUIRE(Point(0, 0).distance_to(line) == 0); + REQUIRE(Point(20, 0).distance_to(line) == 0); + REQUIRE(Point(10, 0).distance_to(line) == 0); + + } + THEN("Points off the line have the appropriate distance"){ + REQUIRE(Point(10, 10).distance_to(line) == 10); + REQUIRE(Point(50, 0).distance_to(line) == 30); + } + } +} + +SCENARIO("Polygon convex/concave detection"){ + GIVEN(("A Square with dimension 100")){ + auto square = Polygon /*new_scale*/(std::vector({ + 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 = Polygon /*new_scale*/(std::vector({ + 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 = Polygon /*new_scale*/(std::vector({ + 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 = Polygon(std::vector({ + 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 = Polygon(std::vector({ + 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 = Polygon(std::vector({ + 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 = Polygon(std::vector({ + Point(16000170,26257364), Point(714223,461012), Point(31286371,461008) + })); + REQUIRE(triangle.simplify(250000).at(0).points.size() == 3); +} + + diff --git a/src/test/libslic3r/test_log.cpp b/src/test/libslic3r/test_log.cpp new file mode 100644 index 000000000..f502f96f2 --- /dev/null +++ b/src/test/libslic3r/test_log.cpp @@ -0,0 +1,695 @@ +#include +#include +#include "Log.hpp" + +using namespace std::literals::string_literals; +using namespace Slic3r; + +SCENARIO( "_Log output with std::string methods" ) { + GIVEN("A log stream and a _Log object") { + std::stringstream log; + std::unique_ptr<_Log> cut { _Log::make_log(log) }; + cut->set_level(log_t::DEBUG); + cut->set_inclusive(true); + WHEN("fatal_error is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->fatal_error("Topic", "This"); + THEN("Output string is Topic FERR: This\\n") { + REQUIRE(log.str() == "Topic FERR: This\n"); + } + } + WHEN("error is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->error("Topic", "This"); + THEN("Output string is Topic ERR: This\\n") { + REQUIRE(log.str() == "Topic ERR: This\n"); + } + } + WHEN("info is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->info("Topic", "This"); + THEN("Output string is Topic INFO: This\\n") { + REQUIRE(log.str() == "Topic INFO: This\n"); + } + } + WHEN("warn is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->warn("Topic", "This"); + THEN("Output string is Topic WARN: This\\n") { + REQUIRE(log.str() == "Topic WARN: This\n"); + } + } + WHEN("info is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->info("Topic", "This"); + THEN("Output string is Topic INFO: This\\n") { + REQUIRE(log.str() == "Topic INFO: This\n"); + } + } + WHEN("debug is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->debug("Topic", "This"); + THEN("Output string is Topic DEBUG: This\\n") { + REQUIRE(log.str() == "Topic DEBUG: This\n"); + } + } + WHEN("msg is called with text \"This\"") { + log.clear(); + cut->raw("This"); + THEN("Output string is This\\n") { + REQUIRE(log.str() == "This\n"); + } + } + } +} +SCENARIO( "_Log output with std::wstring methods" ) { + GIVEN("A log stream and a _Log object") { + std::stringstream log; + std::unique_ptr<_Log> cut { _Log::make_log(log) }; + cut->set_level(log_t::DEBUG); + cut->set_inclusive(true); + WHEN("fatal_error is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->fatal_error("Topic", L"This"); + THEN("Output string is Topic FERR: This\\n") { + REQUIRE(log.str() == "Topic FERR: This\n"); + } + } + WHEN("error is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->error("Topic", L"This"); + THEN("Output string is Topic ERR: This\\n") { + REQUIRE(log.str() == "Topic ERR: This\n"); + } + } + WHEN("info is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->info("Topic", L"This"); + THEN("Output string is Topic INFO: This\\n") { + REQUIRE(log.str() == "Topic INFO: This\n"); + } + } + WHEN("warn is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->warn("Topic", L"This"); + THEN("Output string is Topic WARN: This\\n") { + REQUIRE(log.str() == "Topic WARN: This\n"); + } + } + WHEN("info is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->info("Topic", L"This"); + THEN("Output string is Topic INFO: This\\n") { + REQUIRE(log.str() == "Topic INFO: This\n"); + } + } + WHEN("debug is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->debug("Topic", L"This"); + THEN("Output string is Topic DEBUG: This\\n") { + REQUIRE(log.str() == "Topic DEBUG: This\n"); + } + } + WHEN("msg is called with text \"This\"") { + log.clear(); + cut->raw(L"This"); + THEN("Output string is This\\n") { + REQUIRE(log.str() == "This\n"); + } + } + } +} + +SCENARIO( "_Log output with << methods" ) { + GIVEN("A log stream and a _Log object") { + std::stringstream log; + std::unique_ptr<_Log> cut { _Log::make_log(log) }; + cut->set_level(log_t::DEBUG); + cut->set_inclusive(true); + WHEN("fatal_error is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->fatal_error("Topic") << "This"; + THEN("Output string is Topic FERR: This") { + REQUIRE(log.str() == "Topic FERR: This"); + } + } + WHEN("error is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->error("Topic") << "This"; + THEN("Output string is Topic ERR: This") { + REQUIRE(log.str() == "Topic ERR: This"); + } + } + WHEN("info is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->info("Topic") << "This"; + THEN("Output string is Topic INFO: This") { + REQUIRE(log.str() == "Topic INFO: This"); + } + } + WHEN("warn is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->warn("Topic") << "This"; + THEN("Output string is Topic WARN: This") { + REQUIRE(log.str() == "Topic WARN: This"); + } + } + WHEN("info is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->info("Topic") << "This"; + THEN("Output string is Topic INFO: This") { + REQUIRE(log.str() == "Topic INFO: This"); + } + } + WHEN("debug is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->debug("Topic") << "This"; + THEN("Output string is Topic DEBUG: This") { + REQUIRE(log.str() == "Topic DEBUG: This"); + } + } + WHEN("msg is called with text \"This\"") { + log.clear(); + cut->raw() << "This"; + THEN("Output string is This") { + REQUIRE(log.str() == "This"); + } + } + + } +} + +SCENARIO( "_Log output inclusive filtering with std::string methods" ) { + GIVEN("Single, inclusive log level of FERR (highest)") { + std::stringstream log; + std::unique_ptr<_Log> cut { _Log::make_log(log) }; + cut->clear_level(log_t::FERR); + cut->set_inclusive(true); + cut->set_level(log_t::FERR); + WHEN("fatal_error is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->fatal_error("Topic", "This"); + THEN("Output string is Topic FERR: This\\n") { + REQUIRE(log.str() == "Topic FERR: This\n"); + } + } + WHEN("error is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->error("Topic", "This"); + THEN("Output string is blank") { + REQUIRE(log.str() == ""); + } + } + WHEN("warn is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->warn("Topic", "This"); + THEN("Output string is blank") { + REQUIRE(log.str() == ""); + } + } + WHEN("info is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->info("Topic", "This"); + THEN("Output string is blank") { + REQUIRE(log.str() == ""); + } + } + WHEN("debug is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->debug("Topic", "This"); + THEN("Output string is blank") { + REQUIRE(log.str() == ""); + } + } + } + GIVEN("Single, inclusive log level of ERR (second-highest)") { + std::stringstream log; + std::unique_ptr<_Log> cut { _Log::make_log(log) }; + cut->set_inclusive(true); + cut->set_level(log_t::ERR); + WHEN("fatal_error is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->fatal_error("Topic", "This"); + THEN("Output string is Topic FERR: This\\n") { + REQUIRE(log.str() == "Topic FERR: This\n"); + } + } + WHEN("error is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->error("Topic", "This"); + THEN("Output string is Topic ERR: This\\n") { + REQUIRE(log.str() == "Topic ERR: This\n"); + } + } + WHEN("warn is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->warn("Topic", "This"); + THEN("Output string is blank") { + REQUIRE(log.str() == ""); + } + } + WHEN("info is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->info("Topic", "This"); + THEN("Output string is blank") { + REQUIRE(log.str() == ""); + } + } + WHEN("debug is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->debug("Topic", "This"); + THEN("Output string is blank") { + REQUIRE(log.str() == ""); + } + } + } + GIVEN("Single, inclusive log level of WARN (third-highest)") { + std::stringstream log; + std::unique_ptr<_Log> cut { _Log::make_log(log) }; + cut->set_inclusive(true); + cut->set_level(log_t::WARN); + WHEN("fatal_error is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->fatal_error("Topic", "This"); + THEN("Output string is Topic FERR: This\\n") { + REQUIRE(log.str() == "Topic FERR: This\n"); + } + } + WHEN("error is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->error("Topic", "This"); + THEN("Output string is Topic ERR: This\\n") { + REQUIRE(log.str() == "Topic ERR: This\n"); + } + } + WHEN("warn is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->warn("Topic", "This"); + THEN("Output string is Topic WARN: This\\n") { + REQUIRE(log.str() == "Topic WARN: This\n"); + } + } + WHEN("info is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->info("Topic", "This"); + THEN("Output string is blank") { + REQUIRE(log.str() == ""); + } + } + WHEN("debug is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->debug("Topic", "This"); + THEN("Output string is blank") { + REQUIRE(log.str() == ""); + } + } + } + GIVEN("Single, inclusive log level of INFO (fourth-highest)") { + std::stringstream log; + std::unique_ptr<_Log> cut { _Log::make_log(log) }; + cut->set_inclusive(true); + cut->set_level(log_t::INFO); + WHEN("fatal_error is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->fatal_error("Topic", "This"); + THEN("Output string is Topic FERR: This\\n") { + REQUIRE(log.str() == "Topic FERR: This\n"); + } + } + WHEN("error is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->error("Topic", "This"); + THEN("Output string is Topic ERR: This\\n") { + REQUIRE(log.str() == "Topic ERR: This\n"); + } + } + WHEN("warn is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->warn("Topic", "This"); + THEN("Output string is Topic WARN: This\\n") { + REQUIRE(log.str() == "Topic WARN: This\n"); + } + } + WHEN("info is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->info("Topic", "This"); + THEN("Output string is Topic INFO: This\\n") { + REQUIRE(log.str() == "Topic INFO: This\n"); + } + } + WHEN("debug is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->debug("Topic", "This"); + THEN("Output string is blank") { + REQUIRE(log.str() == ""); + } + } + } + GIVEN("Single, inclusive log level of DEBUG (fifth-highest)") { + std::stringstream log; + std::unique_ptr<_Log> cut { _Log::make_log(log) }; + cut->set_inclusive(true); + cut->set_level(log_t::DEBUG); + WHEN("fatal_error is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->fatal_error("Topic", "This"); + THEN("Output string is Topic FERR: This\\n") { + REQUIRE(log.str() == "Topic FERR: This\n"); + } + } + WHEN("error is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->error("Topic", "This"); + THEN("Output string is Topic ERR: This\\n") { + REQUIRE(log.str() == "Topic ERR: This\n"); + } + } + WHEN("warn is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->warn("Topic", "This"); + THEN("Output string is Topic WARN: This\\n") { + REQUIRE(log.str() == "Topic WARN: This\n"); + } + } + WHEN("info is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->info("Topic", "This"); + THEN("Output string is Topic INFO: This\\n") { + REQUIRE(log.str() == "Topic INFO: This\n"); + } + } + WHEN("debug is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->debug("Topic", "This"); + THEN("Output string is Topic DEBUG: This\\n") { + REQUIRE(log.str() == "Topic DEBUG: This\n"); + } + } + } +} + +SCENARIO( "_Log output set filtering with std::string methods" ) { + + GIVEN("log level of DEBUG only") { + std::stringstream log; + std::unique_ptr<_Log> cut { _Log::make_log(log) }; + cut->set_inclusive(false); + cut->clear_level(log_t::ALL); + cut->set_level(log_t::DEBUG); + WHEN("fatal_error is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->fatal_error("Topic", "This"); + THEN("Output string is blank") { + REQUIRE(log.str() == ""); + } + } + WHEN("error is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->error("Topic", "This"); + THEN("Output string is blank") { + REQUIRE(log.str() == ""); + } + } + WHEN("warn is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->warn("Topic", "This"); + THEN("Output string is blank") { + REQUIRE(log.str() == ""); + } + } + WHEN("info is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->info("Topic", "This"); + THEN("Output string is blank") { + REQUIRE(log.str() == ""); + } + } + WHEN("debug is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->debug("Topic", "This"); + THEN("Output string is Topic DEBUG: This\\n") { + REQUIRE(log.str() == "Topic DEBUG: This\n"); + } + } + } + GIVEN("log level of INFO only") { + std::stringstream log; + std::unique_ptr<_Log> cut { _Log::make_log(log) }; + cut->set_inclusive(false); + cut->clear_level(log_t::ALL); + cut->set_level(log_t::INFO); + WHEN("fatal_error is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->fatal_error("Topic", "This"); + THEN("Output string is blank") { + REQUIRE(log.str() == ""); + } + } + WHEN("error is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->error("Topic", "This"); + THEN("Output string is blank") { + REQUIRE(log.str() == ""); + } + } + WHEN("warn is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->warn("Topic", "This"); + THEN("Output string is blank") { + REQUIRE(log.str() == ""); + } + } + WHEN("info is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->info("Topic", "This"); + THEN("Output string is Topic INFO: This\\n") { + REQUIRE(log.str() == "Topic INFO: This\n"); + } + } + WHEN("debug is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->debug("Topic", "This"); + THEN("Output string is blank") { + REQUIRE(log.str() == ""); + } + } + } + GIVEN("log level of WARN only") { + std::stringstream log; + std::unique_ptr<_Log> cut { _Log::make_log(log) }; + cut->set_inclusive(false); + cut->clear_level(log_t::ALL); + cut->set_level(log_t::WARN); + WHEN("fatal_error is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->fatal_error("Topic", "This"); + THEN("Output string is blank") { + REQUIRE(log.str() == ""); + } + } + WHEN("error is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->error("Topic", "This"); + THEN("Output string is blank") { + REQUIRE(log.str() == ""); + } + } + WHEN("warn is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->warn("Topic", "This"); + THEN("Output string is Topic WARN: This\\n") { + REQUIRE(log.str() == "Topic WARN: This\n"); + } + } + WHEN("info is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->info("Topic", "This"); + THEN("Output string is blank") { + REQUIRE(log.str() == ""); + } + } + WHEN("debug is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->debug("Topic", "This"); + THEN("Output string is blank") { + REQUIRE(log.str() == ""); + } + } + } + GIVEN("log level of FERR only") { + std::stringstream log; + std::unique_ptr<_Log> cut { _Log::make_log(log) }; + cut->set_inclusive(false); + cut->clear_level(log_t::ALL); + cut->set_level(log_t::FERR); + WHEN("fatal_error is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->fatal_error("Topic", "This"); + THEN("Output string is Topic FERR: This\\n") { + REQUIRE(log.str() == "Topic FERR: This\n"); + } + } + WHEN("error is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->error("Topic", "This"); + THEN("Output string is blank") { + REQUIRE(log.str() == ""); + } + } + WHEN("warn is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->warn("Topic", "This"); + THEN("Output string is blank") { + REQUIRE(log.str() == ""); + } + } + WHEN("info is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->info("Topic", "This"); + THEN("Output string is blank") { + REQUIRE(log.str() == ""); + } + } + WHEN("debug is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->debug("Topic", "This"); + THEN("Output string is blank") { + REQUIRE(log.str() == ""); + } + } + } + GIVEN("log level of DEBUG and ERR") { + std::stringstream log; + std::unique_ptr<_Log> cut { _Log::make_log(log) }; + cut->set_inclusive(false); + cut->clear_level(log_t::ALL); + cut->set_level(log_t::DEBUG); + cut->set_level(log_t::ERR); + WHEN("fatal_error is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->fatal_error("Topic", "This"); + THEN("Output string is blank") { + REQUIRE(log.str() == ""); + } + } + WHEN("error is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->error("Topic", "This"); + THEN("Output string is Topic ERR: This\\n") { + REQUIRE(log.str() == "Topic ERR: This\n"); + } + } + WHEN("warn is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->warn("Topic", "This"); + THEN("Output string is blank") { + REQUIRE(log.str() == ""); + } + } + WHEN("info is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->info("Topic", "This"); + THEN("Output string is blank") { + REQUIRE(log.str() == ""); + } + } + WHEN("debug is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->debug("Topic", "This"); + THEN("Output string is Topic DEBUG: This\\n") { + REQUIRE(log.str() == "Topic DEBUG: This\n"); + } + } + } + GIVEN("log level of INFO and WARN") { + std::stringstream log; + std::unique_ptr<_Log> cut { _Log::make_log(log) }; + cut->set_inclusive(false); + cut->clear_level(log_t::ALL); + cut->set_level(log_t::INFO); + cut->set_level(log_t::WARN); + cut->set_inclusive(false); + WHEN("fatal_error is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->fatal_error("Topic", "This"); + THEN("Output string is blank") { + REQUIRE(log.str() == ""); + } + } + WHEN("error is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->error("Topic", "This"); + THEN("Output string is blank") { + REQUIRE(log.str() == ""); + } + } + WHEN("warn is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->warn("Topic", "This"); + THEN("Output string is Topic WARN: This\\n") { + REQUIRE(log.str() == "Topic WARN: This\n"); + } + } + WHEN("info is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->info("Topic", "This"); + THEN("Output string is Topic INFO: This\\n") { + REQUIRE(log.str() == "Topic INFO: This\n"); + } + } + WHEN("debug is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->debug("Topic", "This"); + THEN("Output string is blank") { + REQUIRE(log.str() == ""); + } + } + } +} + +SCENARIO( "_Log output filtering on topic name" ) { + std::stringstream log; + std::unique_ptr<_Log> cut { _Log::make_log(log) }; + cut->set_inclusive(true); + cut->set_level(log_t::ALL); + WHEN("Topic is \"t1\"") { + cut->add_topic("t1"); + cut->debug("t1") << "TEXT FOR T1 "; + cut->debug("t2") << "TEXT FOR T2 "; + cut->debug("t3") << "TEXT FOR T3"; + THEN("Log text is \"TEXT FOR T1 \"") { + REQUIRE(log.str() == "t1 DEBUG: TEXT FOR T1 "); + } + } + + WHEN("Topic is \"t2\"") { + cut->add_topic("t2"); + cut->debug("t1") << "TEXT FOR T1 "; + cut->debug("t2") << "TEXT FOR T2 "; + cut->debug("t3") << "TEXT FOR T3"; + THEN("Log text is \"TEXT FOR T2 \"") { + REQUIRE(log.str() == "t2 DEBUG: TEXT FOR T2 "); + } + } + + WHEN("Topic is \"t3\"") { + cut->add_topic("t3"); + cut->debug("t1") << "TEXT FOR T1 "; + cut->debug("t2") << "TEXT FOR T2 "; + cut->debug("t3") << "TEXT FOR T3"; + THEN("Log text is \"TEXT FOR T3\"") { + REQUIRE(log.str() == "t3 DEBUG: TEXT FOR T3"); + } + } + + WHEN("Topic is \"t3\" and \"t2\"") { + cut->add_topic("t2"); + cut->add_topic("t3"); + cut->debug("t1") << "TEXT FOR T1 "; + cut->debug("t2") << "TEXT FOR T2 "; + cut->debug("t3") << "TEXT FOR T3"; + THEN("Log text is \"TEXT FOR T2 TEXT FOR T3\"") { + REQUIRE(log.str() == "t2 DEBUG: TEXT FOR T2 t3 DEBUG: TEXT FOR T3"); + } + } +} diff --git a/src/test/libslic3r/test_model.cpp b/src/test/libslic3r/test_model.cpp new file mode 100644 index 000000000..009c5255f --- /dev/null +++ b/src/test/libslic3r/test_model.cpp @@ -0,0 +1,50 @@ +#include +#include "Model.hpp" +#include "test_data.hpp" // get access to init_print, etc + +using namespace Slic3r::Test; + +SCENARIO("Model construction") { + GIVEN("A Slic3r Model") { + auto model {Slic3r::Model()}; + auto sample_mesh {Slic3r::TriangleMesh::make_cube(20,20,20)}; + sample_mesh.repair(); + + auto config {Slic3r::Config::new_from_defaults()}; + std::shared_ptr print = std::make_shared(); + print->apply_config(config); + + WHEN("Model object is added") { + ModelObject* mo {model.add_object()}; + THEN("Model object list == 1") { + REQUIRE(model.objects.size() == 1); + } + + mo->add_volume(sample_mesh); + THEN("Model volume list == 1") { + REQUIRE(mo->volumes.size() == 1); + } + THEN("Model volume modifier is false") { + REQUIRE(mo->volumes.front()->modifier == false); + } + THEN("Mesh is equivalent to input mesh.") { + REQUIRE(sample_mesh.vertices() == mo->volumes.front()->mesh.vertices()); + } + ModelInstance* inst {mo->add_instance()}; + inst->rotation = 0; + inst->scaling_factor = 1.0; + model.arrange_objects(print->config.min_object_distance()); + model.center_instances_around_point(Slic3r::Pointf(100,100)); + print->auto_assign_extruders(mo); + print->add_model_object(mo); + THEN("Print works?") { + print->process(); + auto gcode {std::stringstream("")}; + print->export_gcode(gcode, true); + REQUIRE(gcode.str().size() > 0); + } + + } + } + +} diff --git a/src/test/libslic3r/test_print.cpp b/src/test/libslic3r/test_print.cpp new file mode 100644 index 000000000..14981cfaa --- /dev/null +++ b/src/test/libslic3r/test_print.cpp @@ -0,0 +1,98 @@ +#include +#include +#include "test_data.hpp" +#include "libslic3r.h" + +using namespace Slic3r::Test; +using namespace std::literals; + +SCENARIO("PrintObject: Perimeter generation") { + GIVEN("20mm cube and default config") { + auto config {Slic3r::Config::new_from_defaults()}; + TestMesh m { TestMesh::cube_20x20x20 }; + Slic3r::Model model; + auto event_counter {0U}; + 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("fill_density", 0); + + WHEN("make_perimeters() is called") { + auto print {Slic3r::Test::init_print({m}, model, config)}; + const auto& object = *(print->objects.at(0)); + print->objects[0]->make_perimeters(); + THEN("67 layers exist in the model") { + REQUIRE(object.layers.size() == 67); + } + THEN("Every layer in region 0 has 1 island of perimeters") { + for(auto* layer : object.layers) { + REQUIRE(layer->regions[0]->perimeters.size() == 1); + } + } + THEN("Every layer in region 0 has 3 paths in its perimeters list.") { + for(auto* layer : object.layers) { + REQUIRE(layer->regions[0]->perimeters.items_count() == 3); + } + } + } + } +} + +SCENARIO("Print: Skirt generation") { + GIVEN("20mm cube and default config") { + auto config {Slic3r::Config::new_from_defaults()}; + TestMesh m { TestMesh::cube_20x20x20 }; + Slic3r::Model model; + auto event_counter {0U}; + std::string stage; + int value {0}; + config->set("skirt_height", 1); + config->set("skirt_distance", 1); + WHEN("Skirts is set to 2 loops") { + config->set("skirts", 2); + auto print {Slic3r::Test::init_print({m}, model, config)}; + print->make_skirt(); + THEN("Skirt Extrusion collection has 2 loops in it") { + REQUIRE(print->skirt.items_count() == 2); + REQUIRE(print->skirt.flatten().entities.size() == 2); + } + } + } +} + +SCENARIO("Print: Brim generation") { + GIVEN("20mm cube and default config, 1mm first layer width") { + auto config {Slic3r::Config::new_from_defaults()}; + TestMesh m { TestMesh::cube_20x20x20 }; + Slic3r::Model model; + auto event_counter {0U}; + std::string stage; + int value {0}; + config->set("first_layer_extrusion_width", 1); + WHEN("Brim is set to 3mm") { + config->set("brim_width", 3); + auto print {Slic3r::Test::init_print({m}, model, config)}; + print->make_brim(); + THEN("Brim Extrusion collection has 3 loops in it") { + REQUIRE(print->brim.items_count() == 3); + } + } + WHEN("Brim is set to 6mm") { + config->set("brim_width", 6); + auto print {Slic3r::Test::init_print({m}, model, config)}; + print->make_brim(); + 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->set("brim_width", 6); + config->set("first_layer_extrusion_width", 0.5); + auto print {Slic3r::Test::init_print({m}, model, config)}; + print->make_brim(); + THEN("Brim Extrusion collection has 12 loops in it") { + REQUIRE(print->brim.items_count() == 12); + } + } + } +} diff --git a/src/test/libslic3r/test_printgcode.cpp b/src/test/libslic3r/test_printgcode.cpp new file mode 100644 index 000000000..67577875b --- /dev/null +++ b/src/test/libslic3r/test_printgcode.cpp @@ -0,0 +1,230 @@ +#include + +#include +#include "test_data.hpp" +#include "libslic3r.h" +#include "GCodeReader.hpp" + +using namespace Slic3r::Test; +using namespace Slic3r; + +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") { + GIVEN("A default configuration and a print test object") { + auto config {Slic3r::Config::new_from_defaults()}; + auto gcode {std::stringstream("")}; + + WHEN("the output is executed with no support material") { + config->set("first_layer_extrusion_width", 0); + config->set("gcode_comments", true); + config->set("start_gcode", ""); + Slic3r::Model model; + auto print {Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config)}; + print->process(); + Slic3r::Test::gcode(gcode, print); + auto exported {gcode.str()}; + THEN("Some text output is generated.") { + REQUIRE(exported.size() > 0); + } + THEN("Exported text contains slic3r version") { + REQUIRE(exported.find(SLIC3R_VERSION) != std::string::npos); + } + THEN("Exported text contains git commit id") { + REQUIRE(exported.find("; Git Commit") != std::string::npos); + REQUIRE(exported.find(BUILD_COMMIT) != std::string::npos); + } + THEN("Exported text contains extrusion statistics.") { + REQUIRE(exported.find("; external perimeters extrusion width") != std::string::npos); + REQUIRE(exported.find("; perimeters extrusion width") != std::string::npos); + REQUIRE(exported.find("; infill extrusion width") != std::string::npos); + REQUIRE(exported.find("; solid infill extrusion width") != std::string::npos); + REQUIRE(exported.find("; top solid infill extrusion width") != std::string::npos); + REQUIRE(exported.find("; support material extrusion width") == std::string::npos); + REQUIRE(exported.find("; first layer extrusion width") == std::string::npos); + } + THEN("Exported text does not contain cooling markers (they were consumed)") { + REQUIRE(exported.find(";_EXTRUDE_SET_SPEED") == std::string::npos); + } + + THEN("GCode preamble is emitted.") { + REQUIRE(exported.find("G21 ; set units to millimeters") != std::string::npos); + } + + THEN("Config options emitted for print config, default region config, default object config") { + REQUIRE(exported.find("; first_layer_temperature") != std::string::npos); + REQUIRE(exported.find("; layer_height") != std::string::npos); + REQUIRE(exported.find("; fill_density") != std::string::npos); + } + THEN("Infill is emitted.") { + std::smatch has_match; + REQUIRE(std::regex_search(exported, has_match, infill_regex)); + } + THEN("Perimeters are emitted.") { + std::smatch has_match; + REQUIRE(std::regex_search(exported, has_match, perimeters_regex)); + } + THEN("Skirt is emitted.") { + std::smatch has_match; + REQUIRE(std::regex_search(exported, has_match, skirt_regex)); + } + THEN("final Z height is ~20mm") { + double final_z {0.0}; + auto reader {GCodeReader()}; + reader.apply_config(print->config); + reader.parse(exported, [&final_z] (GCodeReader& self, const GCodeReader::GCodeLine& line) + { + final_z = std::max(final_z, static_cast(self.Z)); // record the highest Z point we reach + }); + REQUIRE(final_z == Approx(20.15)); + } + } + WHEN("output is executed with complete objects and two differently-sized meshes") { + Slic3r::Model model; + config->set("first_layer_extrusion_width", 0); + config->set("first_layer_height", 0.3); + config->set("support_material", false); + config->set("raft_layers", 0); + config->set("complete_objects", true); + config->set("gcode_comments", true); + config->set("between_objects_gcode", "; between-object-gcode"); + auto print {Slic3r::Test::init_print({TestMesh::cube_20x20x20, TestMesh::ipadstand}, model, config)}; + Slic3r::Test::gcode(gcode, print); + auto exported {gcode.str()}; + THEN("Some text output is generated.") { + REQUIRE(exported.size() > 0); + } + THEN("Infill is emitted.") { + std::smatch has_match; + REQUIRE(std::regex_search(exported, has_match, infill_regex)); + } + THEN("Perimeters are emitted.") { + std::smatch has_match; + REQUIRE(std::regex_search(exported, has_match, perimeters_regex)); + } + THEN("Skirt is emitted.") { + std::smatch has_match; + REQUIRE(std::regex_search(exported, has_match, skirt_regex)); + } + THEN("Between-object-gcode is emitted.") { + REQUIRE(exported.find("; between-object-gcode") != std::string::npos); + } + THEN("final Z height is ~27mm") { + double final_z {0.0}; + auto reader {GCodeReader()}; + reader.apply_config(print->config); + reader.parse(exported, [&final_z] (GCodeReader& self, const GCodeReader::GCodeLine& line) + { + final_z = std::max(final_z, static_cast(self.Z)); // record the highest Z point we reach + }); + REQUIRE(final_z == Approx(30).margin(0.1)); // close enough + } + THEN("Z height resets on object change") { + double final_z {0.0}; + bool reset {false}; + auto reader {GCodeReader()}; + reader.apply_config(print->config); + reader.parse(exported, [&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(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}; + auto reader {GCodeReader()}; + reader.apply_config(print->config); + reader.parse(exported, [&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(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("first_layer_extrusion_width", 0); + config->set("support_material", true); + config->set("raft_layers", 3); + config->set("gcode_comments", true); + auto print {Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config)}; + Slic3r::Test::gcode(gcode, print); + auto exported {gcode.str()}; + THEN("Some text output is generated.") { + REQUIRE(exported.size() > 0); + } + THEN("Exported text contains extrusion statistics.") { + REQUIRE(exported.find("; external perimeters extrusion width") != std::string::npos); + REQUIRE(exported.find("; perimeters extrusion width") != std::string::npos); + REQUIRE(exported.find("; infill extrusion width") != std::string::npos); + REQUIRE(exported.find("; solid infill extrusion width") != std::string::npos); + REQUIRE(exported.find("; top solid infill extrusion width") != std::string::npos); + REQUIRE(exported.find("; support material extrusion width") != std::string::npos); + REQUIRE(exported.find("; first layer extrusion width") == std::string::npos); + } + THEN("Raft is emitted.") { + REQUIRE(exported.find("; raft") != std::string::npos); + } + } + WHEN("the output is executed with a separate first layer extrusion width") { + Slic3r::Model model; + config->set("first_layer_extrusion_width", 0.5); + auto print {Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config)}; + Slic3r::Test::gcode(gcode, print); + auto exported {gcode.str()}; + THEN("Some text output is generated.") { + REQUIRE(exported.size() > 0); + } + THEN("Exported text contains extrusion statistics.") { + REQUIRE(exported.find("; external perimeters extrusion width") != std::string::npos); + REQUIRE(exported.find("; perimeters extrusion width") != std::string::npos); + REQUIRE(exported.find("; infill extrusion width") != std::string::npos); + REQUIRE(exported.find("; solid infill extrusion width") != std::string::npos); + REQUIRE(exported.find("; top solid infill extrusion width") != std::string::npos); + REQUIRE(exported.find("; support material extrusion width") == std::string::npos); + REQUIRE(exported.find("; first layer extrusion width") != std::string::npos); + } + } + WHEN("Cooling is enabled and the fan is disabled.") { + config->set("cooling", true); + config->set("disable_fan_first_layers", 5); + Slic3r::Model model; + auto print {Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config)}; + Slic3r::Test::gcode(gcode, print); + auto exported {gcode.str()}; + THEN("GCode to disable fan is emitted."){ + REQUIRE(exported.find("M107") != std::string::npos); + } + } + + + WHEN("end_gcode exists with layer_num and layer_z") { + config->set("end_gcode", "; Layer_num [layer_num]\n; Layer_z [layer_z]"); + config->set("layer_height", 0.1); + config->set("first_layer_height", 0.1); + + Slic3r::Model model; + auto print {Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config)}; + Slic3r::Test::gcode(gcode, print); + + auto exported {gcode.str()}; + THEN("layer_num and layer_z are processed in the end gcode") {\ + REQUIRE(exported.find("; Layer_num 199") != std::string::npos); + REQUIRE(exported.find("; Layer_z 20") != std::string::npos); + } + } + + gcode.clear(); + } +} diff --git a/src/test/libslic3r/test_skirt_brim.cpp b/src/test/libslic3r/test_skirt_brim.cpp new file mode 100644 index 000000000..6af9aeda0 --- /dev/null +++ b/src/test/libslic3r/test_skirt_brim.cpp @@ -0,0 +1,122 @@ +#include +#include "test_data.hpp" // get access to init_print, etc +#include "GCodeReader.hpp" + +using namespace Slic3r::Test; +using namespace Slic3r; + +SCENARIO("Original Slic3r Skirt/Brim tests", "[!mayfail]") { + GIVEN("Configuration with a skirt height of 2") { + auto config {Config::new_from_defaults()}; + config->set("skirts", 1); + config->set("skirt_height", 2); + config->set("perimeters", 0); + config->set("support_material_speed", 99); + + // avoid altering speeds unexpectedly + config->set("cooling", 0); + config->set("first_layer_speed", "100%"); + + WHEN("multiple objects are printed") { + auto gcode {std::stringstream("")}; + Slic3r::Model model; + auto print {Slic3r::Test::init_print({TestMesh::cube_20x20x20, TestMesh::cube_20x20x20}, model, config)}; + std::map layers_with_skirt; + Slic3r::Test::gcode(gcode, print); + auto parser {Slic3r::GCodeReader()}; + parser.parse_stream(gcode, [&layers_with_skirt, &config] (Slic3r::GCodeReader& self, const Slic3r::GCodeReader::GCodeLine& line) + { + if (self.Z > 0) { + if (line.extruding() && line.new_F() == config->getFloat("support_material_speed") * 60.0) { + layers_with_skirt[self.Z] = 1; + } + } + }); + + THEN("skirt_height is honored") { + REQUIRE(layers_with_skirt.size() == (size_t)config->getInt("skirt_height")); + } + } + } + GIVEN("A default configuration") { + auto config {Config::new_from_defaults()}; + config->set("support_material_speed", 99); + + // avoid altering speeds unexpectedly + config->set("cooling", 0); + config->set("first_layer_speed", "100%"); + // remove noise from top/solid layers + config->set("top_solid_layers", 0); + config->set("bottom_solid_layers", 0); + + WHEN("Brim width is set to 5") { + config->set("perimeters", 0); + config->set("skirts", 0); + config->set("brim_width", 5); + THEN("Brim is generated") { + REQUIRE(false); + } + } + + WHEN("Skirt area is smaller than the brim") { + config->set("skirts", 1); + config->set("brim_width", 10); + THEN("GCode generates successfully.") { + REQUIRE(false); + } + } + + WHEN("Skirt height is 0 and skirts > 0") { + config->set("skirts", 2); + config->set("skirt_height", 0); + + THEN("GCode generates successfully.") { + REQUIRE(false); + } + } + + WHEN("Perimeter extruder = 2 and support extruders = 3") { + THEN("Brim is printed with the extruder used for the perimeters of first object") { + REQUIRE(false); + } + } + WHEN("Perimeter extruder = 2, support extruders = 3, raft is enabled") { + THEN("brim is printed with same extruder as skirt") { + REQUIRE(false); + } + } + + WHEN("Object is plated with overhang support and a brim") { + config->set("layer_height", 0.4); + config->set("first_layer_height", 0.4); + config->set("skirts", 1); + config->set("skirt_distance", 0); + config->set("support_material_speed", 99); + config->set("perimeter_extruder", 1); + config->set("support_material_extruder", 2); + config->set("cooling", 0); // to prevent speeds to be altered + config->set("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") { + REQUIRE(false); + } + } + WHEN("Large minimum skirt length is used.") { + config->set("min_skirt_length", 20); + auto gcode {std::stringstream("")}; + Slic3r::Model model; + auto print {Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config)}; + THEN("Gcode generation doesn't crash") { + Slic3r::Test::gcode(gcode, print); + auto exported {gcode.str()}; + REQUIRE(exported.size() > 0); + } + } + } +} diff --git a/src/test/libslic3r/test_support_material.cpp b/src/test/libslic3r/test_support_material.cpp new file mode 100644 index 000000000..fa82323d5 --- /dev/null +++ b/src/test/libslic3r/test_support_material.cpp @@ -0,0 +1,295 @@ +#include +#include +#include + +#include "libslic3r.h" +#include "TriangleMesh.hpp" +#include "Model.hpp" +#include "SupportMaterial.hpp" + +using namespace std; +using namespace Slic3r; + +void test_1_checks(Print &print, bool &a, bool &b, bool &c, bool &d); +bool test_6_checks(Print &print); + +// Testing 0.1: supports material member functions. +TEST_CASE("", "") +{ + // Create a mesh & modelObject. + TriangleMesh mesh = TriangleMesh::make_cube(20, 20, 20); + + // Create modelObject. + Model model = Model(); + ModelObject *object = model.add_object(); + object->add_volume(mesh); + model.add_default_instances(); + + // Align to origin. + model.align_instances_to_origin(); + + // Create Print. + Print print = Print(); + vector contact_z = {1.9}; + vector top_z = {1.1}; + print.default_object_config.support_material = 1; + print.default_object_config.set_deserialize("raft_layers", "3"); + print.add_model_object(model.objects[0]); + print.objects.front()->_slice(); + + + SupportMaterial *support = print.objects.front()->_support_material(); + + support->generate(print.objects.front()); + REQUIRE(print.objects.front()->support_layer_count() == 3); + +} + +// Test 1. +SCENARIO("SupportMaterial: support_layers_z and contact_distance") +{ + GIVEN("A print object having one modelObject") { + // Create a mesh & modelObject. + TriangleMesh mesh = TriangleMesh::make_cube(20, 20, 20); + + // Create modelObject. + Model model = Model(); + ModelObject *object = model.add_object(); + object->add_volume(mesh); + model.add_default_instances(); + + // Align to origin. + model.align_instances_to_origin(); + // Create Print. + Print print = Print(); + print.default_object_config.set_deserialize("support_material", "1"); + + WHEN("First layer height = 0.4") { + print.default_object_config.set_deserialize("layer_height", "0.2"); + print.default_object_config.set_deserialize("first_layer_height", "0.4"); + + print.add_model_object(model.objects[0]); + print.objects.front()->_slice(); + bool a, b, c, d; + + test_1_checks(print, a, b, c, d); + THEN("First layer height is honored") { + REQUIRE(a == true); + } + THEN("No null or negative support layers") { + REQUIRE(b == true); + } + THEN("No layers thicker than nozzle diameter") { + REQUIRE(c == true); + } + THEN("Layers above top surfaces are spaced correctly") { + REQUIRE(d == true); + } + } + WHEN("Layer height = 0.2 and, first layer height = 0.3") { + print.default_object_config.set_deserialize("layer_height", "0.2"); + print.default_object_config.set_deserialize("first_layer_height", "0.3"); + print.add_model_object(model.objects[0]); + print.objects.front()->_slice(); + bool a, b, c, d; + + test_1_checks(print, a, b, c, d); + THEN("First layer height is honored") { + REQUIRE(a == true); + } + THEN("No null or negative support layers") { + REQUIRE(b == true); + } + THEN("No layers thicker than nozzle diameter") { + REQUIRE(c == true); + } + THEN("Layers above top surfaces are spaced correctly") { + REQUIRE(d == true); + } + } + + + WHEN("Layer height = nozzle_diameter[0]") { + print.default_object_config.set_deserialize("layer_height", "0.2"); + print.default_object_config.set_deserialize("first_layer_height", "0.3"); + print.add_model_object(model.objects[0]); + print.objects.front()->_slice(); + bool a, b, c, d; + + test_1_checks(print, a, b, c, d); + THEN("First layer height is honored") { + REQUIRE(a == true); + } + THEN("No null or negative support layers") { + REQUIRE(b == true); + } + THEN("No layers thicker than nozzle diameter") { + REQUIRE(c == true); + } + THEN("Layers above top surfaces are spaced correctly") { + REQUIRE(d == true); + } + } + } +} + +// Test 8. +TEST_CASE("SupportMaterial: forced support is generated", "") +{ + // Create a mesh & modelObject. + TriangleMesh mesh = TriangleMesh::make_cube(20, 20, 20); + + Model model = Model(); + ModelObject *object = model.add_object(); + object->add_volume(mesh); + model.add_default_instances(); + model.align_instances_to_origin(); + + Print print = Print(); + + vector contact_z = {1.9}; + vector top_z = {1.1}; + print.default_object_config.support_material_enforce_layers = 100; + print.default_object_config.support_material = 0; + print.default_object_config.layer_height = 0.2; + print.default_object_config.set_deserialize("first_layer_height", "0.3"); + + print.add_model_object(model.objects[0]); + print.objects.front()->_slice(); + + SupportMaterial *support = print.objects.front()->_support_material(); + auto support_z = support->support_layers_z(contact_z, top_z, print.default_object_config.layer_height); + + bool check = true; + for (size_t i = 1; i < support_z.size(); i++) { + if (support_z[i] - support_z[i - 1] <= 0) + check = false; + } + + REQUIRE(check == true); +} + +// Test 6. +SCENARIO("SupportMaterial: Checking bridge speed") +{ + GIVEN("Print object") { + // Create a mesh & modelObject. + TriangleMesh mesh = TriangleMesh::make_cube(20, 20, 20); + + Model model = Model(); + ModelObject *object = model.add_object(); + object->add_volume(mesh); + model.add_default_instances(); + model.align_instances_to_origin(); + + Print print = Print(); + print.config.brim_width = 0; + print.config.skirts = 0; + print.config.skirts = 0; + print.default_object_config.support_material = 1; + print.default_region_config.top_solid_layers = 0; // so that we don't have the internal bridge over infill. + print.default_region_config.bridge_speed = 99; + print.config.cooling = 0; + print.config.set_deserialize("first_layer_speed", "100%"); + + WHEN("support_material_contact_distance = 0.2") { + print.default_object_config.support_material_contact_distance = 0.2; + print.add_model_object(model.objects[0]); + + bool check = test_6_checks(print); + REQUIRE(check == true); // bridge speed is used. + } + + WHEN("support_material_contact_distance = 0") { + print.default_object_config.support_material_contact_distance = 0; + print.add_model_object(model.objects[0]); + + bool check = test_6_checks(print); + REQUIRE(check == true); // bridge speed is not used. + } + + WHEN("support_material_contact_distance = 0.2 & raft_layers = 5") { + print.default_object_config.support_material_contact_distance = 0.2; + print.default_object_config.raft_layers = 5; + print.add_model_object(model.objects[0]); + + bool check = test_6_checks(print); + REQUIRE(check == true); // bridge speed is used. + } + + WHEN("support_material_contact_distance = 0 & raft_layers = 5") { + print.default_object_config.support_material_contact_distance = 0; + print.default_object_config.raft_layers = 5; + print.add_model_object(model.objects[0]); + + bool check = test_6_checks(print); + + REQUIRE(check == true); // bridge speed is not used. + } + } +} + +void test_1_checks(Print &print, bool &a, bool &b, bool &c, bool &d) +{ + vector contact_z = {1.9}; + vector top_z = {1.1}; + + SupportMaterial *support = print.objects.front()->_support_material(); + + vector + support_z = support->support_layers_z(contact_z, top_z, print.default_object_config.layer_height); + + a = (support_z[0] == print.default_object_config.first_layer_height.value); + + b = true; + for (size_t i = 1; i < support_z.size(); ++i) + if (support_z[i] - support_z[i - 1] <= 0) b = false; + + + c = true; + for (size_t i = 1; i < support_z.size(); ++i) + if (support_z[i] - support_z[i - 1] > print.config.nozzle_diameter.get_at(0) + EPSILON) + c = false; + + coordf_t expected_top_spacing = support + ->contact_distance(print.default_object_config.layer_height, + print.config.nozzle_diameter.get_at(0)); + + bool wrong_top_spacing = 0; + for (coordf_t top_z_el : top_z) { + // find layer index of this top surface. + size_t layer_id = -1; + for (size_t i = 0; i < support_z.size(); i++) { + if (abs(support_z[i] - top_z_el) < EPSILON) { + layer_id = i; + i = static_cast(support_z.size()); + } + } + + // check that first support layer above this top surface (or the next one) is spaced with nozzle diameter + if (abs(support_z[layer_id + 1] - support_z[layer_id] - expected_top_spacing) > EPSILON + && abs(support_z[layer_id + 2] - support_z[layer_id] - expected_top_spacing) > EPSILON) { + wrong_top_spacing = 1; + } + } + d = !wrong_top_spacing; +} + +// TODO +bool test_6_checks(Print &print) +{ + bool has_bridge_speed = true; + + // Pre-Processing. + PrintObject *print_object = print.objects.front(); + print_object->infill(); + SupportMaterial *support_material = print.objects.front()->_support_material(); + support_material->generate(print_object); + // TODO but not needed in test 6 (make brims and make skirts). + + // Exporting gcode. + // TODO validation found in Simple.pm + + + return has_bridge_speed; +} diff --git a/src/test/libslic3r/test_test_data.cpp b/src/test/libslic3r/test_test_data.cpp new file mode 100644 index 000000000..8bf8be969 --- /dev/null +++ b/src/test/libslic3r/test_test_data.cpp @@ -0,0 +1,29 @@ +#include +#include + +#include "test_data.hpp" + +using namespace Slic3r::Test; + +SCENARIO("init_print functionality") { + GIVEN("A default config") { + config_ptr config {Slic3r::Config::new_from_defaults()}; + std::stringstream gcode; + WHEN("init_print is called with a single mesh.") { + Slic3r::Model model; + auto print = init_print({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.") { + print->process(); + print->export_gcode(gcode, true); + REQUIRE(gcode.str().size() > 0); + } + } + } +} diff --git a/src/test/libslic3r/test_trianglemesh.cpp b/src/test/libslic3r/test_trianglemesh.cpp new file mode 100644 index 000000000..c4aafd4fd --- /dev/null +++ b/src/test/libslic3r/test_trianglemesh.cpp @@ -0,0 +1,428 @@ +#include + +#include "TriangleMesh.hpp" +#include "libslic3r.h" +#include "Point.hpp" +#include "test_options.hpp" +#include "Config.hpp" +#include "Model.hpp" +#include "test_data.hpp" + +#include "Log.hpp" + +#include +#include +#include + +using namespace Slic3r; +using namespace std; + +SCENARIO( "TriangleMesh: Basic mesh statistics") { + GIVEN( "A 20mm cube, built from constexpr std::array" ) { + constexpr std::array vertices { Pointf3(20,20,0), Pointf3(20,0,0), Pointf3(0,0,0), Pointf3(0,20,0), Pointf3(20,20,20), Pointf3(0,20,20), Pointf3(0,0,20), Pointf3(20,0,20) }; + constexpr std::array facets { Point3(0,1,2), Point3(0,2,3), Point3(4,5,6), Point3(4,6,7), Point3(0,4,7), Point3(0,7,1), Point3(1,7,6), Point3(1,6,2), Point3(2,6,5), Point3(2,5,3), Point3(4,0,3), Point3(4,3,5) }; + auto cube {TriangleMesh(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 (auto i = 0U; i < cube.vertices().size(); i++) { + REQUIRE(cube.vertices().at(i) == vertices.at(i)); + } + for (auto i = 0U; i < vertices.size(); i++) { + REQUIRE(vertices.at(i) == cube.vertices().at(i)); + } + } + THEN( "Vertex count matches vertex array size.") { + REQUIRE(cube.facets_count() == facets.size()); + } + + THEN( "Facet array matches input.") { + for (auto i = 0U; i < cube.facets().size(); i++) { + REQUIRE(cube.facets().at(i) == facets.at(i)); + } + + for (auto i = 0U; i < facets.size(); i++) { + REQUIRE(facets.at(i) == cube.facets().at(i)); + } + } + THEN( "Facet count matches facet array size.") { + REQUIRE(cube.facets_count() == facets.size()); + } + + THEN( "Number of normals is equal to the number of facets.") { + REQUIRE(cube.normals().size() == facets.size()); + } + + THEN( "center() returns the center of the object.") { + REQUIRE(cube.center() == Pointf3(10.0,10.0,10.0)); + } + + THEN( "Size of cube is (20,20,20)") { + REQUIRE(cube.size() == Pointf3(20,20,20)); + } + + } + GIVEN( "A 20mm cube with one corner on the origin") { + const Pointf3s vertices { Pointf3(20,20,0), Pointf3(20,0,0), Pointf3(0,0,0), Pointf3(0,20,0), Pointf3(20,20,20), Pointf3(0,20,20), Pointf3(0,0,20), Pointf3(20,0,20) }; + const Point3s facets { Point3(0,1,2), Point3(0,2,3), Point3(4,5,6), Point3(4,6,7), Point3(0,4,7), Point3(0,7,1), Point3(1,7,6), Point3(1,6,2), Point3(2,6,5), Point3(2,5,3), Point3(4,0,3), Point3(4,3,5) }; + + auto cube {TriangleMesh(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 (auto i = 0U; i < cube.vertices().size(); i++) { + REQUIRE(cube.vertices().at(i) == vertices.at(i)); + } + for (auto i = 0U; i < vertices.size(); i++) { + REQUIRE(vertices.at(i) == cube.vertices().at(i)); + } + } + THEN( "Vertex count matches vertex array size.") { + REQUIRE(cube.facets_count() == facets.size()); + } + + THEN( "Facet array matches input.") { + for (auto i = 0U; i < cube.facets().size(); i++) { + REQUIRE(cube.facets().at(i) == facets.at(i)); + } + + for (auto i = 0U; i < facets.size(); i++) { + REQUIRE(facets.at(i) == cube.facets().at(i)); + } + } + THEN( "Facet count matches facet array size.") { + REQUIRE(cube.facets_count() == facets.size()); + } + + THEN( "Number of normals is equal to the number of facets.") { + REQUIRE(cube.normals().size() == facets.size()); + } + + THEN( "center() returns the center of the object.") { + REQUIRE(cube.center() == Pointf3(10.0,10.0,10.0)); + } + + THEN( "Size of cube is (20,20,20)") { + REQUIRE(cube.size() == Pointf3(20,20,20)); + } + } +} + +SCENARIO( "TriangleMesh: Transformation functions affect mesh as expected.") { + GIVEN( "A 20mm cube with one corner on the origin") { + const Pointf3s vertices { Pointf3(20,20,0), Pointf3(20,0,0), Pointf3(0,0,0), Pointf3(0,20,0), Pointf3(20,20,20), Pointf3(0,20,20), Pointf3(0,0,20), Pointf3(20,0,20) }; + const std::vector facets { Point3(0,1,2), Point3(0,2,3), Point3(4,5,6), Point3(4,6,7), Point3(0,4,7), Point3(0,7,1), Point3(1,7,6), Point3(1,6,2), Point3(2,6,5), Point3(2,5,3), Point3(4,0,3), Point3(4,3,5) }; + auto cube {TriangleMesh(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(Vectorf3(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.vertices().at(0).x == 40.0); + } + } + + WHEN( "The cube is scaled 25\% in the X direction") { + cube.scale(Vectorf3(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.vertices().at(0).x == 5.0); + } + } + + WHEN( "The cube is rotated 45 degrees.") { + cube.rotate(45.0, Slic3r::Point(20,20)); + 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 Pointf3 ") { + cube.translate(Pointf3(5.0, 10.0, 0.0)); + THEN( "The first vertex is located at 25, 30, 0") { + REQUIRE(cube.vertices().at(0) == Pointf3(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.vertices().at(0) == Pointf3(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.vertices().at(2) == Pointf3(0.0, 0.0, 0.0)); + } + } + } +} + +SCENARIO( "TriangleMesh: slice behavior.") { + GIVEN( "A 20mm cube with one corner on the origin") { + const Pointf3s vertices { Pointf3(20,20,0), Pointf3(20,0,0), Pointf3(0,0,0), Pointf3(0,20,0), Pointf3(20,20,20), Pointf3(0,20,20), Pointf3(0,0,20), Pointf3(20,0,20) }; + const std::vector facets { Point3(0,1,2), Point3(0,2,3), Point3(4,5,6), Point3(4,6,7), Point3(0,4,7), Point3(0,7,1), Point3(1,7,6), Point3(1,6,2), Point3(2,6,5), Point3(2,5,3), Point3(4,0,3), Point3(4,3,5) }; + auto cube {TriangleMesh(vertices, facets)}; + cube.repair(); + + WHEN("Cube is sliced with z = [0,2,4,8,6,8,10,12,14,16,18,20]") { + std::vector z { 0,2,4,8,6,8,10,12,14,16,18,20 }; + auto result {cube.slice(z)}; + THEN( "The correct number of polygons are returned per layer.") { + for (auto i = 0U; i < z.size(); i++) { + REQUIRE(result.at(i).size() == 1); + } + } + THEN( "The area of the returned polygons is correct.") { + for (auto 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 Pointf3s vertices {Pointf3(0,0,0),Pointf3(0,0,20),Pointf3(0,5,0),Pointf3(0,5,20),Pointf3(50,0,0),Pointf3(50,0,20),Pointf3(15,5,0),Pointf3(35,5,0),Pointf3(15,20,0),Pointf3(50,5,0),Pointf3(35,20,0),Pointf3(15,5,10),Pointf3(50,5,20),Pointf3(35,5,10),Pointf3(35,20,10),Pointf3(15,20,10)}; + const Point3s facets {Point3(0,1,2),Point3(2,1,3),Point3(1,0,4),Point3(5,1,4),Point3(0,2,4),Point3(4,2,6),Point3(7,6,8),Point3(4,6,7),Point3(9,4,7),Point3(7,8,10),Point3(2,3,6),Point3(11,3,12),Point3(7,12,9),Point3(13,12,7),Point3(6,3,11),Point3(11,12,13),Point3(3,1,5),Point3(12,3,5),Point3(5,4,9),Point3(12,5,9),Point3(13,7,10),Point3(14,13,10),Point3(8,15,10),Point3(10,15,14),Point3(6,11,8),Point3(8,11,15),Point3(15,11,13),Point3(14,15,13)}; + + auto cube {TriangleMesh(vertices, facets)}; + cube.repair(); + WHEN(" a top tangent plane is sliced") { + auto 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(); + auto 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") { + auto cube {TriangleMesh::make_cube(20,20,20)}; + THEN("The resulting mesh has one and only one vertex at 0,0,0") { + auto verts {cube.vertices()}; + REQUIRE(std::count_if(verts.begin(), verts.end(), [](Pointf3& 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.facets().size() == 12); + } + } + } + GIVEN("make_cylinder() function") { + WHEN("make_cylinder() is called with arguments 10,10, PI / 3") { + auto cyl {TriangleMesh::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") { + auto verts {cyl.vertices()}; + REQUIRE(std::count_if(verts.begin(), verts.end(), [](Pointf3& 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") { + auto verts {cyl.vertices()}; + REQUIRE(std::count_if(verts.begin(), verts.end(), [](Pointf3& t) { return t.x == 0 && t.y == 0 && t.z == 10; } ) == 1); + } + THEN("Resulting mesh has 2 + (2*PI/angle * 2) vertices.") { + REQUIRE(cyl.vertices().size() == (2 + ((2*PI/angle)*2))); + } + THEN("Resulting mesh has 2*PI/angle * 4 facets") { + REQUIRE(cyl.facets().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") { + auto sph {TriangleMesh::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") { + auto verts {sph.vertices()}; + REQUIRE(std::count_if(verts.begin(), verts.end(), [](Pointf3& t) { return t.x == 0 && t.y == 0 && t.z == 10; } ) == 1); + REQUIRE(std::count_if(verts.begin(), verts.end(), [](Pointf3& t) { return t.x == 0 && t.y == 0 && t.z == -10; } ) == 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 Pointf3s vertices { Pointf3(20,20,0), Pointf3(20,0,0), Pointf3(0,0,0), Pointf3(0,20,0), Pointf3(20,20,20), Pointf3(0,20,20), Pointf3(0,0,20), Pointf3(20,0,20) }; + const Point3s facets { Point3(0,1,2), Point3(0,2,3), Point3(4,5,6), Point3(4,6,7), Point3(0,4,7), Point3(0,7,1), Point3(1,7,6), Point3(1,6,2), Point3(2,6,5), Point3(2,5,3), Point3(4,0,3), Point3(4,3,5) }; + + auto cube {TriangleMesh(vertices, facets)}; + cube.repair(); + WHEN( "The mesh is split into its component parts.") { + auto meshes {cube.split()}; + THEN(" The bounding box statistics are propagated to the split copies") { + REQUIRE(meshes.size() == 1); + REQUIRE(meshes.at(0)->bb3() == cube.bb3()); + } + } + } + GIVEN( "Two 20mm cubes, each with one corner on the origin, merged into a single TriangleMesh") { + const Pointf3s vertices { Pointf3(20,20,0), Pointf3(20,0,0), Pointf3(0,0,0), Pointf3(0,20,0), Pointf3(20,20,20), Pointf3(0,20,20), Pointf3(0,0,20), Pointf3(20,0,20) }; + const Point3s facets { Point3(0,1,2), Point3(0,2,3), Point3(4,5,6), Point3(4,6,7), Point3(0,4,7), Point3(0,7,1), Point3(1,7,6), Point3(1,6,2), Point3(2,6,5), Point3(2,5,3), Point3(4,0,3), Point3(4,3,5) }; + + auto cube {TriangleMesh(vertices, facets)}; + cube.repair(); + auto cube2 {TriangleMesh(vertices, facets)}; + cube2.repair(); + + cube.merge(cube2); + cube.repair(); + WHEN( "The combined mesh is split") { + auto 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 Pointf3s vertices { Pointf3(20,20,0), Pointf3(20,0,0), Pointf3(0,0,0), Pointf3(0,20,0), Pointf3(20,20,20), Pointf3(0,20,20), Pointf3(0,0,20), Pointf3(20,0,20) }; + const Point3s facets { Point3(0,1,2), Point3(0,2,3), Point3(4,5,6), Point3(4,6,7), Point3(0,4,7), Point3(0,7,1), Point3(1,7,6), Point3(1,6,2), Point3(2,6,5), Point3(2,5,3), Point3(4,0,3), Point3(4,3,5) }; + + auto cube {TriangleMesh(vertices, facets)}; + cube.repair(); + auto cube2 {TriangleMesh(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.stats().number_of_facets == 2 * cube2.stats().number_of_facets); + } + } + } +} + +SCENARIO( "TriangleMeshSlicer: Cut behavior.") { + GIVEN( "A 20mm cube with one corner on the origin") { + const Pointf3s vertices { Pointf3(20,20,0), Pointf3(20,0,0), Pointf3(0,0,0), Pointf3(0,20,0), Pointf3(20,20,20), Pointf3(0,20,20), Pointf3(0,0,20), Pointf3(20,0,20) }; + const Point3s facets { Point3(0,1,2), Point3(0,2,3), Point3(4,5,6), Point3(4,6,7), Point3(0,4,7), Point3(0,7,1), Point3(1,7,6), Point3(1,6,2), Point3(2,6,5), Point3(2,5,3), Point3(4,0,3), Point3(4,3,5) }; + + auto cube {TriangleMesh(vertices, facets)}; + cube.repair(); + WHEN( "Object is cut at the bottom") { + TriangleMesh upper {}; + TriangleMesh lower {}; + cube.cut(Z, 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 {}; + cube.cut(Z, 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; + auto config {Slic3r::Config::new_from_defaults()}; + 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 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; + auto config {Slic3r::Config::new_from_defaults()}; + 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/src/test/test_data.cpp b/src/test/test_data.cpp new file mode 100644 index 000000000..72becdf41 --- /dev/null +++ b/src/test/test_data.cpp @@ -0,0 +1,322 @@ +#include "test_data.hpp" +#include "../../libslic3r/TriangleMesh.hpp" +#include "../../libslic3r/GCodeReader.hpp" +#include "../../libslic3r/Config.hpp" +#include "../../libslic3r/Print.hpp" +#include "../../libslic3r/Point.hpp" +#include +#include +#include +#include + +using namespace std::string_literals; +using namespace std; + +namespace Slic3r { namespace Test { + +// Mesh enumeration to name mapping +const std::unordered_map mesh_names { + std::make_pair(TestMesh::A,"A"), + std::make_pair(TestMesh::L,"L"), + std::make_pair(TestMesh::V,"V"), + std::make_pair(TestMesh::_40x10,"40x10"), + std::make_pair(TestMesh::cube_20x20x20,"cube_20x20x20"), + std::make_pair(TestMesh::sphere_50mm,"sphere_50mm"), + std::make_pair(TestMesh::bridge,"bridge"), + std::make_pair(TestMesh::bridge_with_hole,"bridge_with_hole"), + std::make_pair(TestMesh::cube_with_concave_hole,"cube_with_concave_hole"), + std::make_pair(TestMesh::cube_with_hole,"cube_with_hole"), + std::make_pair(TestMesh::gt2_teeth,"gt2_teeth"), + std::make_pair(TestMesh::ipadstand,"ipadstand"), + std::make_pair(TestMesh::overhang,"overhang"), + std::make_pair(TestMesh::pyramid,"pyramid"), + std::make_pair(TestMesh::sloping_hole,"sloping_hole"), + std::make_pair(TestMesh::slopy_cube,"slopy_cube"), + std::make_pair(TestMesh::small_dorito,"small_dorito"), + std::make_pair(TestMesh::step,"step"), + std::make_pair(TestMesh::two_hollow_squares,"two_hollow_squares") +}; + + + +TriangleMesh mesh(TestMesh m) { + std::vector facets; + std::vector 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({ + Vec3i(0,1,2), Vec3i(2,1,3), Vec3i(1,0,4), Vec3i(5,1,4), Vec3i(6,7,4), Vec3i(8,2,9), Vec3i(0,2,8), Vec3i(10,8,9), Vec3i(0,8,6), Vec3i(0,6,4), Vec3i(4,7,9), Vec3i(7,10,9), Vec3i(2,3,9), Vec3i(9,3,11), Vec3i(12,1,5), Vec3i(13,3,12), Vec3i(14,12,5), Vec3i(3,1,12), Vec3i(11,3,13), Vec3i(11,15,5), Vec3i(11,13,15), Vec3i(15,14,5), Vec3i(5,4,9), Vec3i(11,5,9), Vec3i(8,13,12), Vec3i(6,8,12), Vec3i(10,15,13), Vec3i(8,10,13), Vec3i(15,10,14), Vec3i(14,10,7), Vec3i(14,7,12), Vec3i(12,7,6) + }); + break; + case TestMesh::cube_with_concave_hole: + vertices = std::vector({ + 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({ + Vec3i(0,1,2), Vec3i(2,1,3), Vec3i(1,0,4), Vec3i(5,1,4), Vec3i(6,7,4), Vec3i(8,2,9), Vec3i(10,2,11), Vec3i(11,12,13), Vec3i(0,2,10), Vec3i(0,10,6), Vec3i(0,6,4), Vec3i(11,2,8), Vec3i(4,7,12), Vec3i(4,12,8), Vec3i(12,11,8), Vec3i(14,4,8), Vec3i(2,3,9), Vec3i(9,3,15), Vec3i(16,1,5), Vec3i(17,18,5), Vec3i(19,3,16), Vec3i(20,21,5), Vec3i(18,16,5), Vec3i(3,1,16), Vec3i(22,3,19), Vec3i(21,3,22), Vec3i(21,17,5), Vec3i(21,22,17), Vec3i(21,15,3), Vec3i(23,17,22), Vec3i(5,4,14), Vec3i(20,5,14), Vec3i(20,14,21), Vec3i(21,14,8), Vec3i(9,15,21), Vec3i(8,9,21), Vec3i(10,19,16), Vec3i(6,10,16), Vec3i(11,22,19), Vec3i(10,11,19), Vec3i(13,23,11), Vec3i(11,23,22), Vec3i(23,13,12), Vec3i(17,23,12), Vec3i(17,12,18), Vec3i(18,12,7), Vec3i(18,7,16), Vec3i(16,7,6) + }); + break; + case TestMesh::V: + vertices = std::vector({ + 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({ + Vec3i(0,1,2), Vec3i(2,1,3), Vec3i(1,0,4), Vec3i(5,1,4), Vec3i(4,0,2), Vec3i(6,4,2), Vec3i(7,6,2), Vec3i(8,9,7), Vec3i(9,6,7), Vec3i(2,3,7), Vec3i(7,3,10), Vec3i(1,5,3), Vec3i(3,5,11), Vec3i(11,12,13), Vec3i(11,13,3), Vec3i(3,13,10), Vec3i(5,4,6), Vec3i(11,5,6), Vec3i(6,9,11), Vec3i(11,9,12), Vec3i(12,9,8), Vec3i(13,12,8), Vec3i(8,7,10), Vec3i(13,8,10) + }); + break; + case TestMesh::L: + vertices = std::vector({ + 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({ + Vec3i(0,1,2), Vec3i(2,1,3), Vec3i(4,5,1), Vec3i(0,4,1), Vec3i(0,2,4), Vec3i(4,2,6), Vec3i(4,6,7), Vec3i(4,7,8), Vec3i(2,3,6), Vec3i(6,3,9), Vec3i(3,1,5), Vec3i(9,3,5), Vec3i(10,11,5), Vec3i(11,9,5), Vec3i(5,4,10), Vec3i(10,4,8), Vec3i(10,8,7), Vec3i(11,10,7), Vec3i(11,7,6), Vec3i(9,11,6) + }); + break; + case TestMesh::overhang: + vertices = std::vector({ + 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({ + Vec3i(0,1,2), Vec3i(2,3,4), Vec3i(2,5,0), Vec3i(4,6,2), Vec3i(2,6,5), Vec3i(2,1,3), Vec3i(7,8,9), Vec3i(10,9,8), Vec3i(11,9,10), Vec3i(12,9,11), Vec3i(9,13,14), Vec3i(7,15,16), Vec3i(10,17,0), Vec3i(10,0,5), Vec3i(12,11,6), Vec3i(18,16,0), Vec3i(6,19,13), Vec3i(6,13,9), Vec3i(9,12,6), Vec3i(17,18,0), Vec3i(11,10,5), Vec3i(11,5,6), Vec3i(14,16,15), Vec3i(17,7,18), Vec3i(16,18,7), Vec3i(14,15,9), Vec3i(7,9,15), Vec3i(7,17,8), Vec3i(10,8,17), Vec3i(20,21,22), Vec3i(23,24,25), Vec3i(26,23,27), Vec3i(28,27,23), Vec3i(29,28,23), Vec3i(30,29,23), Vec3i(25,31,32), Vec3i(22,33,34), Vec3i(35,36,37), Vec3i(24,38,39), Vec3i(21,40,41), Vec3i(38,42,20), Vec3i(33,43,44), Vec3i(6,4,23), Vec3i(6,23,25), Vec3i(36,35,1), Vec3i(1,0,38), Vec3i(1,38,36), Vec3i(29,30,4), Vec3i(25,32,6), Vec3i(40,42,0), Vec3i(35,45,1), Vec3i(4,3,28), Vec3i(4,28,29), Vec3i(3,1,45), Vec3i(3,45,28), Vec3i(22,34,19), Vec3i(19,6,32), Vec3i(19,32,22), Vec3i(42,38,0), Vec3i(30,23,4), Vec3i(0,16,43), Vec3i(0,43,40), Vec3i(24,37,36), Vec3i(38,24,36), Vec3i(24,23,37), Vec3i(37,23,26), Vec3i(22,32,20), Vec3i(20,32,31), Vec3i(33,41,40), Vec3i(43,33,40), Vec3i(45,35,26), Vec3i(37,26,35), Vec3i(33,44,34), Vec3i(44,43,46), Vec3i(20,42,21), Vec3i(40,21,42), Vec3i(31,39,38), Vec3i(20,31,38), Vec3i(33,22,41), Vec3i(21,41,22), Vec3i(31,25,39), Vec3i(24,39,25), Vec3i(26,27,45), Vec3i(28,45,27), Vec3i(47,48,49), Vec3i(47,50,48), Vec3i(51,48,50), Vec3i(52,48,51), Vec3i(53,48,52), Vec3i(54,55,56), Vec3i(57,55,54), Vec3i(58,55,57), Vec3i(49,59,47), Vec3i(60,56,55), Vec3i(59,56,60), Vec3i(60,47,59), Vec3i(48,53,16), Vec3i(56,13,19), Vec3i(54,56,19), Vec3i(56,59,13), Vec3i(59,49,14), Vec3i(59,14,13), Vec3i(49,48,16), Vec3i(49,16,14), Vec3i(44,46,60), Vec3i(44,60,55), Vec3i(51,50,43), Vec3i(19,34,58), Vec3i(19,58,57), Vec3i(53,52,16), Vec3i(43,16,52), Vec3i(43,52,51), Vec3i(57,54,19), Vec3i(47,60,46), Vec3i(55,58,34), Vec3i(55,34,44), Vec3i(50,47,46), Vec3i(50,46,43) + }); + break; + case TestMesh::_40x10: + vertices = std::vector({ + 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({ + Vec3i(0,1,2), Vec3i(2,1,3), Vec3i(2,3,4), Vec3i(4,3,5), Vec3i(4,5,6), Vec3i(6,5,7), Vec3i(6,7,8), Vec3i(8,7,9), Vec3i(8,9,10), Vec3i(10,9,11), Vec3i(10,11,12), Vec3i(12,11,13), Vec3i(12,13,14), Vec3i(14,13,15), Vec3i(14,15,16), Vec3i(16,15,17), Vec3i(16,17,18), Vec3i(18,17,19), Vec3i(18,19,20), Vec3i(20,19,21), Vec3i(20,21,22), Vec3i(22,21,23), Vec3i(22,23,24), Vec3i(24,23,25), Vec3i(24,25,26), Vec3i(26,25,27), Vec3i(26,27,28), Vec3i(28,27,29), Vec3i(28,29,30), Vec3i(30,29,31), Vec3i(30,31,32), Vec3i(32,31,33), Vec3i(32,33,34), Vec3i(34,33,35), Vec3i(34,35,36), Vec3i(36,35,37), Vec3i(36,37,38), Vec3i(38,37,39), Vec3i(38,39,40), Vec3i(40,39,41), Vec3i(40,41,42), Vec3i(42,41,43), Vec3i(42,43,44), Vec3i(44,43,45), Vec3i(44,45,46), Vec3i(46,45,47), Vec3i(46,47,48), Vec3i(48,47,49), Vec3i(48,49,50), Vec3i(50,49,51), Vec3i(50,51,52), Vec3i(52,51,53), Vec3i(52,53,54), Vec3i(54,53,55), Vec3i(54,55,56), Vec3i(56,55,57), Vec3i(56,57,58), Vec3i(58,57,59), Vec3i(58,59,60), Vec3i(60,59,61), Vec3i(60,61,62), Vec3i(62,61,63), Vec3i(62,63,64), Vec3i(64,63,65), Vec3i(64,65,66), Vec3i(66,65,67), Vec3i(66,67,68), Vec3i(68,67,69), Vec3i(68,69,70), Vec3i(70,69,71), Vec3i(70,71,72), Vec3i(72,71,73), Vec3i(72,73,74), Vec3i(74,73,75), Vec3i(74,75,76), Vec3i(76,75,77), Vec3i(76,77,78), Vec3i(78,77,0), Vec3i(78,0,2), Vec3i(79,80,81), Vec3i(81,80,82), Vec3i(81,82,83), Vec3i(83,82,84), Vec3i(83,84,85), Vec3i(85,84,86), Vec3i(85,86,87), Vec3i(87,86,88), Vec3i(87,88,89), Vec3i(89,88,90), Vec3i(89,90,91), Vec3i(91,90,92), Vec3i(91,92,93), Vec3i(93,92,94), Vec3i(93,94,95), Vec3i(95,94,96), Vec3i(95,96,97), Vec3i(97,96,98), Vec3i(97,98,99), Vec3i(99,98,100), Vec3i(99,100,101), Vec3i(101,100,102), Vec3i(101,102,103), Vec3i(103,102,104), Vec3i(103,104,105), Vec3i(105,104,106), Vec3i(105,106,107), Vec3i(107,106,108), Vec3i(107,108,109), Vec3i(109,108,110), Vec3i(109,110,111), Vec3i(111,110,112), Vec3i(111,112,113), Vec3i(113,112,114), Vec3i(113,114,115), Vec3i(115,114,116), Vec3i(115,116,117), Vec3i(117,116,118), Vec3i(117,118,119), Vec3i(119,118,120), Vec3i(119,120,121), Vec3i(121,120,122), Vec3i(121,122,123), Vec3i(123,122,124), Vec3i(123,124,125), Vec3i(125,124,126), Vec3i(125,126,127), Vec3i(127,126,128), Vec3i(127,128,129), Vec3i(129,128,130), Vec3i(129,130,131), Vec3i(131,130,132), Vec3i(131,132,133), Vec3i(133,132,134), Vec3i(133,134,135), Vec3i(135,134,136), Vec3i(135,136,137), Vec3i(137,136,138), Vec3i(137,138,139), Vec3i(139,138,140), Vec3i(139,140,141), Vec3i(141,140,142), Vec3i(141,142,143), Vec3i(143,142,144), Vec3i(143,144,145), Vec3i(145,144,146), Vec3i(145,146,147), Vec3i(147,146,148), Vec3i(147,148,149), Vec3i(149,148,150), Vec3i(149,150,151), Vec3i(151,150,152), Vec3i(151,152,153), Vec3i(153,152,154), Vec3i(153,154,155), Vec3i(155,154,156), Vec3i(155,156,157), Vec3i(157,156,79), Vec3i(157,79,81), Vec3i(57,110,108), Vec3i(57,108,59), Vec3i(59,108,106), Vec3i(59,106,61), Vec3i(61,106,104), Vec3i(61,104,63), Vec3i(63,104,102), Vec3i(63,102,65), Vec3i(65,102,100), Vec3i(65,100,67), Vec3i(67,100,98), Vec3i(67,98,69), Vec3i(69,98,96), Vec3i(69,96,71), Vec3i(71,96,94), Vec3i(71,94,73), Vec3i(73,94,92), Vec3i(73,92,75), Vec3i(75,92,90), Vec3i(75,90,77), Vec3i(77,90,88), Vec3i(77,88,0), Vec3i(0,88,86), Vec3i(0,86,1), Vec3i(1,86,84), Vec3i(1,84,3), Vec3i(3,84,82), Vec3i(3,82,5), Vec3i(5,82,80), Vec3i(5,80,7), Vec3i(7,80,79), Vec3i(7,79,9), Vec3i(9,79,156), Vec3i(9,156,11), Vec3i(11,156,154), Vec3i(11,154,13), Vec3i(13,154,152), Vec3i(13,152,15), Vec3i(15,152,150), Vec3i(15,150,17), Vec3i(17,150,148), Vec3i(17,148,19), Vec3i(19,148,146), Vec3i(19,146,21), Vec3i(21,146,144), Vec3i(21,144,23), Vec3i(23,144,142), Vec3i(23,142,25), Vec3i(25,142,140), Vec3i(25,140,27), Vec3i(27,140,138), Vec3i(27,138,29), Vec3i(29,138,136), Vec3i(29,136,31), Vec3i(33,31,134), Vec3i(134,31,136), Vec3i(33,134,132), Vec3i(33,132,35), Vec3i(35,132,130), Vec3i(35,130,37), Vec3i(37,130,128), Vec3i(37,128,39), Vec3i(39,128,126), Vec3i(39,126,41), Vec3i(41,126,124), Vec3i(41,124,43), Vec3i(43,124,122), Vec3i(43,122,45), Vec3i(45,122,120), Vec3i(45,120,47), Vec3i(47,120,118), Vec3i(47,118,49), Vec3i(49,118,116), Vec3i(49,116,51), Vec3i(51,116,114), Vec3i(51,114,53), Vec3i(55,53,112), Vec3i(112,53,114), Vec3i(57,55,110), Vec3i(110,55,112), Vec3i(30,135,137), Vec3i(30,137,28), Vec3i(28,137,139), Vec3i(28,139,26), Vec3i(26,139,141), Vec3i(26,141,24), Vec3i(24,141,143), Vec3i(24,143,22), Vec3i(22,143,145), Vec3i(22,145,20), Vec3i(20,145,147), Vec3i(20,147,18), Vec3i(18,147,149), Vec3i(18,149,16), Vec3i(16,149,151), Vec3i(16,151,14), Vec3i(14,151,153), Vec3i(14,153,12), Vec3i(12,153,155), Vec3i(12,155,10), Vec3i(10,155,157), Vec3i(10,157,8), Vec3i(8,157,81), Vec3i(8,81,6), Vec3i(6,81,83), Vec3i(6,83,4), Vec3i(4,83,85), Vec3i(4,85,2), Vec3i(2,85,87), Vec3i(2,87,78), Vec3i(78,87,89), Vec3i(78,89,76), Vec3i(76,89,91), Vec3i(76,91,74), Vec3i(74,91,93), Vec3i(74,93,72), Vec3i(72,93,95), Vec3i(72,95,70), Vec3i(70,95,97), Vec3i(70,97,68), Vec3i(68,97,99), Vec3i(68,99,66), Vec3i(66,99,101), Vec3i(66,101,64), Vec3i(64,101,103), Vec3i(64,103,62), Vec3i(62,103,105), Vec3i(62,105,60), Vec3i(60,105,107), Vec3i(60,107,58), Vec3i(58,107,109), Vec3i(58,109,56), Vec3i(30,32,135), Vec3i(135,32,133), Vec3i(52,113,115), Vec3i(52,115,50), Vec3i(50,115,117), Vec3i(50,117,48), Vec3i(48,117,119), Vec3i(48,119,46), Vec3i(46,119,121), Vec3i(46,121,44), Vec3i(44,121,123), Vec3i(44,123,42), Vec3i(42,123,125), Vec3i(42,125,40), Vec3i(40,125,127), Vec3i(40,127,38), Vec3i(38,127,129), Vec3i(38,129,36), Vec3i(36,129,131), Vec3i(36,131,34), Vec3i(34,131,133), Vec3i(34,133,32), Vec3i(52,54,113), Vec3i(113,54,111), Vec3i(54,56,111), Vec3i(111,56,109) + }); + break; + case TestMesh::sloping_hole: + vertices = std::vector({ + 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({ + Vec3i(0,1,2), Vec3i(2,1,3), Vec3i(1,0,4), Vec3i(5,1,4), Vec3i(6,2,7), Vec3i(0,2,8), Vec3i(0,8,9), Vec3i(0,9,10), Vec3i(0,10,11), Vec3i(0,11,12), Vec3i(0,12,13), Vec3i(0,13,4), Vec3i(13,14,4), Vec3i(15,4,16), Vec3i(17,4,15), Vec3i(18,4,17), Vec3i(19,4,20), Vec3i(18,20,4), Vec3i(21,2,22), Vec3i(4,19,23), Vec3i(4,23,7), Vec3i(23,24,7), Vec3i(22,2,25), Vec3i(26,2,27), Vec3i(28,29,7), Vec3i(25,2,30), Vec3i(29,31,7), Vec3i(30,2,26), Vec3i(31,32,7), Vec3i(27,2,6), Vec3i(32,6,7), Vec3i(28,7,24), Vec3i(33,2,21), Vec3i(34,2,33), Vec3i(35,2,34), Vec3i(36,2,35), Vec3i(8,2,36), Vec3i(16,4,14), Vec3i(2,3,7), Vec3i(7,3,37), Vec3i(38,1,5), Vec3i(3,1,39), Vec3i(3,39,40), Vec3i(3,40,41), Vec3i(42,38,5), Vec3i(3,41,43), Vec3i(3,43,44), Vec3i(37,3,45), Vec3i(37,45,46), Vec3i(37,46,47), Vec3i(48,49,5), Vec3i(37,47,50), Vec3i(49,42,5), Vec3i(37,50,51), Vec3i(37,51,52), Vec3i(37,52,53), Vec3i(37,53,54), Vec3i(55,56,5), Vec3i(37,54,57), Vec3i(37,57,58), Vec3i(59,60,5), Vec3i(37,58,61), Vec3i(37,62,5), Vec3i(37,61,62), Vec3i(62,59,5), Vec3i(60,55,5), Vec3i(63,1,38), Vec3i(64,1,63), Vec3i(65,1,64), Vec3i(66,1,65), Vec3i(67,1,66), Vec3i(39,1,67), Vec3i(44,45,3), Vec3i(56,48,5), Vec3i(5,4,7), Vec3i(37,5,7), Vec3i(41,40,36), Vec3i(36,40,8), Vec3i(39,9,40), Vec3i(40,9,8), Vec3i(43,41,35), Vec3i(35,41,36), Vec3i(44,43,34), Vec3i(34,43,35), Vec3i(33,45,44), Vec3i(34,33,44), Vec3i(21,46,45), Vec3i(33,21,45), Vec3i(22,47,46), Vec3i(21,22,46), Vec3i(25,50,47), Vec3i(22,25,47), Vec3i(30,51,50), Vec3i(25,30,50), Vec3i(26,52,51), Vec3i(30,26,51), Vec3i(27,53,52), Vec3i(26,27,52), Vec3i(6,54,53), Vec3i(27,6,53), Vec3i(32,57,54), Vec3i(6,32,54), Vec3i(31,58,57), Vec3i(32,31,57), Vec3i(29,61,58), Vec3i(31,29,58), Vec3i(28,62,61), Vec3i(29,28,61), Vec3i(59,62,28), Vec3i(24,59,28), Vec3i(60,59,24), Vec3i(23,60,24), Vec3i(55,60,23), Vec3i(19,55,23), Vec3i(55,19,56), Vec3i(56,19,20), Vec3i(56,20,48), Vec3i(48,20,18), Vec3i(48,18,49), Vec3i(49,18,17), Vec3i(49,17,42), Vec3i(42,17,15), Vec3i(42,15,38), Vec3i(38,15,16), Vec3i(38,16,63), Vec3i(63,16,14), Vec3i(63,14,64), Vec3i(64,14,13), Vec3i(64,13,65), Vec3i(65,13,12), Vec3i(65,12,66), Vec3i(66,12,11), Vec3i(66,11,67), Vec3i(67,11,10), Vec3i(67,10,39), Vec3i(39,10,9) + }); + break; + case TestMesh::ipadstand: + vertices = std::vector({ + 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({ + Vec3i(0,1,2), Vec3i(1,0,3), Vec3i(4,5,6), Vec3i(5,4,7), Vec3i(8,9,10), Vec3i(9,11,12), Vec3i(11,9,8), Vec3i(13,14,15), Vec3i(14,13,16), Vec3i(17,2,1), Vec3i(2,17,18), Vec3i(19,11,20), Vec3i(11,19,12), Vec3i(17,21,18), Vec3i(21,2,18), Vec3i(2,21,22), Vec3i(22,21,23), Vec3i(8,22,23), Vec3i(22,8,10), Vec3i(24,25,26), Vec3i(25,24,27), Vec3i(23,1,8), Vec3i(1,23,21), Vec3i(1,21,17), Vec3i(5,15,3), Vec3i(15,5,7), Vec3i(15,7,28), Vec3i(28,7,26), Vec3i(28,26,25), Vec3i(8,14,11), Vec3i(14,8,3), Vec3i(3,8,1), Vec3i(14,3,15), Vec3i(11,14,20), Vec3i(26,4,24), Vec3i(4,26,7), Vec3i(12,16,9), Vec3i(16,12,19), Vec3i(29,4,13), Vec3i(4,29,24), Vec3i(24,29,27), Vec3i(9,22,10), Vec3i(22,9,0), Vec3i(0,9,16), Vec3i(0,16,13), Vec3i(0,13,6), Vec3i(6,13,4), Vec3i(2,22,0), Vec3i(19,14,16), Vec3i(14,19,20), Vec3i(15,29,13), Vec3i(29,25,27), Vec3i(25,29,15), Vec3i(25,15,28), Vec3i(6,3,0), Vec3i(3,6,5) + }); + break; + case TestMesh::A: + vertices = std::vector({ + 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({ + Vec3i(0,1,2), Vec3i(3,4,5), Vec3i(6,7,8), Vec3i(9,10,11), Vec3i(12,2,1), Vec3i(12,1,13), Vec3i(14,15,16), Vec3i(17,18,19), Vec3i(20,21,22), Vec3i(17,19,23), Vec3i(24,25,26), Vec3i(27,13,1), Vec3i(28,25,29), Vec3i(30,31,32), Vec3i(28,33,34), Vec3i(35,36,7), Vec3i(37,38,39), Vec3i(40,10,41), Vec3i(42,43,44), Vec3i(45,5,4), Vec3i(46,47,48), Vec3i(46,48,49), Vec3i(45,4,50), Vec3i(51,52,53), Vec3i(51,54,55), Vec3i(56,52,57), Vec3i(58,59,60), Vec3i(61,50,4), Vec3i(62,63,64), Vec3i(65,34,33), Vec3i(66,67,42), Vec3i(68,17,69), Vec3i(70,71,22), Vec3i(66,42,72), Vec3i(73,16,15), Vec3i(35,7,74), Vec3i(75,76,54), Vec3i(77,27,1), Vec3i(78,32,31), Vec3i(75,54,79), Vec3i(80,26,25), Vec3i(81,80,25), Vec3i(82,83,48), Vec3i(84,20,85), Vec3i(81,25,86), Vec3i(87,88,19), Vec3i(0,89,1), Vec3i(90,91,92), Vec3i(90,10,93), Vec3i(38,94,39), Vec3i(94,95,39), Vec3i(3,7,96), Vec3i(97,15,98), Vec3i(97,99,15), Vec3i(92,91,100), Vec3i(89,101,1), Vec3i(102,39,95), Vec3i(103,11,10), Vec3i(104,96,7), Vec3i(105,15,99), Vec3i(106,61,4), Vec3i(107,108,33), Vec3i(76,55,54), Vec3i(109,91,110), Vec3i(111,23,19), Vec3i(112,63,113), Vec3i(114,115,48), Vec3i(116,59,117), Vec3i(118,20,119), Vec3i(120,31,121), Vec3i(122,44,43), Vec3i(110,91,123), Vec3i(124,125,126), Vec3i(127,128,129), Vec3i(127,130,124), Vec3i(131,124,132), Vec3i(126,133,134), Vec3i(135,136,126), Vec3i(137,138,127), Vec3i(139,127,138), Vec3i(128,140,141), Vec3i(142,128,143), Vec3i(144,140,145), Vec3i(100,91,146), Vec3i(147,148,134), Vec3i(101,149,1), Vec3i(102,150,39), Vec3i(103,10,151), Vec3i(145,140,152), Vec3i(152,140,153), Vec3i(148,154,134), Vec3i(154,155,134), Vec3i(156,15,105), Vec3i(157,104,7), Vec3i(36,8,7), Vec3i(158,37,39), Vec3i(159,19,88), Vec3i(160,19,159), Vec3i(161,59,58), Vec3i(161,117,59), Vec3i(162,31,30), Vec3i(162,121,31), Vec3i(163,43,164), Vec3i(163,165,43), Vec3i(166,167,43), Vec3i(167,164,43), Vec3i(168,57,52), Vec3i(82,48,169), Vec3i(114,170,171), Vec3i(108,65,33), Vec3i(64,63,112), Vec3i(114,172,170), Vec3i(160,173,170), Vec3i(171,170,173), Vec3i(172,174,170), Vec3i(160,170,174), Vec3i(175,176,177), Vec3i(178,77,1), Vec3i(179,31,120), Vec3i(175,180,176), Vec3i(181,182,176), Vec3i(177,176,182), Vec3i(180,183,176), Vec3i(181,176,183), Vec3i(184,42,67), Vec3i(185,69,17), Vec3i(160,111,19), Vec3i(186,187,160), Vec3i(188,189,114), Vec3i(190,188,114), Vec3i(114,48,191), Vec3i(192,114,193), Vec3i(194,160,195), Vec3i(196,160,194), Vec3i(197,198,181), Vec3i(199,197,181), Vec3i(122,43,165), Vec3i(200,201,175), Vec3i(202,175,203), Vec3i(204,175,202), Vec3i(205,119,20), Vec3i(206,181,207), Vec3i(208,209,15), Vec3i(210,15,209), Vec3i(211,10,9), Vec3i(212,10,211), Vec3i(213,214,215), Vec3i(216,217,218), Vec3i(219,14,17), Vec3i(113,63,220), Vec3i(221,222,48), Vec3i(191,48,222), Vec3i(22,223,20), Vec3i(205,20,223), Vec3i(224,40,42), Vec3i(123,91,225), Vec3i(214,226,215), Vec3i(227,215,226), Vec3i(218,217,228), Vec3i(229,228,217), Vec3i(215,230,213), Vec3i(125,135,126), Vec3i(217,216,231), Vec3i(129,128,142), Vec3i(216,213,232), Vec3i(130,132,124), Vec3i(213,216,233), Vec3i(234,213,235), Vec3i(236,227,237), Vec3i(238,237,227), Vec3i(239,240,216), Vec3i(233,216,240), Vec3i(241,242,229), Vec3i(243,229,242), Vec3i(215,227,244), Vec3i(245,215,246), Vec3i(217,247,229), Vec3i(248,249,217), Vec3i(232,213,250), Vec3i(230,250,213), Vec3i(133,147,134), Vec3i(244,227,251), Vec3i(236,252,227), Vec3i(251,227,252), Vec3i(231,216,253), Vec3i(254,253,216), Vec3i(141,140,144), Vec3i(247,255,229), Vec3i(241,229,256), Vec3i(255,256,229), Vec3i(257,241,258), Vec3i(259,146,91), Vec3i(260,261,236), Vec3i(262,1,149), Vec3i(263,264,241), Vec3i(265,241,264), Vec3i(266,236,267), Vec3i(268,267,236), Vec3i(49,48,83), Vec3i(166,43,269), Vec3i(270,271,272), Vec3i(273,274,275), Vec3i(276,274,277), Vec3i(278,151,10), Vec3i(279,280,272), Vec3i(281,39,150), Vec3i(272,282,279), Vec3i(155,283,134), Vec3i(274,276,284), Vec3i(153,140,285), Vec3i(286,276,287), Vec3i(265,276,286), Vec3i(288,289,279), Vec3i(268,288,279), Vec3i(290,291,272), Vec3i(271,290,272), Vec3i(292,274,293), Vec3i(275,274,292), Vec3i(294,265,295), Vec3i(276,265,294), Vec3i(296,297,268), Vec3i(279,296,268), Vec3i(241,265,298), Vec3i(298,265,299), Vec3i(236,300,268), Vec3i(300,301,268), Vec3i(107,33,78), Vec3i(302,303,59), Vec3i(304,305,279), Vec3i(282,304,279), Vec3i(306,276,307), Vec3i(284,276,306), Vec3i(185,17,73), Vec3i(308,309,221), Vec3i(158,39,70), Vec3i(310,41,10), Vec3i(15,311,208), Vec3i(7,6,312), Vec3i(313,314,6), Vec3i(315,6,314), Vec3i(316,208,317), Vec3i(318,317,208), Vec3i(258,241,319), Vec3i(319,241,320), Vec3i(261,321,236), Vec3i(321,322,236), Vec3i(6,315,323), Vec3i(208,324,318), Vec3i(270,325,318), Vec3i(326,318,325), Vec3i(327,328,315), Vec3i(273,315,328), Vec3i(118,329,20), Vec3i(330,20,329), Vec3i(331,332,25), Vec3i(86,25,332), Vec3i(333,334,52), Vec3i(335,52,334), Vec3i(115,336,48), Vec3i(169,48,336), Vec3i(62,106,4), Vec3i(35,15,210), Vec3i(35,337,15), Vec3i(158,10,212), Vec3i(158,310,10), Vec3i(338,178,1), Vec3i(339,59,116), Vec3i(107,302,59), Vec3i(66,22,340), Vec3i(66,341,22), Vec3i(185,221,342), Vec3i(185,308,221), Vec3i(75,31,179), Vec3i(75,343,31), Vec3i(166,20,330), Vec3i(166,85,20), Vec3i(81,52,335), Vec3i(81,168,52), Vec3i(82,19,344), Vec3i(82,87,19), Vec3i(108,339,345), Vec3i(346,108,345), Vec3i(64,347,348), Vec3i(349,347,64), Vec3i(178,109,350), Vec3i(351,178,350), Vec3i(179,352,353), Vec3i(354,352,179), Vec3i(355,208,356), Vec3i(356,208,311), Vec3i(357,358,6), Vec3i(358,312,6), Vec3i(68,22,21), Vec3i(68,340,22), Vec3i(221,48,47), Vec3i(184,342,221), Vec3i(359,270,360), Vec3i(318,360,270), Vec3i(361,362,273), Vec3i(315,273,362), Vec3i(272,102,270), Vec3i(363,270,102), Vec3i(274,273,103), Vec3i(364,103,273), Vec3i(21,19,18), Vec3i(21,20,84), Vec3i(184,46,42), Vec3i(43,42,46), Vec3i(12,22,71), Vec3i(365,22,12), Vec3i(14,98,15), Vec3i(14,220,63), Vec3i(40,93,10), Vec3i(40,225,91), Vec3i(45,221,309), Vec3i(366,221,45), Vec3i(313,367,212), Vec3i(212,367,368), Vec3i(36,369,367), Vec3i(313,36,367), Vec3i(316,37,367), Vec3i(37,368,367), Vec3i(210,367,369), Vec3i(316,367,210), Vec3i(362,370,315), Vec3i(370,323,315), Vec3i(360,318,371), Vec3i(371,318,324), Vec3i(372,331,159), Vec3i(159,195,160), Vec3i(373,115,56), Vec3i(115,114,189), Vec3i(52,56,161), Vec3i(374,161,56), Vec3i(25,28,331), Vec3i(375,331,28), Vec3i(376,333,163), Vec3i(163,203,175), Vec3i(377,118,24), Vec3i(118,181,198), Vec3i(25,24,162), Vec3i(378,162,24), Vec3i(52,51,333), Vec3i(379,333,51), Vec3i(167,380,381), Vec3i(376,167,381), Vec3i(377,381,330), Vec3i(330,381,380), Vec3i(335,381,382), Vec3i(376,381,335), Vec3i(373,383,169), Vec3i(169,383,384), Vec3i(168,385,383), Vec3i(373,168,383), Vec3i(372,87,383), Vec3i(87,384,383), Vec3i(377,80,381), Vec3i(80,382,381), Vec3i(86,383,385), Vec3i(372,383,86), Vec3i(106,348,347), Vec3i(386,106,347), Vec3i(375,65,346), Vec3i(108,346,65), Vec3i(64,112,349), Vec3i(387,349,112), Vec3i(171,190,114), Vec3i(346,345,171), Vec3i(374,190,345), Vec3i(171,345,190), Vec3i(349,172,347), Vec3i(172,114,192), Vec3i(386,347,192), Vec3i(172,192,347), Vec3i(173,160,196), Vec3i(171,173,346), Vec3i(375,346,196), Vec3i(173,196,346), Vec3i(172,349,174), Vec3i(174,186,160), Vec3i(387,186,349), Vec3i(174,349,186), Vec3i(64,348,62), Vec3i(106,62,348), Vec3i(108,107,339), Vec3i(59,339,107), Vec3i(374,345,116), Vec3i(339,116,345), Vec3i(76,353,352), Vec3i(379,76,352), Vec3i(388,77,351), Vec3i(178,351,77), Vec3i(179,120,354), Vec3i(378,354,120), Vec3i(177,200,175), Vec3i(351,350,177), Vec3i(389,200,350), Vec3i(177,350,200), Vec3i(354,180,352), Vec3i(180,175,204), Vec3i(379,352,204), Vec3i(180,204,352), Vec3i(182,181,206), Vec3i(177,182,351), Vec3i(388,351,206), Vec3i(182,206,351), Vec3i(180,354,183), Vec3i(183,199,181), Vec3i(378,199,354), Vec3i(183,354,199), Vec3i(91,109,338), Vec3i(178,338,109), Vec3i(76,75,353), Vec3i(179,353,75), Vec3i(389,350,110), Vec3i(109,110,350), Vec3i(390,391,392), Vec3i(393,394,395), Vec3i(224,122,389), Vec3i(122,175,201), Vec3i(365,388,205), Vec3i(205,207,181), Vec3i(66,340,396), Vec3i(68,396,340), Vec3i(184,396,342), Vec3i(185,342,396), Vec3i(66,396,67), Vec3i(184,67,396), Vec3i(68,69,396), Vec3i(185,396,69), Vec3i(219,111,387), Vec3i(111,160,187), Vec3i(366,386,191), Vec3i(191,193,114), Vec3i(150,272,280), Vec3i(102,272,150), Vec3i(151,277,274), Vec3i(103,151,274), Vec3i(161,374,117), Vec3i(116,117,374), Vec3i(366,61,386), Vec3i(106,386,61), Vec3i(111,187,387), Vec3i(186,387,187), Vec3i(56,188,374), Vec3i(190,374,188), Vec3i(191,386,193), Vec3i(192,193,386), Vec3i(331,375,194), Vec3i(196,194,375), Vec3i(28,34,375), Vec3i(65,375,34), Vec3i(219,387,113), Vec3i(112,113,387), Vec3i(224,389,123), Vec3i(110,123,389), Vec3i(51,55,379), Vec3i(76,379,55), Vec3i(24,197,378), Vec3i(199,378,197), Vec3i(122,201,389), Vec3i(200,389,201), Vec3i(333,379,202), Vec3i(204,202,379), Vec3i(205,388,207), Vec3i(206,207,388), Vec3i(365,27,388), Vec3i(77,388,27), Vec3i(162,378,121), Vec3i(120,121,378), Vec3i(162,30,25), Vec3i(30,29,25), Vec3i(51,53,54), Vec3i(303,60,59), Vec3i(28,29,33), Vec3i(29,397,33), Vec3i(161,58,52), Vec3i(53,52,58), Vec3i(21,84,19), Vec3i(84,344,19), Vec3i(46,49,43), Vec3i(49,269,43), Vec3i(208,316,209), Vec3i(210,209,316), Vec3i(327,313,211), Vec3i(212,211,313), Vec3i(36,35,369), Vec3i(210,369,35), Vec3i(37,158,368), Vec3i(212,368,158), Vec3i(6,8,313), Vec3i(36,313,8), Vec3i(326,38,316), Vec3i(37,316,38), Vec3i(392,391,398), Vec3i(399,398,391), Vec3i(394,400,395), Vec3i(401,395,400), Vec3i(390,214,391), Vec3i(214,213,234), Vec3i(393,395,218), Vec3i(218,239,216), Vec3i(402,230,403), Vec3i(230,215,245), Vec3i(125,124,131), Vec3i(404,125,403), Vec3i(405,406,231), Vec3i(231,248,217), Vec3i(129,137,127), Vec3i(407,406,129), Vec3i(130,127,139), Vec3i(402,130,408), Vec3i(194,195,331), Vec3i(159,331,195), Vec3i(115,189,56), Vec3i(188,56,189), Vec3i(14,219,220), Vec3i(113,220,219), Vec3i(45,50,366), Vec3i(61,366,50), Vec3i(221,366,222), Vec3i(191,222,366), Vec3i(17,23,219), Vec3i(111,219,23), Vec3i(118,198,24), Vec3i(197,24,198), Vec3i(202,203,333), Vec3i(163,333,203), Vec3i(40,224,225), Vec3i(123,225,224), Vec3i(12,13,365), Vec3i(27,365,13), Vec3i(22,365,223), Vec3i(205,223,365), Vec3i(42,44,224), Vec3i(122,224,44), Vec3i(399,391,234), Vec3i(214,234,391), Vec3i(401,239,395), Vec3i(218,395,239), Vec3i(214,390,226), Vec3i(226,238,227), Vec3i(218,228,393), Vec3i(228,229,243), Vec3i(401,399,233), Vec3i(233,235,213), Vec3i(392,409,390), Vec3i(410,390,409), Vec3i(394,393,411), Vec3i(412,411,393), Vec3i(402,403,131), Vec3i(125,131,403), Vec3i(405,137,406), Vec3i(129,406,137), Vec3i(405,408,139), Vec3i(130,139,408), Vec3i(230,245,403), Vec3i(404,403,245), Vec3i(231,406,248), Vec3i(407,248,406), Vec3i(232,254,216), Vec3i(402,408,232), Vec3i(413,404,244), Vec3i(244,246,215), Vec3i(414,247,407), Vec3i(247,217,249), Vec3i(133,126,136), Vec3i(415,133,413), Vec3i(141,143,128), Vec3i(416,414,141), Vec3i(410,238,390), Vec3i(226,390,238), Vec3i(412,393,243), Vec3i(228,243,393), Vec3i(233,399,235), Vec3i(234,235,399), Vec3i(237,260,236), Vec3i(238,410,237), Vec3i(417,260,410), Vec3i(237,410,260), Vec3i(239,401,240), Vec3i(233,240,401), Vec3i(242,241,257), Vec3i(243,242,412), Vec3i(418,412,257), Vec3i(242,257,412), Vec3i(401,419,399), Vec3i(398,399,419), Vec3i(417,410,420), Vec3i(409,420,410), Vec3i(400,421,401), Vec3i(419,401,421), Vec3i(418,422,412), Vec3i(411,412,422), Vec3i(413,135,404), Vec3i(125,404,135), Vec3i(414,407,142), Vec3i(129,142,407), Vec3i(130,402,132), Vec3i(131,132,402), Vec3i(133,136,413), Vec3i(135,413,136), Vec3i(423,147,415), Vec3i(133,415,147), Vec3i(137,405,138), Vec3i(139,138,405), Vec3i(141,414,143), Vec3i(142,143,414), Vec3i(424,416,144), Vec3i(141,144,416), Vec3i(405,254,408), Vec3i(232,408,254), Vec3i(244,404,246), Vec3i(245,246,404), Vec3i(247,249,407), Vec3i(248,407,249), Vec3i(232,250,402), Vec3i(230,402,250), Vec3i(415,413,251), Vec3i(244,251,413), Vec3i(252,236,266), Vec3i(251,252,415), Vec3i(423,415,266), Vec3i(252,266,415), Vec3i(231,253,405), Vec3i(254,405,253), Vec3i(416,255,414), Vec3i(247,414,255), Vec3i(256,263,241), Vec3i(255,416,256), Vec3i(424,263,416), Vec3i(256,416,263), Vec3i(257,258,418), Vec3i(425,418,258), Vec3i(260,417,261), Vec3i(426,261,417), Vec3i(422,418,427), Vec3i(427,259,91), Vec3i(420,428,417), Vec3i(428,1,262), Vec3i(147,423,148), Vec3i(429,148,423), Vec3i(263,424,264), Vec3i(264,295,265), Vec3i(266,267,423), Vec3i(267,268,297), Vec3i(144,145,424), Vec3i(430,424,145), Vec3i(49,431,269), Vec3i(166,269,431), Vec3i(82,431,83), Vec3i(49,83,431), Vec3i(84,85,431), Vec3i(166,431,85), Vec3i(82,344,431), Vec3i(84,431,344), Vec3i(432,278,90), Vec3i(10,90,278), Vec3i(433,0,281), Vec3i(39,281,0), Vec3i(362,361,434), Vec3i(435,271,359), Vec3i(270,359,271), Vec3i(436,361,275), Vec3i(273,275,361), Vec3i(360,437,359), Vec3i(277,287,276), Vec3i(151,278,277), Vec3i(280,279,289), Vec3i(150,280,281), Vec3i(436,438,439), Vec3i(439,285,140), Vec3i(90,92,432), Vec3i(440,432,92), Vec3i(282,272,291), Vec3i(441,282,442), Vec3i(284,293,274), Vec3i(443,438,284), Vec3i(278,432,286), Vec3i(286,299,265), Vec3i(281,288,433), Vec3i(288,268,301), Vec3i(0,433,89), Vec3i(444,89,433), Vec3i(435,445,442), Vec3i(445,134,283), Vec3i(439,446,436), Vec3i(361,436,446), Vec3i(442,290,435), Vec3i(271,435,290), Vec3i(438,436,292), Vec3i(275,292,436), Vec3i(445,435,447), Vec3i(359,447,435), Vec3i(286,287,278), Vec3i(277,278,287), Vec3i(288,281,289), Vec3i(280,289,281), Vec3i(145,152,430), Vec3i(443,430,152), Vec3i(148,429,154), Vec3i(441,154,429), Vec3i(424,430,294), Vec3i(294,307,276), Vec3i(423,296,429), Vec3i(296,279,305), Vec3i(425,440,100), Vec3i(92,100,440), Vec3i(290,442,291), Vec3i(282,291,442), Vec3i(292,293,438), Vec3i(284,438,293), Vec3i(298,320,241), Vec3i(432,440,298), Vec3i(300,236,322), Vec3i(433,300,444), Vec3i(426,101,444), Vec3i(89,444,101), Vec3i(107,448,302), Vec3i(302,79,54), Vec3i(78,31,343), Vec3i(107,78,448), Vec3i(75,79,448), Vec3i(302,448,79), Vec3i(78,343,448), Vec3i(75,448,343), Vec3i(427,418,259), Vec3i(425,259,418), Vec3i(428,262,417), Vec3i(426,417,262), Vec3i(437,449,359), Vec3i(447,359,449), Vec3i(434,361,450), Vec3i(446,450,361), Vec3i(32,33,397), Vec3i(78,33,32), Vec3i(53,303,54), Vec3i(302,54,303), Vec3i(152,153,443), Vec3i(438,443,153), Vec3i(429,304,441), Vec3i(282,441,304), Vec3i(430,443,306), Vec3i(284,306,443), Vec3i(154,441,155), Vec3i(442,155,441), Vec3i(298,299,432), Vec3i(286,432,299), Vec3i(300,433,301), Vec3i(288,301,433), Vec3i(185,451,308), Vec3i(308,74,7), Vec3i(73,15,337), Vec3i(185,73,451), Vec3i(35,74,451), Vec3i(308,451,74), Vec3i(73,337,451), Vec3i(35,451,337), Vec3i(158,452,310), Vec3i(310,72,42), Vec3i(70,22,341), Vec3i(158,70,452), Vec3i(66,72,452), Vec3i(310,452,72), Vec3i(70,341,452), Vec3i(66,452,341), Vec3i(313,327,314), Vec3i(315,314,327), Vec3i(316,317,326), Vec3i(318,326,317), Vec3i(15,156,311), Vec3i(356,311,156), Vec3i(7,312,157), Vec3i(358,157,312), Vec3i(211,9,327), Vec3i(364,327,9), Vec3i(38,326,94), Vec3i(363,94,326), Vec3i(294,295,424), Vec3i(264,424,295), Vec3i(296,423,297), Vec3i(267,297,423), Vec3i(262,149,426), Vec3i(101,426,149), Vec3i(258,319,425), Vec3i(440,425,319), Vec3i(261,426,321), Vec3i(444,321,426), Vec3i(259,425,146), Vec3i(100,146,425), Vec3i(306,307,430), Vec3i(294,430,307), Vec3i(304,429,305), Vec3i(296,305,429), Vec3i(319,320,440), Vec3i(298,440,320), Vec3i(321,444,322), Vec3i(300,322,444), Vec3i(445,283,442), Vec3i(155,442,283), Vec3i(439,438,285), Vec3i(153,285,438), Vec3i(17,68,18), Vec3i(21,18,68), Vec3i(46,184,47), Vec3i(221,47,184), Vec3i(102,95,363), Vec3i(94,363,95), Vec3i(9,11,364), Vec3i(103,364,11), Vec3i(6,323,357), Vec3i(370,357,323), Vec3i(371,324,355), Vec3i(208,355,324), Vec3i(270,363,325), Vec3i(326,325,363), Vec3i(327,364,328), Vec3i(273,328,364), Vec3i(0,2,39), Vec3i(12,39,2), Vec3i(90,93,91), Vec3i(40,91,93), Vec3i(14,16,17), Vec3i(73,17,16), Vec3i(45,309,7), Vec3i(308,7,309), Vec3i(12,71,39), Vec3i(70,39,71), Vec3i(40,41,42), Vec3i(310,42,41), Vec3i(97,98,63), Vec3i(14,63,98), Vec3i(3,5,7), Vec3i(45,7,5), Vec3i(118,377,329), Vec3i(330,329,377), Vec3i(331,372,332), Vec3i(86,332,372), Vec3i(333,376,334), Vec3i(335,334,376), Vec3i(115,373,336), Vec3i(169,336,373), Vec3i(167,166,380), Vec3i(330,380,166), Vec3i(80,81,382), Vec3i(335,382,81), Vec3i(86,385,81), Vec3i(168,81,385), Vec3i(169,384,82), Vec3i(87,82,384), Vec3i(159,88,372), Vec3i(87,372,88), Vec3i(163,164,376), Vec3i(167,376,164), Vec3i(24,26,377), Vec3i(80,377,26), Vec3i(56,57,373), Vec3i(168,373,57), Vec3i(32,397,30), Vec3i(29,30,397), Vec3i(58,60,53), Vec3i(303,53,60), Vec3i(205,181,119), Vec3i(118,119,181), Vec3i(163,175,165), Vec3i(122,165,175), Vec3i(453,454,455), Vec3i(454,456,455), Vec3i(457,455,456), Vec3i(458,455,457), Vec3i(459,455,458), Vec3i(460,455,459), Vec3i(461,462,463), Vec3i(464,465,466), Vec3i(467,468,469), Vec3i(470,471,472), Vec3i(465,473,474), Vec3i(475,476,477), Vec3i(478,479,480), Vec3i(481,482,478), Vec3i(483,484,481), Vec3i(485,486,483), Vec3i(487,488,485), Vec3i(489,490,487), Vec3i(491,492,489), Vec3i(493,494,491), Vec3i(495,496,493), Vec3i(497,498,495), Vec3i(499,500,497), Vec3i(501,502,499), Vec3i(503,504,501), Vec3i(505,504,503), Vec3i(506,504,505), Vec3i(507,504,506), Vec3i(508,504,507), Vec3i(509,504,508), Vec3i(510,504,509), Vec3i(511,504,510), Vec3i(512,504,511), Vec3i(513,504,512), Vec3i(514,504,513), Vec3i(476,515,516), Vec3i(517,518,519), Vec3i(520,517,521), Vec3i(518,522,523), Vec3i(522,480,479), Vec3i(524,525,526), Vec3i(468,470,527), Vec3i(525,467,528), Vec3i(529,475,530), Vec3i(531,532,533), Vec3i(534,531,535), Vec3i(536,537,538), Vec3i(473,539,540), Vec3i(539,536,541), Vec3i(537,534,542), Vec3i(471,520,543), Vec3i(532,529,544), Vec3i(545,524,546), Vec3i(453,461,547), Vec3i(463,464,548), Vec3i(523,549,504), Vec3i(527,550,551), Vec3i(519,552,553), Vec3i(521,554,555), Vec3i(466,556,557), Vec3i(469,558,559), Vec3i(528,560,561), Vec3i(477,562,563), Vec3i(543,564,565), Vec3i(535,566,567), Vec3i(530,568,569), Vec3i(540,570,571), Vec3i(474,572,573), Vec3i(542,574,575), Vec3i(538,576,577), Vec3i(541,578,579), Vec3i(472,580,581), Vec3i(526,582,583), Vec3i(533,584,585), Vec3i(544,586,587), Vec3i(516,545,588), Vec3i(588,589,590), Vec3i(455,460,4), Vec3i(591,592,63), Vec3i(462,455,4), Vec3i(592,547,63), Vec3i(547,548,63), Vec3i(465,462,4), Vec3i(548,557,63), Vec3i(127,124,501), Vec3i(127,501,499), Vec3i(505,503,124), Vec3i(124,126,507), Vec3i(124,507,506), Vec3i(509,508,126), Vec3i(126,134,512), Vec3i(126,512,511), Vec3i(510,509,126), Vec3i(128,127,493), Vec3i(128,493,491), Vec3i(497,495,127), Vec3i(489,487,128), Vec3i(140,128,483), Vec3i(140,483,481), Vec3i(487,485,128), Vec3i(478,480,140), Vec3i(480,522,140), Vec3i(514,513,134), Vec3i(504,514,134), Vec3i(551,581,437), Vec3i(471,470,434), Vec3i(445,447,555), Vec3i(445,555,553), Vec3i(134,445,553), Vec3i(134,553,504), Vec3i(446,439,518), Vec3i(446,518,517), Vec3i(439,140,522), Vec3i(439,522,518), Vec3i(515,476,358), Vec3i(563,588,356), Vec3i(557,573,63), Vec3i(473,465,4), Vec3i(437,360,559), Vec3i(437,559,551), Vec3i(360,371,561), Vec3i(360,561,559), Vec3i(362,434,470), Vec3i(362,470,468), Vec3i(370,362,468), Vec3i(370,468,467), Vec3i(499,497,127), Vec3i(506,505,124), Vec3i(495,493,127), Vec3i(513,512,134), Vec3i(481,478,140), Vec3i(447,449,565), Vec3i(447,565,555), Vec3i(450,446,517), Vec3i(450,517,520), Vec3i(356,156,569), Vec3i(356,569,563), Vec3i(157,358,476), Vec3i(157,476,475), Vec3i(357,370,467), Vec3i(357,467,525), Vec3i(371,355,583), Vec3i(371,583,561), Vec3i(460,459,4), Vec3i(63,62,593), Vec3i(63,593,591), Vec3i(62,4,459), Vec3i(62,459,458), Vec3i(532,531,104), Vec3i(531,534,104), Vec3i(567,585,105), Vec3i(575,567,105), Vec3i(4,3,539), Vec3i(4,539,473), Vec3i(536,539,3), Vec3i(97,63,573), Vec3i(97,573,571), Vec3i(571,579,97), Vec3i(99,97,579), Vec3i(99,579,577), Vec3i(105,99,577), Vec3i(105,577,575), Vec3i(96,104,534), Vec3i(96,534,537), Vec3i(3,96,537), Vec3i(3,537,536), Vec3i(503,501,124), Vec3i(508,507,126), Vec3i(491,489,128), Vec3i(511,510,126), Vec3i(485,483,128), Vec3i(434,450,520), Vec3i(434,520,471), Vec3i(449,437,581), Vec3i(449,581,565), Vec3i(156,105,585), Vec3i(156,585,587), Vec3i(587,569,156), Vec3i(104,157,529), Vec3i(104,529,532), Vec3i(475,529,157), Vec3i(590,583,355), Vec3i(355,356,588), Vec3i(355,588,590), Vec3i(358,357,524), Vec3i(358,524,515), Vec3i(525,524,357), Vec3i(458,457,62), Vec3i(457,593,62), Vec3i(479,478,482), Vec3i(479,504,549), Vec3i(479,482,504), Vec3i(482,481,484), Vec3i(472,551,550), Vec3i(581,551,472), Vec3i(482,484,504), Vec3i(484,483,486), Vec3i(523,553,552), Vec3i(504,553,523), Vec3i(540,573,572), Vec3i(571,573,540), Vec3i(544,585,584), Vec3i(587,585,544), Vec3i(542,577,576), Vec3i(575,577,542), Vec3i(526,590,589), Vec3i(583,590,526), Vec3i(535,575,574), Vec3i(567,575,535), Vec3i(533,567,566), Vec3i(585,567,533), Vec3i(538,579,578), Vec3i(577,579,538), Vec3i(543,581,580), Vec3i(565,581,543), Vec3i(477,569,568), Vec3i(563,569,477), Vec3i(530,587,586), Vec3i(569,587,530), Vec3i(541,571,570), Vec3i(579,571,541), Vec3i(528,583,582), Vec3i(561,583,528), Vec3i(591,453,592), Vec3i(547,592,453), Vec3i(521,565,564), Vec3i(555,565,521), Vec3i(474,557,556), Vec3i(573,557,474), Vec3i(516,563,562), Vec3i(588,563,516), Vec3i(519,555,554), Vec3i(553,555,519), Vec3i(527,559,558), Vec3i(551,559,527), Vec3i(469,561,560), Vec3i(559,561,469), Vec3i(462,461,455), Vec3i(453,455,461), Vec3i(461,463,547), Vec3i(548,547,463), Vec3i(465,464,462), Vec3i(463,462,464), Vec3i(464,466,548), Vec3i(557,548,466), Vec3i(469,560,467), Vec3i(528,467,560), Vec3i(472,550,470), Vec3i(527,470,550), Vec3i(474,556,465), Vec3i(466,465,556), Vec3i(477,568,475), Vec3i(530,475,568), Vec3i(516,562,476), Vec3i(477,476,562), Vec3i(519,554,517), Vec3i(521,517,554), Vec3i(521,564,520), Vec3i(543,520,564), Vec3i(523,552,518), Vec3i(519,518,552), Vec3i(479,549,522), Vec3i(523,522,549), Vec3i(526,589,524), Vec3i(589,546,524), Vec3i(527,558,468), Vec3i(469,468,558), Vec3i(528,582,525), Vec3i(526,525,582), Vec3i(530,586,529), Vec3i(544,529,586), Vec3i(533,566,531), Vec3i(535,531,566), Vec3i(535,574,534), Vec3i(542,534,574), Vec3i(538,578,536), Vec3i(541,536,578), Vec3i(540,572,473), Vec3i(474,473,572), Vec3i(541,570,539), Vec3i(540,539,570), Vec3i(542,576,537), Vec3i(538,537,576), Vec3i(543,580,471), Vec3i(472,471,580), Vec3i(544,584,532), Vec3i(533,532,584), Vec3i(524,545,515), Vec3i(516,515,545), Vec3i(545,546,588), Vec3i(589,588,546), Vec3i(453,591,454), Vec3i(593,454,591), Vec3i(484,486,504), Vec3i(486,485,488), Vec3i(486,488,504), Vec3i(488,487,490), Vec3i(488,490,504), Vec3i(490,489,492), Vec3i(490,492,504), Vec3i(492,491,494), Vec3i(492,494,504), Vec3i(494,493,496), Vec3i(494,496,504), Vec3i(496,495,498), Vec3i(496,498,504), Vec3i(498,497,500), Vec3i(498,500,504), Vec3i(500,499,502), Vec3i(500,502,504), Vec3i(501,504,502), Vec3i(454,593,456), Vec3i(457,456,593), Vec3i(594,595,596), Vec3i(597,598,594), Vec3i(599,597,594), Vec3i(600,599,594), Vec3i(601,600,594), Vec3i(602,601,594), Vec3i(603,602,594), Vec3i(604,603,594), Vec3i(605,604,594), Vec3i(606,607,608), Vec3i(609,606,608), Vec3i(610,609,608), Vec3i(611,610,608), Vec3i(612,611,608), Vec3i(613,612,608), Vec3i(614,613,608), Vec3i(615,614,608), Vec3i(616,615,608), Vec3i(617,616,608), Vec3i(618,617,608), Vec3i(619,618,608), Vec3i(620,619,608), Vec3i(596,608,607), Vec3i(595,594,598), Vec3i(608,596,595), Vec3i(605,594,91), Vec3i(91,338,602), Vec3i(91,602,603), Vec3i(598,597,1), Vec3i(594,596,91), Vec3i(608,595,1), Vec3i(595,598,1), Vec3i(616,617,392), Vec3i(610,611,394), Vec3i(419,421,613), Vec3i(419,613,614), Vec3i(422,427,607), Vec3i(422,607,606), Vec3i(427,91,596), Vec3i(427,596,607), Vec3i(428,420,619), Vec3i(428,619,620), Vec3i(1,428,620), Vec3i(1,620,608), Vec3i(420,409,618), Vec3i(420,618,619), Vec3i(411,422,606), Vec3i(411,606,609), Vec3i(398,419,614), Vec3i(398,614,615), Vec3i(421,400,612), Vec3i(421,612,613), Vec3i(409,392,617), Vec3i(409,617,618), Vec3i(394,411,609), Vec3i(394,609,610), Vec3i(604,605,91), Vec3i(338,1,599), Vec3i(338,599,600), Vec3i(392,398,615), Vec3i(392,615,616), Vec3i(400,394,611), Vec3i(400,611,612), Vec3i(603,604,91), Vec3i(601,602,338), Vec3i(597,599,1), Vec3i(600,601,338) + }); + break; + case TestMesh::gt2_teeth: + vertices = std::vector({ + 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({ + Vec3i(0,1,2), Vec3i(0,3,1), Vec3i(0,4,3), Vec3i(5,6,7), Vec3i(8,6,5), Vec3i(2,9,0), Vec3i(6,10,11), Vec3i(12,13,14), Vec3i(15,16,17), Vec3i(18,16,15), Vec3i(19,20,21), Vec3i(22,23,24), Vec3i(25,23,22), Vec3i(26,23,25), Vec3i(27,23,26), Vec3i(28,23,27), Vec3i(29,30,31), Vec3i(29,32,30), Vec3i(29,28,32), Vec3i(33,28,29), Vec3i(33,23,28), Vec3i(21,23,33), Vec3i(20,23,21), Vec3i(34,35,36), Vec3i(37,35,34), Vec3i(38,35,37), Vec3i(39,35,38), Vec3i(40,35,39), Vec3i(41,35,40), Vec3i(42,35,41), Vec3i(43,35,42), Vec3i(44,35,43), Vec3i(45,35,44), Vec3i(46,35,45), Vec3i(47,35,46), Vec3i(48,35,47), Vec3i(49,50,51), Vec3i(52,48,47), Vec3i(23,49,24), Vec3i(53,54,55), Vec3i(56,57,58), Vec3i(59,57,56), Vec3i(60,57,59), Vec3i(61,57,60), Vec3i(62,57,61), Vec3i(63,57,62), Vec3i(64,57,63), Vec3i(65,57,64), Vec3i(66,57,65), Vec3i(13,57,66), Vec3i(54,67,55), Vec3i(68,69,70), Vec3i(71,69,68), Vec3i(72,69,71), Vec3i(73,69,72), Vec3i(74,69,73), Vec3i(75,69,74), Vec3i(76,69,75), Vec3i(77,69,76), Vec3i(67,69,77), Vec3i(70,16,68), Vec3i(70,17,16), Vec3i(78,79,80), Vec3i(81,79,78), Vec3i(82,79,81), Vec3i(83,79,82), Vec3i(84,79,83), Vec3i(85,79,84), Vec3i(86,79,85), Vec3i(87,79,86), Vec3i(88,8,5), Vec3i(11,7,6), Vec3i(11,89,7), Vec3i(11,9,89), Vec3i(11,0,9), Vec3i(55,90,53), Vec3i(55,79,90), Vec3i(55,80,79), Vec3i(91,11,10), Vec3i(92,69,12), Vec3i(92,70,69), Vec3i(34,93,37), Vec3i(47,94,52), Vec3i(47,95,94), Vec3i(47,57,95), Vec3i(47,58,57), Vec3i(51,24,49), Vec3i(21,35,19), Vec3i(21,36,35), Vec3i(14,92,12), Vec3i(86,10,87), Vec3i(86,91,10), Vec3i(77,55,67), Vec3i(66,14,13), Vec3i(96,97,4), Vec3i(98,99,100), Vec3i(101,102,98), Vec3i(103,101,98), Vec3i(104,103,98), Vec3i(105,106,107), Vec3i(108,105,107), Vec3i(109,108,107), Vec3i(100,109,107), Vec3i(110,111,112), Vec3i(113,110,112), Vec3i(114,115,116), Vec3i(117,114,116), Vec3i(118,119,120), Vec3i(121,122,123), Vec3i(124,121,123), Vec3i(125,126,127), Vec3i(128,129,130), Vec3i(131,132,133), Vec3i(71,131,133), Vec3i(134,71,133), Vec3i(135,134,133), Vec3i(136,135,133), Vec3i(137,138,139), Vec3i(140,137,139), Vec3i(141,140,139), Vec3i(142,31,141), Vec3i(142,141,139), Vec3i(143,126,132), Vec3i(144,145,146), Vec3i(147,144,146), Vec3i(127,147,146), Vec3i(148,121,124), Vec3i(149,148,124), Vec3i(150,149,124), Vec3i(151,150,124), Vec3i(152,151,124), Vec3i(153,152,124), Vec3i(154,153,124), Vec3i(155,154,124), Vec3i(129,156,157), Vec3i(130,129,157), Vec3i(158,159,160), Vec3i(161,158,160), Vec3i(162,161,160), Vec3i(163,162,160), Vec3i(146,163,160), Vec3i(164,165,166), Vec3i(167,164,166), Vec3i(168,169,170), Vec3i(171,168,170), Vec3i(139,171,170), Vec3i(159,172,173), Vec3i(123,174,142), Vec3i(175,110,113), Vec3i(173,175,113), Vec3i(106,176,177), Vec3i(178,106,177), Vec3i(179,180,167), Vec3i(112,179,167), Vec3i(175,173,172), Vec3i(119,118,181), Vec3i(119,181,97), Vec3i(119,97,96), Vec3i(182,98,102), Vec3i(182,102,183), Vec3i(182,183,120), Vec3i(182,120,119), Vec3i(143,132,184), Vec3i(184,185,143), Vec3i(147,127,126), Vec3i(174,123,122), Vec3i(159,173,160), Vec3i(126,125,133), Vec3i(126,133,132), Vec3i(186,187,188), Vec3i(186,188,116), Vec3i(186,116,115), Vec3i(99,98,182), Vec3i(109,100,99), Vec3i(106,178,107), Vec3i(114,117,177), Vec3i(114,177,176), Vec3i(128,130,187), Vec3i(128,187,186), Vec3i(135,136,157), Vec3i(135,157,156), Vec3i(163,146,145), Vec3i(164,167,180), Vec3i(179,112,111), Vec3i(171,139,138), Vec3i(189,155,166), Vec3i(189,166,165), Vec3i(149,150,93), Vec3i(154,155,189), Vec3i(31,142,174), Vec3i(114,176,78), Vec3i(81,78,176), Vec3i(7,89,183), Vec3i(89,9,120), Vec3i(89,120,183), Vec3i(78,80,114), Vec3i(176,106,81), Vec3i(88,5,103), Vec3i(183,102,7), Vec3i(118,120,9), Vec3i(9,2,181), Vec3i(9,181,118), Vec3i(115,114,80), Vec3i(82,81,106), Vec3i(101,103,5), Vec3i(102,101,5), Vec3i(5,7,102), Vec3i(97,181,2), Vec3i(2,1,97), Vec3i(1,3,97), Vec3i(80,55,115), Vec3i(172,159,59), Vec3i(59,56,172), Vec3i(3,4,97), Vec3i(4,0,96), Vec3i(105,108,82), Vec3i(186,115,55), Vec3i(82,106,105), Vec3i(83,82,108), Vec3i(60,59,159), Vec3i(175,172,56), Vec3i(119,96,0), Vec3i(0,11,119), Vec3i(108,109,84), Vec3i(84,83,108), Vec3i(55,77,186), Vec3i(56,58,110), Vec3i(56,110,175), Vec3i(60,159,158), Vec3i(11,91,182), Vec3i(182,119,11), Vec3i(91,86,182), Vec3i(85,84,109), Vec3i(86,85,99), Vec3i(128,186,77), Vec3i(58,111,110), Vec3i(158,161,60), Vec3i(26,25,137), Vec3i(138,137,25), Vec3i(99,182,86), Vec3i(109,99,85), Vec3i(77,76,128), Vec3i(58,47,111), Vec3i(61,60,161), Vec3i(137,140,26), Vec3i(27,26,140), Vec3i(25,22,138), Vec3i(129,128,76), Vec3i(76,75,129), Vec3i(75,74,129), Vec3i(74,73,156), Vec3i(73,72,135), Vec3i(68,16,184), Vec3i(68,184,132), Vec3i(16,18,185), Vec3i(161,162,62), Vec3i(62,61,161), Vec3i(179,111,47), Vec3i(171,138,22), Vec3i(156,129,74), Vec3i(135,156,73), Vec3i(134,135,72), Vec3i(72,71,134), Vec3i(68,132,131), Vec3i(185,184,16), Vec3i(18,15,185), Vec3i(63,62,162), Vec3i(28,27,140), Vec3i(22,24,171), Vec3i(71,68,131), Vec3i(15,17,143), Vec3i(15,143,185), Vec3i(17,70,143), Vec3i(70,92,126), Vec3i(162,163,64), Vec3i(64,63,162), Vec3i(180,179,47), Vec3i(47,46,180), Vec3i(140,141,28), Vec3i(168,171,24), Vec3i(126,143,70), Vec3i(92,14,147), Vec3i(147,126,92), Vec3i(14,66,144), Vec3i(14,144,147), Vec3i(65,64,163), Vec3i(66,65,145), Vec3i(46,45,180), Vec3i(32,28,141), Vec3i(24,51,168), Vec3i(145,144,66), Vec3i(163,145,65), Vec3i(164,180,45), Vec3i(45,44,164), Vec3i(44,43,164), Vec3i(43,42,165), Vec3i(38,37,151), Vec3i(150,151,37), Vec3i(37,93,150), Vec3i(141,31,30), Vec3i(30,32,141), Vec3i(169,168,51), Vec3i(165,164,43), Vec3i(189,165,42), Vec3i(42,41,189), Vec3i(40,39,152), Vec3i(40,152,153), Vec3i(151,152,39), Vec3i(39,38,151), Vec3i(93,34,149), Vec3i(154,189,41), Vec3i(153,154,41), Vec3i(41,40,153), Vec3i(148,149,34), Vec3i(34,36,148), Vec3i(36,21,121), Vec3i(31,174,29), Vec3i(121,148,36), Vec3i(21,33,122), Vec3i(21,122,121), Vec3i(33,29,122), Vec3i(174,122,29), Vec3i(116,188,53), Vec3i(104,98,10), Vec3i(87,10,98), Vec3i(98,100,87), Vec3i(79,87,100), Vec3i(79,100,107), Vec3i(90,79,107), Vec3i(90,107,178), Vec3i(178,177,90), Vec3i(53,90,177), Vec3i(53,177,117), Vec3i(117,116,53), Vec3i(54,53,188), Vec3i(54,188,187), Vec3i(67,54,187), Vec3i(67,187,130), Vec3i(69,67,130), Vec3i(69,130,157), Vec3i(12,69,157), Vec3i(12,157,136), Vec3i(136,133,12), Vec3i(12,133,125), Vec3i(125,127,12), Vec3i(13,12,127), Vec3i(127,146,13), Vec3i(57,13,146), Vec3i(57,146,160), Vec3i(95,57,160), Vec3i(95,160,173), Vec3i(173,113,95), Vec3i(94,95,113), Vec3i(113,112,94), Vec3i(52,94,112), Vec3i(48,52,112), Vec3i(112,167,48), Vec3i(35,48,167), Vec3i(35,167,166), Vec3i(19,35,166), Vec3i(139,170,50), Vec3i(50,49,139), Vec3i(166,155,19), Vec3i(20,19,155), Vec3i(155,124,20), Vec3i(23,20,124), Vec3i(23,124,123), Vec3i(49,23,123), Vec3i(49,123,142), Vec3i(142,139,49), Vec3i(190,191,170), Vec3i(192,191,190), Vec3i(191,192,51), Vec3i(191,51,50), Vec3i(170,169,190), Vec3i(169,51,192), Vec3i(169,192,190), Vec3i(170,191,50), Vec3i(193,194,195), Vec3i(196,197,198), Vec3i(199,200,201), Vec3i(198,202,203), Vec3i(204,201,200), Vec3i(205,204,200), Vec3i(206,207,208), Vec3i(206,208,205), Vec3i(206,205,200), Vec3i(207,206,209), Vec3i(207,209,203), Vec3i(207,203,202), Vec3i(202,198,197), Vec3i(197,196,210), Vec3i(197,210,195), Vec3i(197,195,194), Vec3i(8,88,195), Vec3i(8,195,210), Vec3i(210,196,8), Vec3i(196,198,8), Vec3i(198,203,8), Vec3i(203,209,8), Vec3i(209,206,8), Vec3i(206,200,8), Vec3i(202,197,104), Vec3i(207,202,104), Vec3i(103,104,197), Vec3i(103,197,194), Vec3i(193,195,88), Vec3i(88,103,194), Vec3i(88,194,193), Vec3i(200,199,8), Vec3i(199,201,8), Vec3i(204,205,6), Vec3i(6,8,201), Vec3i(6,201,204), Vec3i(10,6,205), Vec3i(10,205,208), Vec3i(104,10,208), Vec3i(104,208,207) + }); + break; + case TestMesh::pyramid: + vertices = std::vector({ + Vec3d(10,10,40), Vec3d(0,0,0), Vec3d(20,0,0), Vec3d(20,20,0), Vec3d(0,20,0) + }); + facets = std::vector({ + Vec3i(0,1,2), Vec3i(0,3,4), Vec3i(3,1,4), Vec3i(1,3,2), Vec3i(3,0,2), Vec3i(4,1,0) + }); + break; + case TestMesh::two_hollow_squares: + vertices = std::vector({ + 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({ + Vec3i(0,1,2), Vec3i(3,4,5), Vec3i(6,4,0), Vec3i(6,0,2), Vec3i(2,1,5), Vec3i(7,4,3), Vec3i(1,3,5), Vec3i(0,4,7), Vec3i(4,6,8), Vec3i(6,9,8), Vec3i(4,8,10), Vec3i(6,2,9), Vec3i(2,11,9), Vec3i(2,12,11), Vec3i(2,5,12), Vec3i(5,13,12), Vec3i(5,14,13), Vec3i(4,10,15), Vec3i(5,4,14), Vec3i(4,15,14), Vec3i(7,16,17), Vec3i(0,7,18), Vec3i(7,17,18), Vec3i(1,19,20), Vec3i(1,0,19), Vec3i(0,18,19), Vec3i(7,3,21), Vec3i(3,22,21), Vec3i(7,21,16), Vec3i(3,23,22), Vec3i(3,1,23), Vec3i(1,20,23), Vec3i(24,25,26), Vec3i(25,27,26), Vec3i(25,28,27), Vec3i(29,24,30), Vec3i(24,31,30), Vec3i(24,26,31), Vec3i(32,29,33), Vec3i(29,30,33), Vec3i(32,33,34), Vec3i(32,34,35), Vec3i(25,32,28), Vec3i(32,35,28), Vec3i(36,37,24), Vec3i(38,32,25), Vec3i(29,32,36), Vec3i(29,36,24), Vec3i(24,37,25), Vec3i(39,32,38), Vec3i(37,38,25), Vec3i(36,32,39), Vec3i(39,40,41), Vec3i(36,39,42), Vec3i(39,41,42), Vec3i(37,43,44), Vec3i(37,36,43), Vec3i(36,42,43), Vec3i(39,38,45), Vec3i(38,46,45), Vec3i(39,45,40), Vec3i(38,47,46), Vec3i(38,37,47), Vec3i(37,44,47), Vec3i(16,8,9), Vec3i(16,10,8), Vec3i(10,16,15), Vec3i(15,16,21), Vec3i(22,15,21), Vec3i(15,22,14), Vec3i(22,23,14), Vec3i(23,20,14), Vec3i(17,16,9), Vec3i(18,17,9), Vec3i(19,18,9), Vec3i(19,9,11), Vec3i(19,11,20), Vec3i(13,14,20), Vec3i(20,11,12), Vec3i(13,20,12), Vec3i(41,40,30), Vec3i(42,41,30), Vec3i(43,42,30), Vec3i(43,30,31), Vec3i(43,31,44), Vec3i(27,28,44), Vec3i(44,31,26), Vec3i(27,44,26), Vec3i(40,33,30), Vec3i(40,34,33), Vec3i(34,40,35), Vec3i(35,40,45), Vec3i(46,35,45), Vec3i(35,46,28), Vec3i(46,47,28), Vec3i(47,44,28) , + }); + break; + case TestMesh::small_dorito: + vertices = std::vector({ + 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({ + Vec3i(0,1,2), Vec3i(3,4,5), Vec3i(2,1,4), Vec3i(2,4,3), Vec3i(2,3,5), Vec3i(2,5,0), Vec3i(5,4,1), Vec3i(5,1,0) + }); + break; + case TestMesh::bridge: + vertices = std::vector({ + 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({ + Vec3i(0,1,2), Vec3i(1,0,3), Vec3i(2,1,4), Vec3i(2,5,0), Vec3i(0,6,3), Vec3i(1,3,7), Vec3i(1,8,4), Vec3i(4,9,2), Vec3i(10,5,2), Vec3i(5,6,0), Vec3i(6,11,3), Vec3i(3,12,7), Vec3i(7,8,1), Vec3i(4,8,11), Vec3i(4,11,9), Vec3i(9,10,2), Vec3i(10,13,5), Vec3i(14,6,5), Vec3i(9,11,6), Vec3i(11,12,3), Vec3i(12,8,7), Vec3i(11,8,15), Vec3i(13,10,9), Vec3i(5,13,14), Vec3i(14,13,6), Vec3i(6,13,9), Vec3i(15,12,11), Vec3i(15,8,12) + }); + break; + case TestMesh::bridge_with_hole: + vertices = std::vector({ + 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({ + Vec3i(0,1,2), Vec3i(1,0,3), Vec3i(1,3,4), Vec3i(4,3,5), Vec3i(2,6,7), Vec3i(6,2,1), Vec3i(7,6,5), Vec3i(7,5,3), Vec3i(5,8,9), Vec3i(8,5,6), Vec3i(10,11,12), Vec3i(11,10,13), Vec3i(14,8,15), Vec3i(8,14,9), Vec3i(2,16,17), Vec3i(16,2,15), Vec3i(15,2,14), Vec3i(14,10,12), Vec3i(10,14,7), Vec3i(7,14,2), Vec3i(16,18,19), Vec3i(18,16,20), Vec3i(20,16,1), Vec3i(1,16,8), Vec3i(8,16,15), Vec3i(6,1,8), Vec3i(3,11,13), Vec3i(11,3,21), Vec3i(21,3,18), Vec3i(18,22,19), Vec3i(22,18,0), Vec3i(0,18,3), Vec3i(16,22,17), Vec3i(22,16,19), Vec3i(2,22,0), Vec3i(22,2,17), Vec3i(5,23,4), Vec3i(23,11,21), Vec3i(11,23,12), Vec3i(12,23,9), Vec3i(9,23,5), Vec3i(12,9,14), Vec3i(23,18,20), Vec3i(18,23,21), Vec3i(10,3,13), Vec3i(3,10,7), Vec3i(1,23,20), Vec3i(23,1,4) + }); + break; + case TestMesh::step: + vertices = std::vector({ + 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({ + Vec3i(0,1,2), Vec3i(1,3,2), Vec3i(3,4,5), Vec3i(2,3,5), Vec3i(6,0,2), Vec3i(6,2,7), Vec3i(5,8,7), Vec3i(5,7,2), Vec3i(9,10,8), Vec3i(9,8,5), Vec3i(9,0,6), Vec3i(9,6,10), Vec3i(9,11,1), Vec3i(9,1,0), Vec3i(3,1,11), Vec3i(4,3,11), Vec3i(5,11,9), Vec3i(5,4,11), Vec3i(12,10,6), Vec3i(12,6,13), Vec3i(6,7,14), Vec3i(13,6,14), Vec3i(7,8,15), Vec3i(14,7,15), Vec3i(15,8,10), Vec3i(15,10,12), Vec3i(12,13,14), Vec3i(12,14,15) + }); + break; + case TestMesh::slopy_cube: + vertices = std::vector({ + 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({ + Vec3i(0,1,2) , Vec3i(2,1,3) , Vec3i(4,0,5) , Vec3i(4,1,0) , Vec3i(6,4,7) , Vec3i(7,4,5) , Vec3i(4,8,1) , Vec3i(0,2,5) , Vec3i(5,2,9) , Vec3i(2,10,9) , Vec3i(10,3,11) , + Vec3i(2,3,10) , Vec3i(9,10,12) , Vec3i(13,9,12) , Vec3i(3,1,8) , Vec3i(11,3,8) , Vec3i(10,11,8) , Vec3i(4,10,8) , Vec3i(6,12,10) , Vec3i(4,6,10) , + Vec3i(7,13,12) , Vec3i(6,7,12) , Vec3i(7,5,9) , Vec3i(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; +} + + +void init_print(Print& print, std::initializer_list meshes, Slic3r::Model& model, DynamicPrintConfig* _config, bool comments) { + DynamicPrintConfig* config {Slic3r::DynamicPrintConfig::new_from_defaults()}; + config->apply(*_config); + + //const std::string v {std::getenv("SLIC3R_TESTS_GCODE")}; + //auto tests_gcode {(v == "" ? ""s : std::string(v))}; + + //if (tests_gcode != ""s) + // config->set_key_value("gcode_comments", new ConfigOptionBool(true)); + + print.apply_config(*config); + for (const TestMesh& t : meshes) { + ModelObject* object {model.add_object()}; + object->name += std::string(mesh_names.at(t)) + ".stl"s; + object->add_volume(mesh(t)); + + ModelInstance* inst {object->add_instance()}; + inst->set_rotation(Vec3d(0,0,0)); + inst->set_scaling_factor(Vec3d(1, 1, 1)); + } + + model.arrange_objects(print.config().min_object_distance()); + model.center_instances_around_point(Slic3r::Vec2d(100,100)); + for (auto* mo : model.objects) { + print.auto_assign_extruders(mo); + //print.add_model_object(mo); + } + print.apply(model, *config); + + std::string err = print.validate(); + std::cout << "validate result : " << err << ", mempty print? " << print.empty() << "\n"; + +} +void init_print(Print& print, std::initializer_list meshes, Slic3r::Model& model, DynamicPrintConfig* _config, bool comments) { + DynamicPrintConfig* config {Slic3r::DynamicPrintConfig::new_from_defaults()}; + config->apply(*_config); + + const std::string v {std::getenv("SLIC3R_TESTS_GCODE")}; + auto tests_gcode {(v == "" ? ""s : std::string(v))}; + + if (tests_gcode != ""s) + config->set_key_value("gcode_comments", new ConfigOptionBool(true)); + + print.apply_config(*config); + for (const TriangleMesh& t : meshes) { + ModelObject* object {model.add_object()}; + object->name += "object.stl"s; + object->add_volume(t); + + ModelInstance* inst {object->add_instance()}; + inst->set_rotation(Vec3d(0, 0, 0)); + inst->set_scaling_factor(Vec3d(1, 1, 1)); + } + + model.arrange_objects(print.config().min_object_distance()); + model.center_instances_around_point(Slic3r::Vec2d(100,100)); + for (ModelObject* mo : model.objects) { + print.auto_assign_extruders(mo); + print.add_model_object(mo); + } + + print.validate(); + +} + +void gcode(std::string& gcode_path, Print& _print) { + _print.process(); + gcode_path = _print.export_gcode(gcode_path, nullptr); +} + +std::string read_to_string(const std::string& name) { + std::stringstream buf; + std::ifstream f(testfile(name)); + if (!f.good()) f = std::ifstream(name); + if (!f.good()) return ""s; + std::copy(std::istreambuf_iterator(f), + std::istreambuf_iterator(), + std::ostreambuf_iterator(buf)); + f.close(); + return buf.str(); +} + +void clean_file(const std::string& name, const std::string& ext, bool glob) { + std::string filename{ "" }; + filename.append(name); + filename.append("."); + filename.append(ext); + + std::remove(testfile(filename).c_str()); +} + +Slic3r::Model model(const std::string& model_name, TriangleMesh&& _mesh) { + Slic3r::Model result; + + ModelObject* object {result.add_object()}; + object->name += model_name + ".stl"s; + object->add_volume(_mesh); + + ModelInstance* inst {object->add_instance()}; + inst->set_rotation(Vec3d(0, 0, 0)); + inst->set_scaling_factor(Vec3d(1, 1, 1)); + + 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"s; + object->add_volume(_mesh); + + ModelInstance* inst {object->add_instance()}; + inst->set_rotation(Vec3d(0, 0, 0)); + inst->set_scaling_factor(Vec3d(1, 1, 1)); +} + +} } // namespace Slic3r::Test diff --git a/src/test/test_data.hpp b/src/test/test_data.hpp new file mode 100644 index 000000000..192ecc76d --- /dev/null +++ b/src/test/test_data.hpp @@ -0,0 +1,79 @@ +#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 "test_options.hpp" + +#include + +namespace Slic3r { namespace Test { + +/// 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 (tm); + } +}; + +/// Mesh enumeration to name mapping +extern const std::unordered_map 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, Vec3f translate, Vec3f scale = Vec3f(1.0, 1.0, 1.0)); +TriangleMesh mesh(TestMesh m, Vec3f translate, double scale = 1.0); + +/// Templated function to see if two values are equivalent (+/- epsilon) +template +bool _equiv(const T& a, const T& b) { return abs(a - b) < Slic3r::Geometry::epsilon; } + +template +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, Vec3f translate = Vec3f(0,0,0), Vec3f scale = Vec3f(1.0,1.0,1.0)); +//Slic3r::Model model(const std::string& model_name, TestMesh m, Vec3f translate = Vec3f(0,0,0), double scale = 1.0); + +Slic3r::Model model(const std::string& model_name, TriangleMesh&& _mesh); + +void init_print(Print& print, std::initializer_list meshes, Slic3r::Model& model, DynamicPrintConfig* _config = Slic3r::DynamicPrintConfig::new_from_defaults(), bool comments = false); +void init_print(Print& print, std::initializer_list meshes, Slic3r::Model& model, DynamicPrintConfig* _config = Slic3r::DynamicPrintConfig::new_from_defaults(), bool comments = false); + +void gcode(std::string& gcode, Print& print); + + +std::string read_to_string(const std::string& name); +void clean_file(const std::string& name, const std::string& ext, bool glob = false); + +} } // namespace Slic3r::Test + + +#endif // SLIC3R_TEST_DATA_HPP diff --git a/src/test/test_harness.cpp b/src/test/test_harness.cpp new file mode 100644 index 000000000..3c3c6f2f5 --- /dev/null +++ b/src/test/test_harness.cpp @@ -0,0 +1,2 @@ +#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file +#include diff --git a/src/test/test_options.hpp b/src/test/test_options.hpp new file mode 100644 index 000000000..50b818f65 --- /dev/null +++ b/src/test/test_options.hpp @@ -0,0 +1,14 @@ +#ifndef TEST_OPTIONS_HPP +#include + +/// Directory path, passed in from the outside, for the path to the test inputs dir. +constexpr auto* testfile_dir {"C:/local/Slic3rcpp/src/test/inputs/"}; + +inline std::string testfile(std::string filename) { + std::string result; + result.append(testfile_dir); + result.append(filename); + return result; +} + +#endif // TEST_OPTIONS_HPP diff --git a/src/test/test_options.hpp.in b/src/test/test_options.hpp.in new file mode 100644 index 000000000..dd43939dd --- /dev/null +++ b/src/test/test_options.hpp.in @@ -0,0 +1,14 @@ +#ifndef TEST_OPTIONS_HPP +#include + +/// Directory path, passed in from the outside, for the path to the test inputs dir. +constexpr auto* testfile_dir {"@TESTFILE_DIR@"}; + +inline std::string testfile(std::string filename) { + std::string result; + result.append(testfile_dir); + result.append(filename); + return result; +} + +#endif // TEST_OPTIONS_HPP