From 0b31ef6e1e14b0a98306496a84b32f407d86fe2b Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 18 Jul 2023 14:57:47 +0200 Subject: [PATCH] Beginning of arrange gen2 --- sandboxes/CMakeLists.txt | 5 +- sandboxes/print_arrange_polys/CMakeLists.txt | 7 + sandboxes/print_arrange_polys/main.cpp | 103 + src/PrusaSlicer.cpp | 12 +- src/libnest2d/CMakeLists.txt | 9 +- .../backends/libslic3r/geometries.hpp | 12 + src/libslic3r/AnyPtr.hpp | 136 +- src/libslic3r/Arrange.cpp | 3 +- src/libslic3r/Arrange/Arrange.hpp | 236 + src/libslic3r/Arrange/ArrangeImpl.hpp | 450 ++ .../Arrange/ArrangeSettingsDb_AppCfg.cpp | 234 + .../Arrange/ArrangeSettingsDb_AppCfg.hpp | 78 + src/libslic3r/Arrange/ArrangeSettingsView.hpp | 118 + src/libslic3r/Arrange/Core/ArrangeBase.hpp | 258 + .../Arrange/Core/ArrangeFirstFit.hpp | 161 + .../Arrange/Core/ArrangeItemTraits.hpp | 108 + src/libslic3r/Arrange/Core/Beds.cpp | 129 + src/libslic3r/Arrange/Core/Beds.hpp | 191 + .../Arrange/Core/DataStoreTraits.hpp | 78 + .../Arrange/Core/NFP/CircularEdgeIterator.hpp | 110 + src/libslic3r/Arrange/Core/NFP/EdgeCache.cpp | 92 + src/libslic3r/Arrange/Core/NFP/EdgeCache.hpp | 70 + .../Core/NFP/Kernels/CompactifyKernel.hpp | 61 + .../Core/NFP/Kernels/GravityKernel.hpp | 58 + .../Arrange/Core/NFP/Kernels/KernelTraits.hpp | 57 + .../Arrange/Core/NFP/Kernels/KernelUtils.hpp | 75 + .../Kernels/RectangleOverfitKernelWrapper.hpp | 94 + .../Kernels/SVGDebugOutputKernelWrapper.hpp | 96 + .../Core/NFP/Kernels/TMArrangeKernel.hpp | 268 + src/libslic3r/Arrange/Core/NFP/NFP.cpp | 412 ++ src/libslic3r/Arrange/Core/NFP/NFP.hpp | 50 + .../Arrange/Core/NFP/NFPArrangeItemTraits.hpp | 176 + .../Arrange/Core/NFP/NFPConcave_CGAL.cpp | 111 + .../Arrange/Core/NFP/NFPConcave_CGAL.hpp | 14 + .../Arrange/Core/NFP/NFPConcave_Tesselate.cpp | 70 + .../Arrange/Core/NFP/NFPConcave_Tesselate.hpp | 15 + .../Arrange/Core/NFP/PackStrategyNFP.hpp | 279 + .../NFP/RectangleOverfitPackingStrategy.hpp | 122 + src/libslic3r/Arrange/Core/PackingContext.hpp | 124 + .../Arrange/Items/ArbitraryDataStore.hpp | 91 + src/libslic3r/Arrange/Items/ArrangeItem.cpp | 184 + src/libslic3r/Arrange/Items/ArrangeItem.hpp | 461 ++ .../Arrange/Items/SimpleArrangeItem.cpp | 15 + .../Arrange/Items/SimpleArrangeItem.hpp | 178 + .../Arrange/Items/TrafoOnlyArrangeItem.hpp | 79 + .../Arrange/Items/WritableItemTraits.hpp | 112 + src/libslic3r/Arrange/Scene.cpp | 64 + src/libslic3r/Arrange/Scene.hpp | 308 + src/libslic3r/Arrange/SceneBuilder.cpp | 859 +++ src/libslic3r/Arrange/SceneBuilder.hpp | 645 ++ .../Arrange/SegmentedRectangleBed.hpp | 105 + src/libslic3r/Arrange/Tasks/ArrangeTask.hpp | 81 + .../Arrange/Tasks/ArrangeTaskImpl.hpp | 109 + src/libslic3r/Arrange/Tasks/FillBedTask.hpp | 53 + .../Arrange/Tasks/FillBedTaskImpl.hpp | 173 + .../Arrange/Tasks/MultiplySelectionTask.hpp | 108 + .../Tasks/MultiplySelectionTaskImpl.hpp | 120 + src/libslic3r/BoostAdapter.hpp | 139 +- src/libslic3r/BoundingBox.hpp | 35 +- src/libslic3r/CMakeLists.txt | 55 +- src/libslic3r/ClipperUtils.cpp | 8 + src/libslic3r/ClipperUtils.hpp | 4 + src/libslic3r/Geometry/ConvexHull.hpp | 7 +- src/libslic3r/Line.hpp | 28 +- src/libslic3r/MinAreaBoundingBox.cpp | 13 + src/libslic3r/MinAreaBoundingBox.hpp | 4 +- src/libslic3r/Model.cpp | 35 +- src/libslic3r/Model.hpp | 7 +- src/libslic3r/ModelArrange.cpp | 117 +- src/libslic3r/ModelArrange.hpp | 66 +- src/libslic3r/MultiPoint.hpp | 6 + src/libslic3r/Optimize/NLoptOptimizer.hpp | 25 +- src/libslic3r/Optimize/Optimizer.hpp | 9 +- src/libslic3r/Polygon.hpp | 6 + src/libslic3r/Polyline.hpp | 3 + src/libslic3r/PrintConfig.cpp | 15 +- src/libslic3r/PrintConfig.hpp | 3 - src/libslic3r/libslic3r.h | 83 +- src/slic3r/CMakeLists.txt | 8 +- src/slic3r/GUI/ArrangeSettingsDialogImgui.cpp | 134 + src/slic3r/GUI/ArrangeSettingsDialogImgui.hpp | 53 + src/slic3r/GUI/GLCanvas3D.cpp | 278 +- src/slic3r/GUI/GLCanvas3D.hpp | 45 +- src/slic3r/GUI/Jobs/ArrangeJob.cpp | 16 +- src/slic3r/GUI/Jobs/ArrangeJob2.cpp | 207 + src/slic3r/GUI/Jobs/ArrangeJob2.hpp | 138 + src/slic3r/GUI/Plater.cpp | 121 +- src/slic3r/GUI/Plater.hpp | 1 + tests/CMakeLists.txt | 2 +- tests/arrange/CMakeLists.txt | 17 + tests/arrange/arrange_tests_main.cpp | 1 + tests/arrange/test_arrange.cpp | 1122 ++++ tests/arrange/test_arrange_integration.cpp | 1036 +++ tests/data/default_fff.ini | 313 + tests/data/prusaparts.cpp | 5981 +++++++++++++++++ tests/data/prusaparts.hpp | 14 + tests/fff_print/test_data.cpp | 2 +- tests/fff_print/test_model.cpp | 2 +- tests/libnest2d/CMakeLists.txt | 11 - tests/libnest2d/libnest2d_tests_main.cpp | 1233 ---- tests/libnest2d/printer_parts.cpp | 3175 --------- tests/libnest2d/printer_parts.hpp | 14 - tests/libslic3r/CMakeLists.txt | 8 +- tests/libslic3r/test_anyptr.cpp | 198 + tests/libslic3r/test_geometry.cpp | 16 +- tests/libslic3r/test_marchingsquares.cpp | 2 - tests/slic3rutils/CMakeLists.txt | 1 + tests/slic3rutils/slic3r_arrangejob_tests.cpp | 351 + tests/test_utils.hpp | 19 + xs/xsp/Model.xsp | 4 +- 110 files changed, 18609 insertions(+), 5009 deletions(-) create mode 100644 sandboxes/print_arrange_polys/CMakeLists.txt create mode 100644 sandboxes/print_arrange_polys/main.cpp create mode 100644 src/libslic3r/Arrange/Arrange.hpp create mode 100644 src/libslic3r/Arrange/ArrangeImpl.hpp create mode 100644 src/libslic3r/Arrange/ArrangeSettingsDb_AppCfg.cpp create mode 100644 src/libslic3r/Arrange/ArrangeSettingsDb_AppCfg.hpp create mode 100644 src/libslic3r/Arrange/ArrangeSettingsView.hpp create mode 100644 src/libslic3r/Arrange/Core/ArrangeBase.hpp create mode 100644 src/libslic3r/Arrange/Core/ArrangeFirstFit.hpp create mode 100644 src/libslic3r/Arrange/Core/ArrangeItemTraits.hpp create mode 100644 src/libslic3r/Arrange/Core/Beds.cpp create mode 100644 src/libslic3r/Arrange/Core/Beds.hpp create mode 100644 src/libslic3r/Arrange/Core/DataStoreTraits.hpp create mode 100644 src/libslic3r/Arrange/Core/NFP/CircularEdgeIterator.hpp create mode 100644 src/libslic3r/Arrange/Core/NFP/EdgeCache.cpp create mode 100644 src/libslic3r/Arrange/Core/NFP/EdgeCache.hpp create mode 100644 src/libslic3r/Arrange/Core/NFP/Kernels/CompactifyKernel.hpp create mode 100644 src/libslic3r/Arrange/Core/NFP/Kernels/GravityKernel.hpp create mode 100644 src/libslic3r/Arrange/Core/NFP/Kernels/KernelTraits.hpp create mode 100644 src/libslic3r/Arrange/Core/NFP/Kernels/KernelUtils.hpp create mode 100644 src/libslic3r/Arrange/Core/NFP/Kernels/RectangleOverfitKernelWrapper.hpp create mode 100644 src/libslic3r/Arrange/Core/NFP/Kernels/SVGDebugOutputKernelWrapper.hpp create mode 100644 src/libslic3r/Arrange/Core/NFP/Kernels/TMArrangeKernel.hpp create mode 100644 src/libslic3r/Arrange/Core/NFP/NFP.cpp create mode 100644 src/libslic3r/Arrange/Core/NFP/NFP.hpp create mode 100644 src/libslic3r/Arrange/Core/NFP/NFPArrangeItemTraits.hpp create mode 100644 src/libslic3r/Arrange/Core/NFP/NFPConcave_CGAL.cpp create mode 100644 src/libslic3r/Arrange/Core/NFP/NFPConcave_CGAL.hpp create mode 100644 src/libslic3r/Arrange/Core/NFP/NFPConcave_Tesselate.cpp create mode 100644 src/libslic3r/Arrange/Core/NFP/NFPConcave_Tesselate.hpp create mode 100644 src/libslic3r/Arrange/Core/NFP/PackStrategyNFP.hpp create mode 100644 src/libslic3r/Arrange/Core/NFP/RectangleOverfitPackingStrategy.hpp create mode 100644 src/libslic3r/Arrange/Core/PackingContext.hpp create mode 100644 src/libslic3r/Arrange/Items/ArbitraryDataStore.hpp create mode 100644 src/libslic3r/Arrange/Items/ArrangeItem.cpp create mode 100644 src/libslic3r/Arrange/Items/ArrangeItem.hpp create mode 100644 src/libslic3r/Arrange/Items/SimpleArrangeItem.cpp create mode 100644 src/libslic3r/Arrange/Items/SimpleArrangeItem.hpp create mode 100644 src/libslic3r/Arrange/Items/TrafoOnlyArrangeItem.hpp create mode 100644 src/libslic3r/Arrange/Items/WritableItemTraits.hpp create mode 100644 src/libslic3r/Arrange/Scene.cpp create mode 100644 src/libslic3r/Arrange/Scene.hpp create mode 100644 src/libslic3r/Arrange/SceneBuilder.cpp create mode 100644 src/libslic3r/Arrange/SceneBuilder.hpp create mode 100644 src/libslic3r/Arrange/SegmentedRectangleBed.hpp create mode 100644 src/libslic3r/Arrange/Tasks/ArrangeTask.hpp create mode 100644 src/libslic3r/Arrange/Tasks/ArrangeTaskImpl.hpp create mode 100644 src/libslic3r/Arrange/Tasks/FillBedTask.hpp create mode 100644 src/libslic3r/Arrange/Tasks/FillBedTaskImpl.hpp create mode 100644 src/libslic3r/Arrange/Tasks/MultiplySelectionTask.hpp create mode 100644 src/libslic3r/Arrange/Tasks/MultiplySelectionTaskImpl.hpp create mode 100644 src/slic3r/GUI/ArrangeSettingsDialogImgui.cpp create mode 100644 src/slic3r/GUI/ArrangeSettingsDialogImgui.hpp create mode 100644 src/slic3r/GUI/Jobs/ArrangeJob2.cpp create mode 100644 src/slic3r/GUI/Jobs/ArrangeJob2.hpp create mode 100644 tests/arrange/CMakeLists.txt create mode 100644 tests/arrange/arrange_tests_main.cpp create mode 100644 tests/arrange/test_arrange.cpp create mode 100644 tests/arrange/test_arrange_integration.cpp create mode 100644 tests/data/default_fff.ini create mode 100644 tests/data/prusaparts.cpp create mode 100644 tests/data/prusaparts.hpp delete mode 100644 tests/libnest2d/CMakeLists.txt delete mode 100644 tests/libnest2d/libnest2d_tests_main.cpp delete mode 100644 tests/libnest2d/printer_parts.cpp delete mode 100644 tests/libnest2d/printer_parts.hpp create mode 100644 tests/libslic3r/test_anyptr.cpp create mode 100644 tests/slic3rutils/slic3r_arrangejob_tests.cpp diff --git a/sandboxes/CMakeLists.txt b/sandboxes/CMakeLists.txt index f6a4e4a84a..ae760e4487 100644 --- a/sandboxes/CMakeLists.txt +++ b/sandboxes/CMakeLists.txt @@ -1,7 +1,8 @@ #add_subdirectory(slasupporttree) #add_subdirectory(openvdb) # add_subdirectory(meshboolean) -add_subdirectory(its_neighbor_index) +#add_subdirectory(its_neighbor_index) # add_subdirectory(opencsg) #add_subdirectory(aabb-evaluation) -add_subdirectory(wx_gl_test) \ No newline at end of file +#add_subdirectory(wx_gl_test) +add_subdirectory(print_arrange_polys) diff --git a/sandboxes/print_arrange_polys/CMakeLists.txt b/sandboxes/print_arrange_polys/CMakeLists.txt new file mode 100644 index 0000000000..c9f3ed0b17 --- /dev/null +++ b/sandboxes/print_arrange_polys/CMakeLists.txt @@ -0,0 +1,7 @@ +add_executable(print_arrange_polys main.cpp) + +target_link_libraries(print_arrange_polys libslic3r admesh) + +if (WIN32) + prusaslicer_copy_dlls(print_arrange_polys) +endif() diff --git a/sandboxes/print_arrange_polys/main.cpp b/sandboxes/print_arrange_polys/main.cpp new file mode 100644 index 0000000000..c076a9462d --- /dev/null +++ b/sandboxes/print_arrange_polys/main.cpp @@ -0,0 +1,103 @@ +#include +#include + +#include + +#include + +void print_arrange_polygons(const std::string &dirpath, std::ostream &out) +{ + using namespace Slic3r; + + boost::filesystem::path p = dirpath; //"/home/quarky/Workspace/printing/Original-Prusa-i3-MK3/Printed-Parts/stl/"; + + if (!boost::filesystem::exists(p) || !boost::filesystem::is_directory(p)) + return; + + for (const auto& entry : boost::filesystem::directory_iterator(p)) { + if (!boost::filesystem::is_regular_file(entry)) { + continue; + } + + TriangleMesh mesh; + mesh.ReadSTLFile(entry.path().c_str()); + ExPolygons outline = mesh.horizontal_projection(); + + out << "// " << entry.path().filename() << ": " << std::endl; + for (const ExPolygon &expoly : outline) { + out << "MyPoly{\n"; // Start of polygon + + out << "\t{\n"; // Start of contour + for (const auto& point : expoly.contour.points) { + out << " {" << point.x() << ", " << point.y() << "},\n"; // Print point coordinates + } + out << " },\n"; // End of contour + + out << " {\n"; // start of holes + for (const auto& hole : expoly.holes) { + out << " {\n"; // Start of hole + for (const auto& point : hole.points) { + out << " {" << point.x() << ", " << point.y() << "},\n"; // Print point coordinates + } + out << " },\n"; // End of hole Polygon + } + out << " }\n"; // end of holes Polygons + out << "},\n"; // End of ExPolygon + } + } +} + +void print_arrange_items(const std::string &dirpath, std::ostream &out) +{ + using namespace Slic3r; + + boost::filesystem::path p = dirpath; + + if (!boost::filesystem::exists(p) || !boost::filesystem::is_directory(p)) + return; + + for (const auto& entry : boost::filesystem::directory_iterator(p)) { + if (!boost::filesystem::is_regular_file(entry)) { + continue; + } + + TriangleMesh mesh; + mesh.ReadSTLFile(entry.path().c_str()); + ExPolygons outline = mesh.horizontal_projection(); + + out << "ExPolygons{ " << "// " << entry.path().filename() << ":\n"; + for (const ExPolygon &expoly : outline) { + out << " MyPoly{\n"; // Start of polygon + + out << " {\n"; // Start of contour + for (const auto& point : expoly.contour.points) { + out << " {" << point.x() << ", " << point.y() << "},\n"; // Print point coordinates + } + out << " },\n"; // End of contour + + out << " {\n"; // start of holes + for (const auto& hole : expoly.holes) { + out << " {\n"; // Start of hole + for (const auto& point : hole.points) { + out << " {" << point.x() << ", " << point.y() << "},\n"; // Print point coordinates + } + out << " },\n"; // End of hole Polygon + } + out << " }\n"; // end of holes Polygons + out << " },\n"; // End of ExPolygon + } + out << "},\n"; + } +} + +int main(int argc, const char *argv[]) +{ + if (argc <= 1) + return -1; + + std::string dirpath = argv[1]; + + print_arrange_items(dirpath, std::cout); + + return 0; +} diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp index 480f42811e..0d627aa837 100644 --- a/src/PrusaSlicer.cpp +++ b/src/PrusaSlicer.cpp @@ -314,10 +314,10 @@ int CLI::run(int argc, char **argv) // Loop through transform options. bool user_center_specified = false; - Points bed = get_bed_shape(m_print_config); - ArrangeParams arrange_cfg; - arrange_cfg.min_obj_distance = scaled(min_object_distance(m_print_config)); - + arr2::ArrangeBed bed = arr2::to_arrange_bed(get_bed_shape(m_print_config)); + arr2::ArrangeSettings arrange_cfg; + arrange_cfg.set_distance_from_objects(min_object_distance(m_print_config)); + for (auto const &opt_key : m_transforms) { if (opt_key == "merge") { Model m; @@ -330,7 +330,7 @@ int CLI::run(int argc, char **argv) if (this->has_print_action()) arrange_objects(m, bed, arrange_cfg); else - arrange_objects(m, InfiniteBed{}, arrange_cfg); + arrange_objects(m, arr2::InfiniteBed{}, arrange_cfg); } m_models.clear(); m_models.emplace_back(std::move(m)); @@ -576,7 +576,7 @@ int CLI::run(int argc, char **argv) if (! m_config.opt_bool("dont_arrange")) { if (user_center_specified) { Vec2d c = m_config.option("center")->value; - arrange_objects(model, InfiniteBed{scaled(c)}, arrange_cfg); + arrange_objects(model, arr2::InfiniteBed{scaled(c)}, arrange_cfg); } else arrange_objects(model, bed, arrange_cfg); } diff --git a/src/libnest2d/CMakeLists.txt b/src/libnest2d/CMakeLists.txt index 154c965e5b..ccfdfb5512 100644 --- a/src/libnest2d/CMakeLists.txt +++ b/src/libnest2d/CMakeLists.txt @@ -18,11 +18,10 @@ set(LIBNEST2D_SRCFILES include/libnest2d/optimizers/nlopt/simplex.hpp include/libnest2d/optimizers/nlopt/subplex.hpp include/libnest2d/optimizers/nlopt/genetic.hpp - src/libnest2d.cpp ) -add_library(libnest2d STATIC ${LIBNEST2D_SRCFILES}) +add_library(libnest2d INTERFACE ${LIBNEST2D_SRCFILES}) -target_include_directories(libnest2d PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include) -target_link_libraries(libnest2d PUBLIC NLopt::nlopt TBB::tbb TBB::tbbmalloc Boost::boost libslic3r) -target_compile_definitions(libnest2d PUBLIC LIBNEST2D_THREADING_tbb LIBNEST2D_STATIC LIBNEST2D_OPTIMIZER_nlopt LIBNEST2D_GEOMETRIES_libslic3r) +target_include_directories(libnest2d INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include) +target_link_libraries(libnest2d INTERFACE NLopt::nlopt TBB::tbb TBB::tbbmalloc Boost::boost libslic3r) +target_compile_definitions(libnest2d INTERFACE LIBNEST2D_THREADING_tbb LIBNEST2D_STATIC LIBNEST2D_OPTIMIZER_nlopt LIBNEST2D_GEOMETRIES_libslic3r) diff --git a/src/libnest2d/include/libnest2d/backends/libslic3r/geometries.hpp b/src/libnest2d/include/libnest2d/backends/libslic3r/geometries.hpp index 14b075b19d..48b54aa1c6 100644 --- a/src/libnest2d/include/libnest2d/backends/libslic3r/geometries.hpp +++ b/src/libnest2d/include/libnest2d/backends/libslic3r/geometries.hpp @@ -243,6 +243,12 @@ inline void translate(Slic3r::ExPolygon& sh, const Slic3r::Point& offs) sh.translate(offs); } +template<> +inline void translate(Slic3r::Polygon& sh, const Slic3r::Point& offs) +{ + sh.translate(offs); +} + #define DISABLE_BOOST_ROTATE template<> inline void rotate(Slic3r::ExPolygon& sh, const Radians& rads) @@ -250,6 +256,12 @@ inline void rotate(Slic3r::ExPolygon& sh, const Radians& rads) sh.rotate(rads); } +template<> +inline void rotate(Slic3r::Polygon& sh, const Radians& rads) +{ + sh.rotate(rads); +} + } // namespace shapelike namespace nfp { diff --git a/src/libslic3r/AnyPtr.hpp b/src/libslic3r/AnyPtr.hpp index 823fac0808..b5bbdd73db 100644 --- a/src/libslic3r/AnyPtr.hpp +++ b/src/libslic3r/AnyPtr.hpp @@ -15,12 +15,19 @@ namespace Slic3r { // The stored pointer is not checked for being null when dereferenced. // // This is a movable only object due to the fact that it can possibly hold -// a unique_ptr which a non-copy. +// a unique_ptr which can only be moved. +// +// Drawbacks: +// No custom deleters are supported when storing a unique_ptr, but overloading +// std::default_delete for a particular type could be a workaround +// +// raw array types are problematic, since std::default_delete also does not +// support them well. template class AnyPtr { - enum { RawPtr, UPtr, ShPtr, WkPtr }; + enum { RawPtr, UPtr, ShPtr }; - boost::variant, std::shared_ptr, std::weak_ptr> ptr; + boost::variant, std::shared_ptr> ptr; template static T *get_ptr(Self &&s) { @@ -28,91 +35,119 @@ class AnyPtr { case RawPtr: return boost::get(s.ptr); case UPtr: return boost::get>(s.ptr).get(); case ShPtr: return boost::get>(s.ptr).get(); - case WkPtr: { - auto shptr = boost::get>(s.ptr).lock(); - return shptr.get(); - } } return nullptr; } -public: - template>> - AnyPtr(TT *p = nullptr) : ptr{p} - {} - template>> - AnyPtr(std::unique_ptr p) : ptr{std::unique_ptr(std::move(p))} - {} - template>> - AnyPtr(std::shared_ptr p) : ptr{std::shared_ptr(std::move(p))} - {} - template>> - AnyPtr(std::weak_ptr p) : ptr{std::weak_ptr(std::move(p))} - {} + template friend class AnyPtr; - ~AnyPtr() = default; + template + using SimilarPtrOnly = std::enable_if_t>; + +public: + + AnyPtr() noexcept = default; + + AnyPtr(T *p) noexcept: ptr{p} {} + + AnyPtr(std::nullptr_t) noexcept {}; + + template> + AnyPtr(TT *p) noexcept : ptr{p} + {} + template> + AnyPtr(std::unique_ptr p) noexcept : ptr{std::unique_ptr(std::move(p))} + {} + template> + AnyPtr(std::shared_ptr p) noexcept : ptr{std::shared_ptr(std::move(p))} + {} AnyPtr(AnyPtr &&other) noexcept : ptr{std::move(other.ptr)} {} + + template> + AnyPtr(AnyPtr &&other) noexcept + { + this->operator=(std::move(other)); + } + AnyPtr(const AnyPtr &other) = delete; - AnyPtr &operator=(AnyPtr &&other) noexcept { ptr = std::move(other.ptr); return *this; } + AnyPtr &operator=(AnyPtr &&other) noexcept + { + ptr = std::move(other.ptr); + return *this; + } + AnyPtr &operator=(const AnyPtr &other) = delete; - template>> - AnyPtr &operator=(TT *p) { ptr = p; return *this; } + template> + AnyPtr& operator=(AnyPtr &&other) noexcept + { + switch (other.ptr.which()) { + case RawPtr: *this = boost::get(other.ptr); break; + case UPtr: *this = std::move(boost::get>(other.ptr)); break; + case ShPtr: *this = std::move(boost::get>(other.ptr)); break; + } - template>> - AnyPtr &operator=(std::unique_ptr p) { ptr = std::move(p); return *this; } + return *this; + } - template>> - AnyPtr &operator=(std::shared_ptr p) { ptr = p; return *this; } + template> + AnyPtr &operator=(TT *p) noexcept + { + ptr = static_cast(p); + return *this; + } - template>> - AnyPtr &operator=(std::weak_ptr p) { ptr = std::move(p); return *this; } + template> + AnyPtr &operator=(std::unique_ptr p) noexcept + { + ptr = std::unique_ptr(std::move(p)); + return *this; + } - const T &operator*() const { return *get_ptr(*this); } - T &operator*() { return *get_ptr(*this); } + template> + AnyPtr &operator=(std::shared_ptr p) noexcept + { + ptr = std::shared_ptr(std::move(p)); + return *this; + } - T *operator->() { return get_ptr(*this); } - const T *operator->() const { return get_ptr(*this); } + const T &operator*() const noexcept { return *get_ptr(*this); } + T &operator*() noexcept { return *get_ptr(*this); } - T *get() { return get_ptr(*this); } - const T *get() const { return get_ptr(*this); } + T *operator->() noexcept { return get_ptr(*this); } + const T *operator->() const noexcept { return get_ptr(*this); } - operator bool() const + T *get() noexcept { return get_ptr(*this); } + const T *get() const noexcept { return get_ptr(*this); } + + operator bool() const noexcept { switch (ptr.which()) { case RawPtr: return bool(boost::get(ptr)); case UPtr: return bool(boost::get>(ptr)); case ShPtr: return bool(boost::get>(ptr)); - case WkPtr: { - auto shptr = boost::get>(ptr).lock(); - return bool(shptr); - } } return false; } - // If the stored pointer is a shared or weak pointer, returns a reference + // If the stored pointer is a shared pointer, returns a reference // counted copy. Empty shared pointer is returned otherwise. - std::shared_ptr get_shared_cpy() const + std::shared_ptr get_shared_cpy() const noexcept { std::shared_ptr ret; - switch (ptr.which()) { - case ShPtr: ret = boost::get>(ptr); break; - case WkPtr: ret = boost::get>(ptr).lock(); break; - default: - ; - } + if (ptr.which() == ShPtr) + ret = boost::get>(ptr); return ret; } // If the underlying pointer is unique, convert to shared pointer - void convert_unique_to_shared() + void convert_unique_to_shared() noexcept { if (ptr.which() == UPtr) ptr = std::shared_ptr{std::move(boost::get>(ptr))}; @@ -125,6 +160,7 @@ public: } }; + } // namespace Slic3r #endif // ANYPTR_HPP diff --git a/src/libslic3r/Arrange.cpp b/src/libslic3r/Arrange.cpp index ea71a1a55f..db4c343275 100644 --- a/src/libslic3r/Arrange.cpp +++ b/src/libslic3r/Arrange.cpp @@ -12,6 +12,7 @@ #include #include +#include #if defined(_MSC_VER) && defined(__clang__) #define BOOST_NO_CXX17_HDR_STRING_VIEW @@ -258,7 +259,7 @@ protected: auto& index = isBig(item.area()) ? spatindex : smalls_spatindex; // Query the spatial index for the neighbors - std::vector result; + boost::container::small_vector result; result.reserve(index.size()); index.query(query, std::back_inserter(result)); diff --git a/src/libslic3r/Arrange/Arrange.hpp b/src/libslic3r/Arrange/Arrange.hpp new file mode 100644 index 0000000000..1578fed1c6 --- /dev/null +++ b/src/libslic3r/Arrange/Arrange.hpp @@ -0,0 +1,236 @@ +#ifndef ARRANGE2_HPP +#define ARRANGE2_HPP + +#include "Scene.hpp" +#include "Items/WritableItemTraits.hpp" +#include "Core/NFP/NFPArrangeItemTraits.hpp" + +#include "libslic3r/MinAreaBoundingBox.hpp" + +namespace Slic3r { namespace arr2 { + +template class Arranger +{ +public: + class Ctl : public ArrangeTaskCtl { + public: + virtual void on_packed(ArrItem &item) {}; + }; + + virtual ~Arranger() = default; + + virtual void arrange(std::vector &items, + const std::vector &fixed, + const ExtendedBed &bed, + Ctl &ctl) = 0; + + void arrange(std::vector &items, + const std::vector &fixed, + const ExtendedBed &bed, + ArrangeTaskCtl &ctl); + + void arrange(std::vector &items, + const std::vector &fixed, + const ExtendedBed &bed, + Ctl &&ctl) + { + arrange(items, fixed, bed, ctl); + } + + void arrange(std::vector &items, + const std::vector &fixed, + const ExtendedBed &bed, + ArrangeTaskCtl &&ctl) + { + arrange(items, fixed, bed, ctl); + } + + static std::unique_ptr create(const ArrangeSettingsView &settings); +}; + +template using ArrangerCtl = typename Arranger::Ctl; + +template +class DefaultArrangerCtl : public Arranger::Ctl { + ArrangeTaskCtl *taskctl = nullptr; + +public: + DefaultArrangerCtl() = default; + + explicit DefaultArrangerCtl(ArrangeTaskBase::Ctl &ctl) : taskctl{&ctl} {} + + void update_status(int st) override + { + if (taskctl) + taskctl->update_status(st); + } + + bool was_canceled() const override + { + if (taskctl) + return taskctl->was_canceled(); + + return false; + } +}; + +template +void Arranger::arrange(std::vector &items, + const std::vector &fixed, + const ExtendedBed &bed, + ArrangeTaskCtl &ctl) +{ + arrange(items, fixed, bed, DefaultArrangerCtl{ctl}); +} + +template class ArrangeableToItemConverter +{ +public: + virtual ~ArrangeableToItemConverter() = default; + + virtual ArrItem convert(const Arrangeable &arrbl, coord_t offs = 0) const = 0; + + static std::unique_ptr create( + ArrangeSettingsView::GeometryHandling geometry_handling, + coord_t safety_d); + + static std::unique_ptr create( + const Scene &sc) + { + return create(sc.settings().get_geometry_handling(), + scaled(sc.settings().get_distance_from_objects())); + } +}; + +template> +class AnyWritableDataStore: public AnyWritable +{ + DStore &dstore; + +public: + AnyWritableDataStore(DStore &store): dstore{store} {} + + void write(std::string_view key, std::any d) override + { + set_data(dstore, std::string{key}, std::move(d)); + } +}; + +template +class BasicItemConverter : public ArrangeableToItemConverter +{ + coord_t m_safety_d; + +public: + BasicItemConverter(coord_t safety_d = 0) : m_safety_d{safety_d} {} + + coord_t safety_dist() const noexcept { return m_safety_d; } +}; + +template +class ConvexItemConverter : public BasicItemConverter +{ +public: + using BasicItemConverter::BasicItemConverter; + + ArrItem convert(const Arrangeable &arrbl, coord_t offs) const override; +}; + +template +class AdvancedItemConverter : public BasicItemConverter +{ +protected: + virtual ArrItem get_arritem(const Arrangeable &arrbl, coord_t eps) const; + +public: + using BasicItemConverter::BasicItemConverter; + + ArrItem convert(const Arrangeable &arrbl, coord_t offs) const override; +}; + +template +class BalancedItemConverter : public AdvancedItemConverter +{ +protected: + ArrItem get_arritem(const Arrangeable &arrbl, coord_t offs) const override; + +public: + using AdvancedItemConverter::AdvancedItemConverter; +}; + +template struct ImbueableItemTraits_ +{ + static constexpr const char *Key = "object_id"; + + static void imbue_id(ArrItem &itm, const ObjectID &id) + { + set_arbitrary_data(itm, Key, id); + } + + static std::optional retrieve_id(const ArrItem &itm) + { + std::optional ret; + auto idptr = get_data(itm, Key); + if (idptr) + ret = *idptr; + + return ret; + } +}; + +template +using ImbueableItemTraits = ImbueableItemTraits_>; + +template +void imbue_id(ArrItem &itm, const ObjectID &id) +{ + ImbueableItemTraits::imbue_id(itm, id); +} + +template +std::optional retrieve_id(const ArrItem &itm) +{ + return ImbueableItemTraits::retrieve_id(itm); +} + +template +bool apply_arrangeitem(const ArrItem &itm, ArrangeableModel &mdl) +{ + bool ret = false; + + if (auto id = retrieve_id(itm)) { + mdl.visit_arrangeable(*id, [&itm, &ret](Arrangeable &arrbl) { + if ((ret = arrbl.assign_bed(get_bed_index(itm)))) + arrbl.transform(unscaled(get_translation(itm)), get_rotation(itm)); + }); + } + + return ret; +} + +template +double get_min_area_bounding_box_rotation(const ArrItem &itm) +{ + return MinAreaBoundigBox{envelope_convex_hull(itm), + MinAreaBoundigBox::pcConvex} + .angle_to_X(); +} + +template +double get_fit_into_bed_rotation(const ArrItem &itm, const RectangleBed &bed) +{ + double ret = 0.; + + auto bbsz = envelope_bounding_box(itm).size(); + auto binbb = bounding_box(bed); + auto binbbsz = binbb.size(); + + if (bbsz.x() >= binbbsz.x() || bbsz.y() >= binbbsz.y()) + ret = fit_into_box_rotation(envelope_convex_hull(itm), binbb); + + return ret; +} + +}} // namespace Slic3r::arr2 + +#endif // ARRANGE2_HPP diff --git a/src/libslic3r/Arrange/ArrangeImpl.hpp b/src/libslic3r/Arrange/ArrangeImpl.hpp new file mode 100644 index 0000000000..0b0949de28 --- /dev/null +++ b/src/libslic3r/Arrange/ArrangeImpl.hpp @@ -0,0 +1,450 @@ +#ifndef ARRANGEIMPL_HPP +#define ARRANGEIMPL_HPP + +#include + +#include "Arrange.hpp" + +#include "Core/ArrangeBase.hpp" +#include "Core/ArrangeFirstFit.hpp" +#include "Core/NFP/PackStrategyNFP.hpp" +#include "Core/NFP/Kernels/TMArrangeKernel.hpp" +#include "Core/NFP/Kernels/GravityKernel.hpp" +#include "Core/NFP/RectangleOverfitPackingStrategy.hpp" +#include "Core/Beds.hpp" + +#include "Items/WritableItemTraits.hpp" + +#include "SegmentedRectangleBed.hpp" + +#include "libslic3r/Execution/ExecutionTBB.hpp" +#include "libslic3r/Geometry/ConvexHull.hpp" + +#ifndef NDEBUG +#include "Core/NFP/Kernels/SVGDebugOutputKernelWrapper.hpp" +#endif + +namespace Slic3r { namespace arr2 { + +// arrange overload for SegmentedRectangleBed which is exactly what is used +// by XL printers. +template +void arrange(SelectionStrategy &&selstrategy, + PackStrategy &&packingstrategy, + const Range &items, + const Range &fixed, + const SegmentedRectangleBed &bed) +{ + // Dispatch: + arrange(std::forward(selstrategy), + std::forward(packingstrategy), items, fixed, + RectangleBed{bed.bb}, SelStrategyTag{}); + + size_t beds = get_bed_count(crange(items)); + size_t fixed_beds = std::max(beds, get_bed_count(fixed)); + std::vector fixed_is_empty(fixed_beds, true); + + std::vector pilebb(beds); + + for (auto &itm : items) { + auto bedidx = get_bed_index(itm); + if (bedidx >= 0) { + pilebb[bedidx].merge(fixed_bounding_box(itm)); + if (is_wipe_tower(itm)) + fixed_is_empty[bedidx] = false; + } + } + + for (auto &fxitm : fixed) { + auto bedidx = get_bed_index(fxitm); + if (bedidx >= 0 || is_wipe_tower(fxitm)) + fixed_is_empty[bedidx] = false; + } + + auto bedbb = bounding_box(bed); + auto piecesz = unscaled(bedbb).size(); + piecesz.x() /= bed.segments_x(); + piecesz.y() /= bed.segments_y(); + + using Pivots = RectPivots; + + Pivots pivot = bed.alignment(); + + for (size_t bedidx = 0; bedidx < beds; ++bedidx) { + if (! fixed_is_empty[bedidx]) + continue; + + BoundingBox bb; + auto pilesz = unscaled(pilebb[bedidx]).size(); + bb.max.x() = scaled(std::ceil(pilesz.x() / piecesz.x()) * piecesz.x()); + bb.max.y() = scaled(std::ceil(pilesz.y() / piecesz.y()) * piecesz.y()); + + switch (pivot) { + case Pivots::BottomLeft: + bb.translate(bedbb.min - bb.min); + break; + case Pivots::TopRight: + bb.translate(bedbb.max - bb.max); + break; + case Pivots::BottomRight: { + Point bedref{bedbb.max.x(), bedbb.min.y()}; + Point bbref {bb.max.x(), bb.min.y()}; + bb.translate(bedref - bbref); + break; + } + case Pivots::TopLeft: { + Point bedref{bedbb.min.x(), bedbb.max.y()}; + Point bbref {bb.min.x(), bb.max.y()}; + bb.translate(bedref - bbref); + break; + } + case Pivots::Center: { + bb.translate(bedbb.center() - bb.center()); + break; + } + default: + ; + } + + Vec2crd d = bb.center() - pilebb[bedidx].center(); + + auto pilebbx = pilebb[bedidx]; + pilebbx.translate(d); + + Point corr{0, 0}; + corr.x() = -std::min(0, pilebbx.min.x() - bedbb.min.x()) + -std::max(0, pilebbx.max.x() - bedbb.max.x()); + corr.y() = -std::min(0, pilebbx.min.y() - bedbb.min.y()) + -std::max(0, pilebbx.max.y() - bedbb.max.y()); + + d += corr; + + for (auto &itm : items) + if (itm.bed_idx() == static_cast(bedidx) && !is_wipe_tower(itm)) + translate(itm, d); + } +} + + +using VariantKernel = + boost::variant; + +template<> struct KernelTraits_ { + template + static double placement_fitness(const VariantKernel &kernel, + const ArrItem &itm, + const Vec2crd &transl) + { + double ret = NaNd; + boost::apply_visitor( + [&](auto &k) { ret = k.placement_fitness(itm, transl); }, kernel); + + return ret; + } + + template + static bool on_start_packing(VariantKernel &kernel, + ArrItem &itm, + const Bed &bed, + const Ctx &packing_context, + const Range &remaining_items) + { + bool ret = false; + + boost::apply_visitor([&](auto &k) { + ret = k.on_start_packing(itm, bed, packing_context, remaining_items); + }, kernel); + + return ret; + } + + template + static bool on_item_packed(VariantKernel &kernel, ArrItem &itm) + { + bool ret = false; + boost::apply_visitor([&](auto &k) { ret = k.on_item_packed(itm); }, + kernel); + + return ret; + } +}; + +template +struct firstfit::ItemArrangedVisitor> { + template + static void on_arranged(ArrItem &itm, + const Bed &bed, + const Range &packed, + const Range &remaining) + { + using OnArrangeCb = std::function &)>; + + auto cb = get_data(itm, "on_arranged"); + + if (cb) { + (*cb)(itm); + } + } +}; + +inline RectPivots xlpivots_to_rect_pivots(ArrangeSettingsView::XLPivots xlpivot) +{ + if (xlpivot == arr2::ArrangeSettingsView::xlpRandom) { + // means it should be random + std::random_device rd{}; + std::mt19937 rng(rd()); + std::uniform_int_distribution + dist(0, arr2::ArrangeSettingsView::xlpRandom - 1); + xlpivot = static_cast(dist(rng)); + } + + RectPivots rectpivot = RectPivots::Center; + + switch(xlpivot) { + case arr2::ArrangeSettingsView::xlpCenter: rectpivot = RectPivots::Center; break; + case arr2::ArrangeSettingsView::xlpFrontLeft: rectpivot = RectPivots::BottomLeft; break; + case arr2::ArrangeSettingsView::xlpFrontRight: rectpivot = RectPivots::BottomRight; break; + case arr2::ArrangeSettingsView::xlpRearLeft: rectpivot = RectPivots::TopLeft; break; + case arr2::ArrangeSettingsView::xlpRearRight: rectpivot = RectPivots::TopRight; break; + default: + ; + } + + return rectpivot; +} + +// An arranger put together to fulfill all the requirements of PrusaSlicer based +// on the supplied ArrangeSettings +template +class DefaultArranger: public Arranger { + ArrangeSettings m_settings; + + static constexpr auto Accuracy = 1.; + + template + void arrange_( + const Range &items, + const Range &fixed, + const Bed &bed, + ArrangerCtl &ctl) + { + auto cmpfn = [](const auto &itm1, const auto &itm2) { + int pa = get_priority(itm1); + int pb = get_priority(itm2); + + return pa == pb ? envelope_area(itm1) > envelope_area(itm2) : + pa > pb; + }; + + auto on_arranged = [&ctl](auto &itm, auto &bed, auto &ctx, auto &rem) { + ctl.update_status(rem.size()); + + ctl.on_packed(itm); + + firstfit::DefaultOnArrangedFn{}(itm, bed, ctx, rem); + }; + + auto stop_cond = [&ctl] { return ctl.was_canceled(); }; + + firstfit::SelectionStrategy sel{cmpfn, on_arranged, stop_cond}; + + constexpr auto ep = ex_tbb; + + VariantKernel basekernel; + switch (m_settings.get_arrange_strategy()) { + default: + [[fallthrough]]; + case ArrangeSettingsView::asAuto: + basekernel = TMArrangeKernel{items.size(), area(bed)}; + break; + case ArrangeSettingsView::asPullToCenter: + basekernel = GravityKernel{}; + break; + } + +#ifndef NDEBUG + SVGDebugOutputKernelWrapper kernel{bounding_box(bed), basekernel}; +#else + auto & kernel = basekernel; +#endif + + // Use the minimum bounding box rotation as a starting point. + if (m_settings.is_rotation_enabled()) { + for (auto &itm : items) { + double fit_bed_rot = 0.; + + if constexpr (std::is_convertible_v) + fit_bed_rot = get_fit_into_bed_rotation(itm, bed); + + auto minbbr = get_min_area_bounding_box_rotation(itm); + std::vector rotations = + {minbbr, fit_bed_rot, + minbbr + PI / 4., minbbr + PI / 2., + minbbr + PI, minbbr + 3 * PI / 4.}; + + set_allowed_rotations(itm, rotations); + } + } + + bool with_wipe_tower = std::any_of(items.begin(), items.end(), + [](auto &itm) { + return is_wipe_tower(itm); + }); + + // With rectange bed, and no fixed items, let's use an infinite bed + // with RectangleOverfitKernelWrapper. It produces better results than + // a pure RectangleBed with inner-fit polygon calculation. + if (!with_wipe_tower && + m_settings.get_arrange_strategy() == ArrangeSettingsView::asAuto && + std::is_convertible_v) { + PackStrategyNFP base_strategy{std::move(kernel), ep, Accuracy, stop_cond}; + + RectangleOverfitPackingStrategy final_strategy{std::move(base_strategy)}; + + arr2::arrange(sel, final_strategy, items, fixed, bed); + } else { + PackStrategyNFP ps{std::move(kernel), ep, Accuracy, stop_cond}; + + arr2::arrange(sel, ps, items, fixed, bed); + } + } + +public: + explicit DefaultArranger(const ArrangeSettingsView &settings) + { + m_settings.set_from(settings); + } + + void arrange( + std::vector &items, + const std::vector &fixed, + const ExtendedBed &bed, + ArrangerCtl &ctl) override + { + visit_bed([this, &items, &fixed, &ctl](auto rawbed) { + + if constexpr (IsSegmentedBed) + rawbed.pivot = xlpivots_to_rect_pivots( + m_settings.get_xl_alignment()); + + arrange_(range(items), crange(fixed), rawbed, ctl); + }, bed); + } +}; + +template +std::unique_ptr> Arranger::create( + const ArrangeSettingsView &settings) +{ + // Currently all that is needed is handled by DefaultArranger + return std::make_unique>(settings); +} + +template +ArrItem ConvexItemConverter::convert(const Arrangeable &arrbl, + coord_t offs) const +{ + auto bed_index = arrbl.get_bed_index(); + Polygon outline = arrbl.convex_outline(); + Polygon envelope = arrbl.convex_envelope(); + + coord_t infl = offs + coord_t(std::ceil(this->safety_dist() / 2.)); + + if (infl != 0) { + outline = Geometry::convex_hull(offset(outline, infl)); + if (! envelope.empty()) + envelope = Geometry::convex_hull(offset(envelope, infl)); + } + + ArrItem ret; + set_convex_shape(ret, outline); + if (! envelope.empty()) + set_convex_envelope(ret, envelope); + + set_bed_index(ret, bed_index); + set_priority(ret, arrbl.priority()); + + imbue_id(ret, arrbl.id()); + arrbl.imbue_data(AnyWritableDataStore{ret}); + + return ret; +} + +template +ArrItem AdvancedItemConverter::convert(const Arrangeable &arrbl, + coord_t offs) const +{ + auto bed_index = arrbl.get_bed_index(); + ArrItem ret = get_arritem(arrbl, offs); + + set_bed_index(ret, bed_index); + set_priority(ret, arrbl.priority()); + imbue_id(ret, arrbl.id()); + arrbl.imbue_data(AnyWritableDataStore{ret}); + + return ret; +} + +template +ArrItem AdvancedItemConverter::get_arritem(const Arrangeable &arrbl, + coord_t offs) const +{ + coord_t infl = offs + coord_t(std::ceil(this->safety_dist() / 2.)); + + auto outline = arrbl.full_outline(); + auto envelope = arrbl.full_envelope(); + if (infl != 0) { + outline = offset_ex(outline, infl); + if (! envelope.empty()) + envelope = offset_ex(outline, infl); + } + + ArrItem ret; + set_shape(ret, outline); + if (! envelope.empty()) + set_envelope(ret, envelope); + + return ret; +} + +template +ArrItem BalancedItemConverter::get_arritem(const Arrangeable &arrbl, + coord_t offs) const +{ + ArrItem ret = AdvancedItemConverter::get_arritem(arrbl, offs); + set_convex_envelope(ret, envelope_convex_hull(ret)); + + return ret; +} + +template +std::unique_ptr> +ArrangeableToItemConverter::create( + ArrangeSettingsView::GeometryHandling gh, + coord_t safety_d) +{ + std::unique_ptr> ret; + + switch(gh) { + case arr2::ArrangeSettingsView::ghConvex: + ret = std::make_unique>(safety_d); + break; + case arr2::ArrangeSettingsView::ghBalanced: + ret = std::make_unique>(safety_d); + break; + case arr2::ArrangeSettingsView::ghAdvanced: + ret = std::make_unique>(safety_d); + break; + default: + ; + } + + return ret; +} + +}} // namespace Slic3r::arr2 + +#endif // ARRANGEIMPL_HPP diff --git a/src/libslic3r/Arrange/ArrangeSettingsDb_AppCfg.cpp b/src/libslic3r/Arrange/ArrangeSettingsDb_AppCfg.cpp new file mode 100644 index 0000000000..9353d4aac2 --- /dev/null +++ b/src/libslic3r/Arrange/ArrangeSettingsDb_AppCfg.cpp @@ -0,0 +1,234 @@ +#include "ArrangeSettingsDb_AppCfg.hpp" + +namespace Slic3r { + +PrinterTechnology ArrangeSettingsDb_AppCfg::current_printer_technology() const +{ + PrinterTechnology pt = ptFFF; + + if (m_printtech_getter) + pt = m_printtech_getter(); + + return pt; +} + +const DynamicPrintConfig *ArrangeSettingsDb_AppCfg::config() const +{ + const DynamicPrintConfig *ret = nullptr; + + if (m_config_getter) + ret = m_config_getter(); + + return ret; +} + +ArrangeSettingsDb_AppCfg::ArrangeSettingsDb_AppCfg( + AppConfig *appcfg, + std::function cfgfn, + std::function printtech_fn) + : m_appcfg{appcfg}, m_config_getter{cfgfn}, m_printtech_getter{printtech_fn} +{ + m_settings_fff.postfix = "_fff"; + m_settings_fff_seq.postfix = "_fff_seq_print"; + m_settings_sla.postfix = "_sla"; + + std::string dist_fff_str = + m_appcfg->get("arrange", "min_object_distance_fff"); + + std::string dist_bed_fff_str = + m_appcfg->get("arrange", "min_bed_distance_fff"); + + std::string dist_fff_seq_print_str = + m_appcfg->get("arrange", "min_object_distance_fff_seq_print"); + + std::string dist_bed_fff_seq_print_str = + m_appcfg->get("arrange", "min_bed_distance_fff_seq_print"); + + std::string dist_sla_str = + m_appcfg->get("arrange", "min_object_distance_sla"); + + std::string dist_bed_sla_str = + m_appcfg->get("arrange", "min_bed_distance_sla"); + + std::string en_rot_fff_str = + m_appcfg->get("arrange", "enable_rotation_fff"); + + std::string en_rot_fff_seqp_str = + m_appcfg->get("arrange", "enable_rotation_fff_seq_print"); + + std::string en_rot_sla_str = + m_appcfg->get("arrange", "enable_rotation_sla"); + + // std::string alignment_fff_str = + // m_appcfg->get("arrange", "alignment_fff"); + + // std::string alignment_fff_seqp_str = + // m_appcfg->get("arrange", "alignment_fff_seq_pring"); + + // std::string alignment_sla_str = + // m_appcfg->get("arrange", "alignment_sla"); + + // Override default alignment and save save/load it to a temporary slot "alignment_xl" + std::string alignment_xl_str = + m_appcfg->get("arrange", "alignment_xl"); + + std::string geom_handling_str = + m_appcfg->get("arrange", "geometry_handling"); + + std::string strategy_str = + m_appcfg->get("arrange", "arrange_strategy"); + + if (!dist_fff_str.empty()) + m_settings_fff.vals.d_obj = string_to_float_decimal_point(dist_fff_str); + + if (!dist_bed_fff_str.empty()) + m_settings_fff.vals.d_bed = string_to_float_decimal_point(dist_bed_fff_str); + + if (!dist_fff_seq_print_str.empty()) + m_settings_fff_seq.vals.d_obj = string_to_float_decimal_point(dist_fff_seq_print_str); + + if (!dist_bed_fff_seq_print_str.empty()) + m_settings_fff_seq.vals.d_bed = string_to_float_decimal_point(dist_bed_fff_seq_print_str); + + if (!dist_sla_str.empty()) + m_settings_sla.vals.d_obj = string_to_float_decimal_point(dist_sla_str); + + if (!dist_bed_sla_str.empty()) + m_settings_sla.vals.d_bed = string_to_float_decimal_point(dist_bed_sla_str); + + if (!en_rot_fff_str.empty()) + m_settings_fff.vals.rotations = (en_rot_fff_str == "1" || en_rot_fff_str == "yes"); + + if (!en_rot_fff_seqp_str.empty()) + m_settings_fff_seq.vals.rotations = (en_rot_fff_seqp_str == "1" || en_rot_fff_seqp_str == "yes"); + + if (!en_rot_sla_str.empty()) + m_settings_sla.vals.rotations = (en_rot_sla_str == "1" || en_rot_sla_str == "yes"); + + // if (!alignment_sla_str.empty()) + // m_arrange_settings_sla.alignment = std::stoi(alignment_sla_str); + + // if (!alignment_fff_str.empty()) + // m_arrange_settings_fff.alignment = std::stoi(alignment_fff_str); + + // if (!alignment_fff_seqp_str.empty()) + // m_arrange_settings_fff_seq_print.alignment = std::stoi(alignment_fff_seqp_str); + + // Override default alignment and save save/load it to a temporary slot "alignment_xl" + ArrangeSettingsView::XLPivots arr_alignment = ArrangeSettingsView::xlpFrontLeft; + if (!alignment_xl_str.empty()) { + int align_val = std::stoi(alignment_xl_str); + + if (align_val >= 0 && align_val < ArrangeSettingsView::xlpCount) + arr_alignment = + static_cast(align_val); + } + + m_settings_sla.vals.xl_align = arr_alignment ; + m_settings_fff.vals.xl_align = arr_alignment ; + m_settings_fff_seq.vals.xl_align = arr_alignment ; + + ArrangeSettingsView::GeometryHandling geom_handl = arr2::ArrangeSettingsView::ghConvex; + if (!geom_handling_str.empty()) { + int gh = std::stoi(geom_handling_str); + if(gh >= 0 && gh < ArrangeSettingsView::GeometryHandling::ghCount) + geom_handl = static_cast(gh); + } + + m_settings_sla.vals.geom_handling = geom_handl; + m_settings_fff.vals.geom_handling = geom_handl; + m_settings_fff_seq.vals.geom_handling = geom_handl; + + ArrangeSettingsView::ArrangeStrategy arr_strategy = arr2::ArrangeSettingsView::asAuto; + if (!strategy_str.empty()) { + int strateg = std::stoi(strategy_str); + if(strateg >= 0 && strateg < ArrangeSettingsView::ArrangeStrategy::asCount) + arr_strategy = static_cast(strateg); + } + + m_settings_sla.vals.arr_strategy = arr_strategy; + m_settings_fff.vals.arr_strategy = arr_strategy; + m_settings_fff_seq.vals.arr_strategy = arr_strategy; + + if (config()) { + // Set default obj distance for fff sequential print mode + m_settings_fff_seq.defaults.d_obj = + std::max(m_settings_fff_seq.defaults.d_obj, + float(min_object_distance(*config()))); + } +} + +void ArrangeSettingsDb_AppCfg::distance_from_obj_range(float &min, + float &max) const +{ + min = 0.f; + if (config() && current_printer_technology() == ptFFF) { + auto co_opt = config()->option("complete_objects"); + if (co_opt && co_opt->value) { + min = float(min_object_distance(*config())); + } + } + max = 100.f; +} + +void ArrangeSettingsDb_AppCfg::distance_from_bed_range(float &min, + float &max) const +{ + min = 0.f; + max = 100.f; +} + +arr2::ArrangeSettingsDb& ArrangeSettingsDb_AppCfg::set_distance_from_objects(float v) +{ + Slot &slot = get_slot(this); + slot.vals.d_obj = v; + m_appcfg->set("arrange", "min_object_distance" + slot.postfix, + float_to_string_decimal_point(v)); + + return *this; +} + +arr2::ArrangeSettingsDb& ArrangeSettingsDb_AppCfg::set_distance_from_bed(float v) +{ + Slot &slot = get_slot(this); + slot.vals.d_bed = v; + m_appcfg->set("arrange", "min_bed_distance" + slot.postfix, + float_to_string_decimal_point(v)); + + return *this; +} + +arr2::ArrangeSettingsDb& ArrangeSettingsDb_AppCfg::set_rotation_enabled(bool v) +{ + Slot &slot = get_slot(this); + slot.vals.rotations = v; + m_appcfg->set("arrange", "enable_rotation" + slot.postfix, v ? "1" : "0"); + + return *this; +} + +arr2::ArrangeSettingsDb& ArrangeSettingsDb_AppCfg::set_xl_alignment(XLPivots v) +{ + m_settings_fff.vals.xl_align = v; + m_appcfg->set("arrange", "alignment_xl", std::to_string(v)); + + return *this; +} + +arr2::ArrangeSettingsDb& ArrangeSettingsDb_AppCfg::set_geometry_handling(GeometryHandling v) +{ + m_settings_fff.vals.geom_handling = v; + m_appcfg->set("arrange", "geometry_handling", std::to_string(v)); + + return *this; +} + +arr2::ArrangeSettingsDb& ArrangeSettingsDb_AppCfg::set_arrange_strategy(ArrangeStrategy v) +{ + m_settings_fff.vals.arr_strategy = v; + m_appcfg->set("arrange", "arrange_strategy", std::to_string(v)); + + return *this; +} + +} // namespace Slic3r diff --git a/src/libslic3r/Arrange/ArrangeSettingsDb_AppCfg.hpp b/src/libslic3r/Arrange/ArrangeSettingsDb_AppCfg.hpp new file mode 100644 index 0000000000..908deb73bc --- /dev/null +++ b/src/libslic3r/Arrange/ArrangeSettingsDb_AppCfg.hpp @@ -0,0 +1,78 @@ +#ifndef ARRANGESETTINGSDB_APPCFG_HPP +#define ARRANGESETTINGSDB_APPCFG_HPP + +#include "ArrangeSettingsView.hpp" +#include "libslic3r/AppConfig.hpp" +#include "libslic3r/PrintConfig.hpp" + +namespace Slic3r { + +class ArrangeSettingsDb_AppCfg: public arr2::ArrangeSettingsDb +{ + AppConfig *m_appcfg; + std::function m_config_getter; + std::function m_printtech_getter; + + struct Slot { Values vals; Values defaults; std::string postfix; }; + + // Settings and their defaults are stored separately for fff, + // sla and fff sequential mode + Slot m_settings_fff, m_settings_fff_seq, m_settings_sla; + + PrinterTechnology current_printer_technology() const; + const DynamicPrintConfig *config() const; + + template + static auto & get_slot(Self *self) { + PrinterTechnology ptech = self->current_printer_technology(); + + auto *ptr = &self->m_settings_fff; + + if (ptech == ptSLA) { + ptr = &self->m_settings_sla; + } else if (ptech == ptFFF && self->config()) { + auto co_opt = self->config()->template option( + "complete_objects"); + if (co_opt && co_opt->value) + ptr = &self->m_settings_fff_seq; + else + ptr = &self->m_settings_fff; + } + + return *ptr; + } + + template + static auto& get_ref(Self *self) { return get_slot(self).vals; } + +public: + explicit ArrangeSettingsDb_AppCfg( + AppConfig *appcfg, + std::function cfgfn, + std::function printtech_getter); + + float get_distance_from_objects() const override { return get_ref(this).d_obj; } + float get_distance_from_bed() const override { return get_ref(this).d_bed; } + bool is_rotation_enabled() const override { return get_ref(this).rotations; } + + XLPivots get_xl_alignment() const override { return m_settings_fff.vals.xl_align; } + GeometryHandling get_geometry_handling() const override { return m_settings_fff.vals.geom_handling; } + ArrangeStrategy get_arrange_strategy() const override { return m_settings_fff.vals.arr_strategy; } + + void distance_from_obj_range(float &min, float &max) const override; + void distance_from_bed_range(float &min, float &max) const override; + + ArrangeSettingsDb& set_distance_from_objects(float v) override; + ArrangeSettingsDb& set_distance_from_bed(float v) override; + ArrangeSettingsDb& set_rotation_enabled(bool v) override; + + ArrangeSettingsDb& set_xl_alignment(XLPivots v) override; + ArrangeSettingsDb& set_geometry_handling(GeometryHandling v) override; + ArrangeSettingsDb& set_arrange_strategy(ArrangeStrategy v) override; + + Values get_defaults() const override { return get_slot(this).defaults; } +}; + +} // namespace Slic3r + +#endif // ARRANGESETTINGSDB_APPCFG_HPP diff --git a/src/libslic3r/Arrange/ArrangeSettingsView.hpp b/src/libslic3r/Arrange/ArrangeSettingsView.hpp new file mode 100644 index 0000000000..5bd63aeea0 --- /dev/null +++ b/src/libslic3r/Arrange/ArrangeSettingsView.hpp @@ -0,0 +1,118 @@ +#ifndef ARRANGESETTINGSVIEW_HPP +#define ARRANGESETTINGSVIEW_HPP + +namespace Slic3r { namespace arr2 { + +class ArrangeSettingsView +{ +public: + enum GeometryHandling { ghConvex, ghBalanced, ghAdvanced, ghCount }; + enum ArrangeStrategy { asAuto, asPullToCenter, asCount }; + enum XLPivots { + xlpCenter, + xlpRearLeft, + xlpFrontLeft, + xlpFrontRight, + xlpRearRight, + xlpRandom, + xlpCount + }; + + virtual ~ArrangeSettingsView() = default; + + virtual float get_distance_from_objects() const = 0; + virtual float get_distance_from_bed() const = 0; + virtual bool is_rotation_enabled() const = 0; + + virtual XLPivots get_xl_alignment() const = 0; + virtual GeometryHandling get_geometry_handling() const = 0; + virtual ArrangeStrategy get_arrange_strategy() const = 0; +}; + +class ArrangeSettingsDb: public ArrangeSettingsView +{ +public: + + virtual void distance_from_obj_range(float &min, float &max) const = 0; + virtual void distance_from_bed_range(float &min, float &max) const = 0; + + virtual ArrangeSettingsDb& set_distance_from_objects(float v) = 0; + virtual ArrangeSettingsDb& set_distance_from_bed(float v) = 0; + virtual ArrangeSettingsDb& set_rotation_enabled(bool v) = 0; + + virtual ArrangeSettingsDb& set_xl_alignment(XLPivots v) = 0; + virtual ArrangeSettingsDb& set_geometry_handling(GeometryHandling v) = 0; + virtual ArrangeSettingsDb& set_arrange_strategy(ArrangeStrategy v) = 0; + + struct Values { + float d_obj = 6.f, d_bed = 0.f; + bool rotations = false; + XLPivots xl_align = XLPivots::xlpFrontLeft; + GeometryHandling geom_handling = GeometryHandling::ghConvex; + ArrangeStrategy arr_strategy = ArrangeStrategy::asAuto; + + Values() = default; + Values(const ArrangeSettingsView &sv) + { + d_bed = sv.get_distance_from_bed(); + d_obj = sv.get_distance_from_objects(); + arr_strategy = sv.get_arrange_strategy(); + geom_handling = sv.get_geometry_handling(); + rotations = sv.is_rotation_enabled(); + xl_align = sv.get_xl_alignment(); + } + }; + + virtual Values get_defaults() const { return {}; } + + ArrangeSettingsDb& set_from(const ArrangeSettingsView &sv) + { + set_distance_from_bed(sv.get_distance_from_bed()); + set_distance_from_objects(sv.get_distance_from_objects()); + set_arrange_strategy(sv.get_arrange_strategy()); + set_geometry_handling(sv.get_geometry_handling()); + set_rotation_enabled(sv.is_rotation_enabled()); + set_xl_alignment(sv.get_xl_alignment()); + + return *this; + } +}; + +class ArrangeSettings: public Slic3r::arr2::ArrangeSettingsDb +{ + ArrangeSettingsDb::Values m_v = {}; + +public: + explicit ArrangeSettings( + const ArrangeSettingsDb::Values &v = {}) + : m_v{v} + {} + + explicit ArrangeSettings(const ArrangeSettingsView &v) + : m_v{v} + {} + + float get_distance_from_objects() const override { return m_v.d_obj; } + float get_distance_from_bed() const override { return m_v.d_bed; } + bool is_rotation_enabled() const override { return m_v.rotations; } + XLPivots get_xl_alignment() const override { return m_v.xl_align; } + GeometryHandling get_geometry_handling() const override { return m_v.geom_handling; } + ArrangeStrategy get_arrange_strategy() const override { return m_v.arr_strategy; } + + void distance_from_obj_range(float &min, float &max) const override { min = 0.f; max = 100.f; } + void distance_from_bed_range(float &min, float &max) const override { min = 0.f; max = 100.f; } + + ArrangeSettingsDb& set_distance_from_objects(float v) override { m_v.d_obj = v; return *this; } + ArrangeSettingsDb& set_distance_from_bed(float v) override { m_v.d_bed = v; return *this; } + ArrangeSettingsDb& set_rotation_enabled(bool v) override { m_v.rotations = v; return *this; } + ArrangeSettingsDb& set_xl_alignment(XLPivots v) override { m_v.xl_align = v; return *this; } + ArrangeSettingsDb& set_geometry_handling(GeometryHandling v) override { m_v.geom_handling = v; return *this; } + ArrangeSettingsDb& set_arrange_strategy(ArrangeStrategy v) override { m_v.arr_strategy = v; return *this; } + + auto & values() const { return m_v; } + auto & values() { return m_v; } +}; + +}} // namespace Slic3r::arr2 + +#endif // ARRANGESETTINGSVIEW_HPP diff --git a/src/libslic3r/Arrange/Core/ArrangeBase.hpp b/src/libslic3r/Arrange/Core/ArrangeBase.hpp new file mode 100644 index 0000000000..97177d4b3c --- /dev/null +++ b/src/libslic3r/Arrange/Core/ArrangeBase.hpp @@ -0,0 +1,258 @@ +#ifndef ARRANGEBASE_HPP +#define ARRANGEBASE_HPP + +#include +#include + +#include "ArrangeItemTraits.hpp" +#include "PackingContext.hpp" + +#include "libslic3r/Point.hpp" + +namespace Slic3r { namespace arr2 { + +namespace detail_is_const_it { + +template +struct IsConstIt_ { static constexpr bool value = false; }; + +template +using iterator_category_t = typename std::iterator_traits::iterator_category; + +template +using iterator_reference_t = typename std::iterator_traits::reference; + +template +struct IsConstIt_ >> > +{ + static constexpr bool value = + std::is_const_v>>; +}; + +} // namespace detail_is_const_it + +template +static constexpr bool IsConstIterator = detail_is_const_it::IsConstIt_::value; + +template +constexpr bool is_const_iterator(const It &it) noexcept { return IsConstIterator; } + +// The pack() function will use tag dispatching, based on the given strategy +// object that is used as its first argument. + +// This tag is derived for a packing strategy as default, and will be used +// to cast a compile error. +struct UnimplementedPacking {}; + +// PackStrategyTag_ needs to be specialized for any valid packing strategy class +template struct PackStrategyTag_ { + using Tag = UnimplementedPacking; +}; + +// Helper metafunc to derive packing strategy tag from a strategy object. +template +using PackStrategyTag = + typename PackStrategyTag_>::Tag; + + +template struct PackStrategyTraits_ { + template using Context = DefaultPackingContext; + + template + static Context create_context(PackStrategy &ps, + const Bed &bed, + int bed_index) + { + return {}; + } +}; + +template using PackStrategyTraits = PackStrategyTraits_>; + +template +using PackStrategyContext = + typename PackStrategyTraits::template Context>; + +template +PackStrategyContext create_context(PackStrategy &&ps, + const Bed &bed, + int bed_index) +{ + return PackStrategyTraits::template create_context< + StripCVRef>(ps, bed, bed_index); +} + +// Function to pack one item into a bed. +// strategy parameter holds clue to what packing strategy to use. This function +// needs to be overloaded for the strategy tag belonging to the given +// strategy. +// 'bed' parameter is the type of bed into which the new item should be packed. +// See beds.hpp for valid bed classes. +// 'item' parameter is the item to be packed. After succesful arrangement +// (see return value) the item will have it's translation and rotation +// set correctly. If the function returns false, the translation and +// rotation of the input item might be changed to arbitrary values. +// 'fixed_items' paramter holds a range of ArrItem type objects that are already +// on the bed and need to be avoided by the newly packed item. +// 'remaining_items' is a range of ArrItem type objects that are intended to be +// packed in the future. This information can be leveradged by +// the packing strategy to make more intelligent placement +// decisions for the input item. +template +bool pack(Strategy &&strategy, + const Bed &bed, + ArrItem &item, + const PackStrategyContext &context, + const Range &remaining_items) +{ + static_assert(IsConstIterator, "Remaining item iterator is not const!"); + + // Dispatch: + return pack(std::forward(strategy), bed, item, context, + remaining_items, PackStrategyTag{}); +} + +// Overload without fixed items: +template +bool pack(Strategy &&strategy, const Bed &bed, ArrItem &item) +{ + std::vector dummy; + auto context = create_context(strategy, bed, PhysicalBedId); + return pack(std::forward(strategy), bed, item, context, + crange(dummy)); +} + +// Overload when strategy is unkown, yields compile error: +template +bool pack(Strategy &&strategy, + const Bed &bed, + ArrItem &item, + const PackStrategyContext &context, + const Range &remaining_items, + const UnimplementedPacking &) +{ + static_assert(always_false::value, + "Packing unimplemented for this placement strategy"); + + return false; +} + +// Helper function to remove unpackable items from the input container. +template +void remove_unpackable_items(PackStrategy &&ps, + Container &c, + const Bed &bed, + const StopCond &stopcond) +{ + // Safety test: try to pack each item into an empty bed. If it fails + // then it should be removed from the list + auto it = c.begin(); + while (it != c.end() && !stopcond()) { + StripCVRef &itm = *it; + auto cpy{itm}; + + if (!pack(ps, bed, cpy)) { + set_bed_index(itm, Unarranged); + it = c.erase(it); + } else + it++; + } +} + +// arrange() function will use tag dispatching based on the selection strategy +// given as its first argument. + +// This tag is derived for a selection strategy as default, and will be used +// to cast a compile error. +struct UnimplementedSelection {}; + +// SelStrategyTag_ needs to be specialized for any valid selection strategy class +template struct SelStrategyTag_ { + using Tag = UnimplementedSelection; +}; + +// Helper metafunc to derive the selection strategy tag from a strategy object. +template +using SelStrategyTag = typename SelStrategyTag_>::Tag; + +// Main function to start the arrangement. Takes a selection and a packing +// strategy object as the first two parameters. An implementation +// (function overload) must exist for this function that takes the coresponding +// selection strategy tag belonging to the given selstrategy argument. +// +// items parameter is a range of arrange items to arrange. +// fixed parameter is a range of arrange items that have fixed position and will +// not move during the arrangement but need to be avoided by the +// moving items. +// bed parameter is the type of bed into which the items need to fit. +template +void arrange(SelectionStrategy &&selstrategy, + PackStrategy &&packingstrategy, + const Range &items, + const Range &fixed, + const TBed &bed) +{ + static_assert(IsConstIterator, "Fixed item iterator is not const!"); + + // Dispatch: + arrange(std::forward(selstrategy), + std::forward(packingstrategy), items, fixed, bed, + SelStrategyTag{}); +} + +template +void arrange(SelectionStrategy &&selstrategy, + PackStrategy &&packingstrategy, + const Range &items, + const TBed &bed) +{ + std::vector::value_type> dummy; + arrange(std::forward(selstrategy), + std::forward(packingstrategy), items, crange(dummy), + bed); +} + +// Overload for unimplemented selection strategy, yields compile error: +template +void arrange(SelectionStrategy &&selstrategy, + PackStrategy &&packingstrategy, + const Range &items, + const Range &fixed, + const TBed &bed, + const UnimplementedSelection &) +{ + static_assert(always_false::value, + "Arrange unimplemented for this selection strategy"); +} + +template +size_t get_bed_count(const Range &items) +{ + auto it = std::max_element(items.begin(), + items.end(), + [](auto &i1, auto &i2) { + return get_bed_index(i1) < get_bed_index(i2); + }); + + size_t beds = 0; + if (it != items.end()) + beds = get_bed_index(*it) + 1; + + return beds; +} + +struct DefaultStopCondition { + constexpr bool operator()() const noexcept { return false; } +}; + +}} // namespace Slic3r::arr2 + +#endif // ARRANGEBASE_HPP diff --git a/src/libslic3r/Arrange/Core/ArrangeFirstFit.hpp b/src/libslic3r/Arrange/Core/ArrangeFirstFit.hpp new file mode 100644 index 0000000000..ee6940b770 --- /dev/null +++ b/src/libslic3r/Arrange/Core/ArrangeFirstFit.hpp @@ -0,0 +1,161 @@ +#ifndef ARRANGEFIRSTFIT_HPP +#define ARRANGEFIRSTFIT_HPP + +#include + +#include + +namespace Slic3r { namespace arr2 { namespace firstfit { + +struct SelectionTag {}; + +// Can be specialized by Items +template +struct ItemArrangedVisitor { + template + static void on_arranged(ArrItem &itm, + const Bed &bed, + const Range &packed_items, + const Range &remaining_items) + {} +}; + +// Use the the visitor baked into the ArrItem type by default +struct DefaultOnArrangedFn { + template + void operator()(ArrItem &itm, + const Bed &bed, + const Range &packed, + const Range &remaining) + { + ItemArrangedVisitor>::on_arranged(itm, bed, packed, + remaining); + } +}; + +struct DefaultItemCompareFn { + template + bool operator() (const ArrItem &ia, const ArrItem &ib) + { + return get_priority(ia) > get_priority(ib); + } +}; + +template +struct SelectionStrategy +{ + CompareFn cmpfn; + OnArrangedFn on_arranged_fn; + StopCondition cancel_fn; + + SelectionStrategy(CompareFn cmp = {}, + OnArrangedFn on_arranged = {}, + StopCondition stopcond = {}) + : cmpfn{cmp}, + on_arranged_fn{std::move(on_arranged)}, + cancel_fn{std::move(stopcond)} + {} +}; + +} // namespace firstfit + +template struct SelStrategyTag_> { + using Tag = firstfit::SelectionTag; +}; + +template +void arrange( + SelStrategy &&sel, + PackStrategy &&ps, + const Range &items, + const Range &fixed, + const TBed &bed, + const firstfit::SelectionTag &) +{ + using ArrItem = typename std::iterator_traits::value_type; + using ArrItemRef = std::reference_wrapper; + + auto sorted_items = reserve_vector(items.size()); + + for (auto &itm : items) { + set_bed_index(itm, Unarranged); + sorted_items.emplace_back(itm); + } + + int max_bed_idx = get_bed_count(fixed); + + using Context = PackStrategyContext; + + auto bed_contexts = reserve_vector(max_bed_idx + 1); + + for (auto &itm : fixed) { + if (get_bed_index(itm) >= 0) { + auto bedidx = static_cast(get_bed_index(itm)); + + while (bed_contexts.size() <= bedidx) + bed_contexts.emplace_back( + create_context(ps, bed, bedidx)); + + add_fixed_item(bed_contexts[bedidx], itm); + } + } + + if constexpr (!std::is_null_pointer_v) { + std::stable_sort(sorted_items.begin(), sorted_items.end(), sel.cmpfn); + } + + auto is_cancelled = [&sel]() { + return sel.cancel_fn(); + }; + + remove_unpackable_items(ps, sorted_items, bed, [&is_cancelled]() { + return is_cancelled(); + }); + + auto it = sorted_items.begin(); + + using SConstIt = typename std::vector::const_iterator; + + while (it != sorted_items.end() && !is_cancelled()) { + bool was_packed = false; + size_t j = 0; + while (!was_packed && !is_cancelled()) { + for (; j < bed_contexts.size() && !was_packed && !is_cancelled(); j++) { + set_bed_index(*it, int(j)); + + auto remaining = Range{std::next(static_cast(it)), + sorted_items.cend()}; + + was_packed = pack(ps, bed, *it, bed_contexts[j], remaining); + + if(was_packed) { + add_packed_item(bed_contexts[j], *it); + + auto packed_range = Range{sorted_items.cbegin(), + static_cast(it)}; + + sel.on_arranged_fn(*it, bed, packed_range, remaining); + } else { + set_bed_index(*it, Unarranged); + } + } + + if (!was_packed) { + bed_contexts.emplace_back( + create_context(ps, bed, bed_contexts.size())); + j = bed_contexts.size() - 1; + } + } + ++it; + } +} + +}} // namespace Slic3r::arr2 + +#endif // ARRANGEFIRSTFIT_HPP diff --git a/src/libslic3r/Arrange/Core/ArrangeItemTraits.hpp b/src/libslic3r/Arrange/Core/ArrangeItemTraits.hpp new file mode 100644 index 0000000000..5806627eb1 --- /dev/null +++ b/src/libslic3r/Arrange/Core/ArrangeItemTraits.hpp @@ -0,0 +1,108 @@ +#ifndef ARRANGE_ITEM_TRAITS_HPP +#define ARRANGE_ITEM_TRAITS_HPP + +#include + +namespace Slic3r { namespace arr2 { + +// A logical bed representing an object not being arranged. Either the arrange +// has not yet successfully run on this ArrangePolygon or it could not fit the +// object due to overly large size or invalid geometry. +const constexpr int Unarranged = -1; + +const constexpr int PhysicalBedId = 0; + +// Basic interface of an arrange item. This struct can be specialized for any +// type that is arrangeable. +template struct ArrangeItemTraits_ { + static Vec2crd get_translation(const ArrItem &ap) + { + return ap.get_translation(); + } + + static double get_rotation(const ArrItem &ap) + { + return ap.get_rotation(); + } + + static int get_bed_index(const ArrItem &ap) { return ap.get_bed_index(); } + + static int get_priority(const ArrItem &ap) { return ap.get_priority(); } + + // Setters: + + static void set_translation(ArrItem &ap, const Vec2crd &v) + { + ap.set_translation(v); + } + + static void set_rotation(ArrItem &ap, double v) { ap.set_rotation(v); } + + static void set_bed_index(ArrItem &ap, int v) { ap.set_bed_index(v); } +}; + +template using ArrangeItemTraits = ArrangeItemTraits_>; + +// Getters: + +template Vec2crd get_translation(const T &itm) +{ + return ArrangeItemTraits::get_translation(itm); +} + +template double get_rotation(const T &itm) +{ + return ArrangeItemTraits::get_rotation(itm); +} + +template int get_bed_index(const T &itm) +{ + return ArrangeItemTraits::get_bed_index(itm); +} + +template int get_priority(const T &itm) +{ + return ArrangeItemTraits::get_priority(itm); +} + +// Setters: + +template void set_translation(T &itm, const Vec2crd &v) +{ + ArrangeItemTraits::set_translation(itm, v); +} + +template void set_rotation(T &itm, double v) +{ + ArrangeItemTraits::set_rotation(itm, v); +} + +template void set_bed_index(T &itm, int v) +{ + ArrangeItemTraits::set_bed_index(itm, v); +} + +// Helper functions for arrange items +template bool is_arranged(const ArrItem &ap) +{ + return get_bed_index(ap) > Unarranged; +} + +template bool is_fixed(const ArrItem &ap) +{ + return get_bed_index(ap) >= PhysicalBedId; +} + +template void translate(ArrItem &ap, const Vec2crd &t) +{ + set_translation(ap, get_translation(ap) + t); +} + +template void rotate(ArrItem &ap, double rads) +{ + set_rotation(ap, get_rotation(ap) + rads); +} + +}} // namespace Slic3r::arr2 + +#endif // ARRANGE_ITEM_HPP diff --git a/src/libslic3r/Arrange/Core/Beds.cpp b/src/libslic3r/Arrange/Core/Beds.cpp new file mode 100644 index 0000000000..90d5f4f77d --- /dev/null +++ b/src/libslic3r/Arrange/Core/Beds.cpp @@ -0,0 +1,129 @@ +#include "Beds.hpp" + +namespace Slic3r { namespace arr2 { + +BoundingBox bounding_box(const InfiniteBed &bed) +{ + BoundingBox ret; + using C = coord_t; + + // It is important for Mx and My to be strictly less than half of the + // range of type C. width(), height() and area() will not overflow this way. + C Mx = C((std::numeric_limits::lowest() + 2 * bed.center.x()) / 4.01); + C My = C((std::numeric_limits::lowest() + 2 * bed.center.y()) / 4.01); + + ret.max = bed.center - Point{Mx, My}; + ret.min = bed.center + Point{Mx, My}; + + return ret; +} + +Polygon to_rectangle(const BoundingBox &bb) +{ + Polygon ret; + ret.points = { + bb.min, + Point{bb.max.x(), bb.min.y()}, + bb.max, + Point{bb.min.x(), bb.max.y()} + }; + + return ret; +} + +Polygon approximate_circle_with_polygon(const arr2::CircleBed &bed, int nedges) +{ + Polygon ret; + + double angle_incr = (2 * M_PI) / nedges; // Angle increment for each edge + double angle = 0; // Starting angle + + // Loop to generate vertices for each edge + for (int i = 0; i < nedges; i++) { + // Calculate coordinates of the vertices using trigonometry + auto x = bed.center().x() + static_cast(bed.radius() * std::cos(angle)); + auto y = bed.center().y() + static_cast(bed.radius() * std::sin(angle)); + + // Add vertex to the vector + ret.points.emplace_back(x, y); + + // Update the angle for the next iteration + angle += angle_incr; + } + + return ret; +} + +inline coord_t width(const BoundingBox &box) +{ + return box.max.x() - box.min.x(); +} +inline coord_t height(const BoundingBox &box) +{ + return box.max.y() - box.min.y(); +} +inline double poly_area(const Points &pts) +{ + return std::abs(Polygon::area(pts)); +} +inline double distance_to(const Point &p1, const Point &p2) +{ + double dx = p2.x() - p1.x(); + double dy = p2.y() - p1.y(); + return std::sqrt(dx * dx + dy * dy); +} + +static CircleBed to_circle(const Point ¢er, const Points &points) +{ + std::vector vertex_distances; + double avg_dist = 0; + + for (const Point &pt : points) { + double distance = distance_to(center, pt); + vertex_distances.push_back(distance); + avg_dist += distance; + } + + avg_dist /= vertex_distances.size(); + + CircleBed ret(center, avg_dist); + for (auto el : vertex_distances) { + if (std::abs(el - avg_dist) > 10 * SCALED_EPSILON) { + ret = {}; + break; + } + } + + return ret; +} + +template auto call_with_bed(const Points &bed, Fn &&fn) +{ + if (bed.empty()) + return fn(InfiniteBed{}); + else if (bed.size() == 1) + return fn(InfiniteBed{bed.front()}); + else { + auto bb = BoundingBox(bed); + CircleBed circ = to_circle(bb.center(), bed); + auto parea = poly_area(bed); + + if ((1.0 - parea / area(bb)) < 1e-3) { + return fn(RectangleBed{bb}); + } else if (!std::isnan(circ.radius())) + return fn(circ); + else + return fn(IrregularBed{{ExPolygon(bed)}}); + } +} + +ArrangeBed to_arrange_bed(const Points &bedpts) +{ + ArrangeBed ret; + + call_with_bed(bedpts, [&](const auto &bed) { ret = bed; }); + + return ret; +} + +}} // namespace Slic3r::arr2 diff --git a/src/libslic3r/Arrange/Core/Beds.hpp b/src/libslic3r/Arrange/Core/Beds.hpp new file mode 100644 index 0000000000..e49cb9c7ca --- /dev/null +++ b/src/libslic3r/Arrange/Core/Beds.hpp @@ -0,0 +1,191 @@ +#ifndef BEDS_HPP +#define BEDS_HPP + +#include + +#include +#include +#include +#include + +#include + +namespace Slic3r { namespace arr2 { + +// Bed types to be used with arrangement. Most generic bed is a simple polygon +// with holes, but other special bed types are also valid, like a bed without +// boundaries, or a special case of a rectangular or circular bed which leaves +// a lot of room for optimizations. + +// Representing an unbounded bed. +struct InfiniteBed { + Point center; + explicit InfiniteBed(const Point &p = {0, 0}): center{p} {} +}; + +BoundingBox bounding_box(const InfiniteBed &bed); + +inline InfiniteBed offset(const InfiniteBed &bed, coord_t) { return bed; } + +struct RectangleBed { + BoundingBox bb; + + explicit RectangleBed(const BoundingBox &bedbb) : bb{bedbb} {} + explicit RectangleBed(coord_t w, coord_t h, Point c = {0, 0}): + bb{{c.x() - w / 2, c.y() - h / 2}, {c.x() + w / 2, c.y() + h / 2}} + {} + + coord_t width() const { return bb.size().x(); } + coord_t height() const { return bb.size().y(); } +}; + +inline BoundingBox bounding_box(const RectangleBed &bed) { return bed.bb; } +inline RectangleBed offset(RectangleBed bed, coord_t v) +{ + bed.bb.offset(v); + return bed; +} + +Polygon to_rectangle(const BoundingBox &bb); + +inline Polygon to_rectangle(const RectangleBed &bed) +{ + return to_rectangle(bed.bb); +} + +class CircleBed { + Point m_center; + double m_radius; + +public: + CircleBed(): m_center(0, 0), m_radius(NaNd) {} + explicit CircleBed(const Point& c, double r) + : m_center(c) + , m_radius(r) + {} + + double radius() const { return m_radius; } + const Point& center() const { return m_center; } +}; + +// Function to approximate a circle with a convex polygon +Polygon approximate_circle_with_polygon(const CircleBed &bed, int nedges = 24); + +inline BoundingBox bounding_box(const CircleBed &bed) +{ + auto r = static_cast(std::round(bed.radius())); + Point R{r, r}; + + return {bed.center() - R, bed.center() + R}; +} +inline CircleBed offset(const CircleBed &bed, coord_t v) +{ + return CircleBed{bed.center(), bed.radius() + v}; +} + +struct IrregularBed { ExPolygons poly; }; +inline BoundingBox bounding_box(const IrregularBed &bed) +{ + return get_extents(bed.poly); +} + +inline IrregularBed offset(IrregularBed bed, coord_t v) +{ + bed.poly = offset_ex(bed.poly, v); + return bed; +} + +using ArrangeBed = + boost::variant; + +inline BoundingBox bounding_box(const ArrangeBed &bed) +{ + BoundingBox ret; + auto visitor = [&ret](const auto &b) { ret = bounding_box(b); }; + boost::apply_visitor(visitor, bed); + + return ret; +} + +inline ArrangeBed offset(ArrangeBed bed, coord_t v) +{ + auto visitor = [v](auto &b) { b = offset(b, v); }; + boost::apply_visitor(visitor, bed); + + return bed; +} + +inline double area(const BoundingBox &bb) +{ + auto bbsz = bb.size(); + return double(bbsz.x()) * bbsz.y(); +} + +inline double area(const RectangleBed &bed) +{ + auto bbsz = bed.bb.size(); + return double(bbsz.x()) * bbsz.y(); +} + +inline double area(const InfiniteBed &bed) +{ + return std::numeric_limits::infinity(); +} + +inline double area(const IrregularBed &bed) +{ + return std::accumulate(bed.poly.begin(), bed.poly.end(), 0., + [](double s, auto &p) { return s + p.area(); }); +} + +inline double area(const CircleBed &bed) +{ + return bed.radius() * bed.radius() * PI; +} + +inline double area(const ArrangeBed &bed) +{ + double ret = 0.; + auto visitor = [&ret](auto &b) { ret = area(b); }; + boost::apply_visitor(visitor, bed); + + return ret; +} + +inline ExPolygons to_expolygons(const InfiniteBed &bed) +{ + return {ExPolygon{to_rectangle(RectangleBed{scaled(1000.), scaled(1000.)})}}; +} + +inline ExPolygons to_expolygons(const RectangleBed &bed) +{ + return {ExPolygon{to_rectangle(bed)}}; +} + +inline ExPolygons to_expolygons(const CircleBed &bed) +{ + return {ExPolygon{approximate_circle_with_polygon(bed)}}; +} + +inline ExPolygons to_expolygons(const IrregularBed &bed) { return bed.poly; } + +inline ExPolygons to_expolygons(const ArrangeBed &bed) +{ + ExPolygons ret; + auto visitor = [&ret](const auto &b) { ret = to_expolygons(b); }; + boost::apply_visitor(visitor, bed); + + return ret; +} + +ArrangeBed to_arrange_bed(const Points &bedpts); + +} // namespace arr2 + +inline BoundingBox &bounding_box(BoundingBox &bb) { return bb; } +inline const BoundingBox &bounding_box(const BoundingBox &bb) { return bb; } +inline BoundingBox bounding_box(const Polygon &p) { return get_extents(p); } + +} // namespace Slic3r + +#endif // BEDS_HPP diff --git a/src/libslic3r/Arrange/Core/DataStoreTraits.hpp b/src/libslic3r/Arrange/Core/DataStoreTraits.hpp new file mode 100644 index 0000000000..4aca48639d --- /dev/null +++ b/src/libslic3r/Arrange/Core/DataStoreTraits.hpp @@ -0,0 +1,78 @@ +#ifndef DATASTORETRAITS_HPP +#define DATASTORETRAITS_HPP + +#include + +#include "libslic3r/libslic3r.h" + +namespace Slic3r { namespace arr2 { + +// Some items can be containers of arbitrary data stored under string keys. +template struct DataStoreTraits_ +{ + static constexpr bool Implemented = false; + + template static const T *get(const ArrItem &, const std::string &key) + { + return nullptr; + } + + // Same as above just not const. + template static T *get(ArrItem &, const std::string &key) + { + return nullptr; + } + + static bool has_key(const ArrItem &itm, const std::string &key) + { + return false; + } +}; + +template struct WritableDataStoreTraits_ +{ + static constexpr bool Implemented = false; + + template static void set(ArrItem &, const std::string &key, T &&data) + { + } +}; + +template using DataStoreTraits = DataStoreTraits_>; +template constexpr bool IsDataStore = DataStoreTraits>::Implemented; +template using DataStoreOnly = std::enable_if_t, TT>; + +template +const T *get_data(const ArrItem &itm, const std::string &key) +{ + return DataStoreTraits::template get(itm, key); +} + +template +bool has_key(const ArrItem &itm, const std::string &key) +{ + return DataStoreTraits::has_key(itm, key); +} + +template +T *get_data(ArrItem &itm, const std::string &key) +{ + return DataStoreTraits::template get(itm, key); +} + +template using WritableDataStoreTraits = WritableDataStoreTraits_>; +template constexpr bool IsWritableDataStore = WritableDataStoreTraits>::Implemented; +template using WritableDataStoreOnly = std::enable_if_t, TT>; + +template +void set_data(ArrItem &itm, const std::string &key, T &&data) +{ + WritableDataStoreTraits::template set(itm, key, std::forward(data)); +} + +template constexpr bool IsReadWritableDataStore = IsDataStore && IsWritableDataStore; +template using ReadWritableDataStoreOnly = std::enable_if_t, TT>; + +}} // namespace Slic3r::arr2 + +#endif // DATASTORETRAITS_HPP diff --git a/src/libslic3r/Arrange/Core/NFP/CircularEdgeIterator.hpp b/src/libslic3r/Arrange/Core/NFP/CircularEdgeIterator.hpp new file mode 100644 index 0000000000..3370530756 --- /dev/null +++ b/src/libslic3r/Arrange/Core/NFP/CircularEdgeIterator.hpp @@ -0,0 +1,110 @@ +#ifndef CIRCULAR_EDGEITERATOR_HPP +#define CIRCULAR_EDGEITERATOR_HPP + +#include +#include + +namespace Slic3r { + +// Circular iterator over a polygon yielding individual edges as Line objects +// if flip_lines is true, the orientation of each line is flipped (not the +// direction of traversal) +template +class CircularEdgeIterator_ { + const Polygon *m_poly = nullptr; + size_t m_i = 0; + size_t m_c = 0; // counting how many times the iterator has circled over + +public: + + // i: vertex position of first line's starting vertex + // poly: target polygon + CircularEdgeIterator_(size_t i, const Polygon &poly) + : m_poly{&poly} + , m_i{!poly.empty() ? i % poly.size() : 0} + , m_c{!poly.empty() ? i / poly.size() : 0} + {} + + explicit CircularEdgeIterator_ (const Polygon &poly) + : CircularEdgeIterator_(0, poly) {} + + using iterator_category = std::forward_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = Line; + using pointer = Line*; + using reference = Line&; + + CircularEdgeIterator_ & operator++() + { + assert (m_poly); + ++m_i; + if (m_i == m_poly->size()) { // faster than modulo (?) + m_i = 0; + ++m_c; + } + + return *this; + } + + CircularEdgeIterator_ operator++(int) + { + auto cpy = *this; ++(*this); return cpy; + } + + Line operator*() const + { + size_t nx = m_i == m_poly->size() - 1 ? 0 : m_i + 1; + Line ret; + if constexpr (flip_lines) + ret = Line((*m_poly)[nx], (*m_poly)[m_i]); + else + ret = Line((*m_poly)[m_i], (*m_poly)[nx]); + + return ret; + } + + Line operator->() const { return *(*this); } + + bool operator==(const CircularEdgeIterator_& other) const + { + return m_i == other.m_i && m_c == other.m_c; + } + + bool operator!=(const CircularEdgeIterator_& other) const + { + return !(*this == other); + } + + CircularEdgeIterator_& operator +=(size_t dist) + { + m_i = (m_i + dist) % m_poly->size(); + m_c = (m_i + (m_c * m_poly->size()) + dist) / m_poly->size(); + + return *this; + } + + CircularEdgeIterator_ operator +(size_t dist) + { + auto cpy = *this; + cpy += dist; + + return cpy; + } +}; + +using CircularEdgeIterator = CircularEdgeIterator_<>; +using CircularReverseEdgeIterator = CircularEdgeIterator_; + +inline Range line_range(const Polygon &poly) +{ + return Range{CircularEdgeIterator{0, poly}, CircularEdgeIterator{poly.size(), poly}}; +} + +inline Range line_range_flp(const Polygon &poly) +{ + return Range{CircularReverseEdgeIterator{0, poly}, CircularReverseEdgeIterator{poly.size(), poly}}; +} + +} // namespace Slic3r + +#endif // CIRCULAR_EDGEITERATOR_HPP diff --git a/src/libslic3r/Arrange/Core/NFP/EdgeCache.cpp b/src/libslic3r/Arrange/Core/NFP/EdgeCache.cpp new file mode 100644 index 0000000000..03e52fe0ab --- /dev/null +++ b/src/libslic3r/Arrange/Core/NFP/EdgeCache.cpp @@ -0,0 +1,92 @@ +#include "EdgeCache.hpp" +#include "CircularEdgeIterator.hpp" + +namespace Slic3r { namespace arr2 { + +void EdgeCache::create_cache(const ExPolygon &sh) +{ + m_contour.distances.reserve(sh.contour.size()); + m_holes.reserve(sh.holes.size()); + + m_contour.poly = &sh.contour; + + fill_distances(sh.contour, m_contour.distances); + + for (const Polygon &hole : sh.holes) { + auto &hc = m_holes.emplace_back(); + hc.poly = &hole; + fill_distances(hole, hc.distances); + } +} + +Vec2crd EdgeCache::coords(const ContourCache &cache, double distance) const +{ + assert(cache.poly); + return arr2::coords(*cache.poly, cache.distances, distance); +} + +void EdgeCache::sample_contour(double accuracy, std::vector &samples) +{ + const auto N = m_contour.distances.size(); + const auto S = stride(N, accuracy); + + samples.reserve(N / S + 1); + for(size_t i = 0; i < N; i += S) { + samples.emplace_back( + ContourLocation{0, m_contour.distances[i] / m_contour.distances.back()}); + } + + for (size_t hidx = 1; hidx <= m_holes.size(); ++hidx) { + auto& hc = m_holes[hidx - 1]; + + const auto NH = hc.distances.size(); + const auto SH = stride(NH, accuracy); + samples.reserve(samples.size() + NH / SH + 1); + for (size_t i = 0; i < NH; i += SH) { + samples.emplace_back( + ContourLocation{hidx, hc.distances[i] / hc.distances.back()}); + } + } +} + +Vec2crd coords(const Polygon &poly, const std::vector &distances, double distance) +{ + assert(poly.size() > 1 && distance >= .0 && distance <= 1.0); + + // distance is from 0.0 to 1.0, we scale it up to the full length of + // the circumference + double d = distance * distances.back(); + + // Magic: we find the right edge in log time + auto it = std::lower_bound(distances.begin(), distances.end(), d); + + assert(it != distances.end()); + + auto idx = it - distances.begin(); // get the index of the edge + auto &pts = poly.points; + auto edge = idx == long(pts.size() - 1) ? Line(pts.back(), pts.front()) : + Line(pts[idx], pts[idx + 1]); + + // Get the remaining distance on the target edge + auto ed = d - (idx > 0 ? *std::prev(it) : 0 ); + + double t = ed / edge.length(); + Vec2d n {double(edge.b.x()) - edge.a.x(), double(edge.b.y()) - edge.a.y()}; + Vec2crd ret = (edge.a.cast() + t * n).cast(); + + return ret; +} + +void fill_distances(const Polygon &poly, std::vector &distances) +{ + distances.reserve(poly.size()); + + double dist = 0.; + auto lrange = line_range(poly); + for (const Line &l : lrange) { + dist += l.length(); + distances.emplace_back(dist); + } +} + +}} // namespace Slic3r::arr2 diff --git a/src/libslic3r/Arrange/Core/NFP/EdgeCache.hpp b/src/libslic3r/Arrange/Core/NFP/EdgeCache.hpp new file mode 100644 index 0000000000..8146df1f8f --- /dev/null +++ b/src/libslic3r/Arrange/Core/NFP/EdgeCache.hpp @@ -0,0 +1,70 @@ +#ifndef EDGECACHE_HPP +#define EDGECACHE_HPP + +#include + +#include + +namespace Slic3r { namespace arr2 { + +// Position on the circumference of an ExPolygon. +// countour_id: 0th is contour, 1..N are holes +// dist: position given as a floating point number within <0., 1.> +struct ContourLocation { size_t contour_id; double dist; }; + +void fill_distances(const Polygon &poly, std::vector &distances); + +Vec2crd coords(const Polygon &poly, const std::vector& distances, double distance); + +// A class for getting a point on the circumference of the polygon (in log time) +// +// This is a transformation of the provided polygon to be able to pinpoint +// locations on the circumference. The optimizer will pass a floating point +// value e.g. within <0,1> and we have to transform this value quickly into a +// coordinate on the circumference. By definition 0 should yield the first +// vertex and 1.0 would be the last (which should coincide with first). +// +// We also have to make this work for the holes of the captured polygon. +class EdgeCache { + struct ContourCache { + const Polygon *poly; + std::vector distances; + } m_contour; + + std::vector m_holes; + + void create_cache(const ExPolygon& sh); + + Vec2crd coords(const ContourCache& cache, double distance) const; + +public: + + explicit EdgeCache(const ExPolygon *sh) + { + create_cache(*sh); + } + + // Given coeff for accuracy <0., 1.>, return the number of vertices to skip + // when fetching corners. + static inline size_t stride(const size_t N, double accuracy) + { + return static_cast( + std::round(N / std::pow(N, std::pow(accuracy, 1./3.))) + ); + } + + void sample_contour(double accuracy, std::vector &samples); + + Vec2crd coords(const ContourLocation &loc) const + { + assert(loc.contour_id <= m_holes.size()); + + return loc.contour_id > 0 ? + coords(m_holes[loc.contour_id - 1], loc.dist) : + coords(m_contour, loc.dist); + } +}; + +}} // namespace Slic3r::arr2 + +#endif // EDGECACHE_HPP diff --git a/src/libslic3r/Arrange/Core/NFP/Kernels/CompactifyKernel.hpp b/src/libslic3r/Arrange/Core/NFP/Kernels/CompactifyKernel.hpp new file mode 100644 index 0000000000..c476774d12 --- /dev/null +++ b/src/libslic3r/Arrange/Core/NFP/Kernels/CompactifyKernel.hpp @@ -0,0 +1,61 @@ +#ifndef COMPACTIFYKERNEL_HPP +#define COMPACTIFYKERNEL_HPP + +#include + +#include "libslic3r/Arrange/Core/NFP/NFPArrangeItemTraits.hpp" +#include "libslic3r/Arrange/Core/Beds.hpp" + +#include +#include + +#include "KernelUtils.hpp" + +namespace Slic3r { namespace arr2 { + +struct CompactifyKernel { + ExPolygons merged_pile; + + template + double placement_fitness(const ArrItem &itm, const Vec2crd &transl) const + { + auto pile = merged_pile; + + ExPolygons itm_tr = to_expolygons(envelope_outline(itm)); + for (auto &p : itm_tr) + p.translate(transl); + + append(pile, std::move(itm_tr)); + + pile = union_ex(pile); + + Polygon chull = Geometry::convex_hull(pile); + + return -(chull.area()); + } + + template + bool on_start_packing(ArrItem &itm, + const Bed &bed, + const Context &packing_context, + const Range & /*remaining_items*/) + { + bool ret = find_initial_position(itm, bounding_box(bed).center(), bed, + packing_context); + + merged_pile.clear(); + for (const auto &gitm : all_items_range(packing_context)) { + append(merged_pile, to_expolygons(fixed_outline(gitm))); + } + merged_pile = union_ex(merged_pile); + + return ret; + } + + template + bool on_item_packed(ArrItem &itm) { return true; } +}; + +}} // namespace Slic3r::arr2 + +#endif // COMPACTIFYKERNEL_HPP diff --git a/src/libslic3r/Arrange/Core/NFP/Kernels/GravityKernel.hpp b/src/libslic3r/Arrange/Core/NFP/Kernels/GravityKernel.hpp new file mode 100644 index 0000000000..ba4f503c76 --- /dev/null +++ b/src/libslic3r/Arrange/Core/NFP/Kernels/GravityKernel.hpp @@ -0,0 +1,58 @@ +#ifndef GRAVITYKERNEL_HPP +#define GRAVITYKERNEL_HPP + +#include "libslic3r/Arrange/Core/NFP/NFPArrangeItemTraits.hpp" +#include "libslic3r/Arrange/Core/Beds.hpp" + +#include "KernelUtils.hpp" + +namespace Slic3r { namespace arr2 { + +struct GravityKernel { + std::optional sink; + std::optional item_sink; + Vec2d active_sink; + + GravityKernel(Vec2crd gravity_center) : sink{gravity_center} {} + GravityKernel() = default; + + template + double placement_fitness(const ArrItem &itm, const Vec2crd &transl) const + { + Vec2d center = unscaled(envelope_bounding_box(itm).center()); + + center += unscaled(transl); + + return - (center - active_sink).squaredNorm(); + } + + template + bool on_start_packing(ArrItem &itm, + const Bed &bed, + const Ctx &packing_context, + const Range & /*remaining_items*/) + { + bool ret = false; + + item_sink = get_gravity_sink(itm); + + if (!sink) { + sink = bounding_box(bed).center(); + } + + if (item_sink) + active_sink = unscaled(*item_sink); + else + active_sink = unscaled(*sink); + + ret = find_initial_position(itm, scaled(active_sink), bed, packing_context); + + return ret; + } + + template bool on_item_packed(ArrItem &itm) { return true; } +}; + +}} // namespace Slic3r::arr2 + +#endif // GRAVITYKERNEL_HPP diff --git a/src/libslic3r/Arrange/Core/NFP/Kernels/KernelTraits.hpp b/src/libslic3r/Arrange/Core/NFP/Kernels/KernelTraits.hpp new file mode 100644 index 0000000000..04dec4e2bd --- /dev/null +++ b/src/libslic3r/Arrange/Core/NFP/Kernels/KernelTraits.hpp @@ -0,0 +1,57 @@ +#ifndef KERNELTRAITS_HPP +#define KERNELTRAITS_HPP + +#include "libslic3r/Arrange/Core/ArrangeItemTraits.hpp" + +namespace Slic3r { namespace arr2 { + +// An arrangement kernel that specifies the object function to the arrangement +// optimizer and additional callback functions to be able to track the state +// of the arranged pile during arrangement. +template struct KernelTraits_ +{ + // Has to return a score value marking the quality of the arrangement. The + // higher this value is, the better a particular placement of the item is. + // parameter transl is the translation needed for the item to be moved to + // the candidate position. + // To discard the item, return NaN as score for every translation. + template + static double placement_fitness(const Kernel &k, + const ArrItem &itm, + const Vec2crd &transl) + { + return k.placement_fitness(itm, transl); + } + + // Called whenever a new item is about to be processed by the optimizer. + // The current state of the arrangement can be saved by the kernel: the + // already placed items and the remaining items that need to fit into a + // particular bed. + // Returns true if the item is can be packed immediately, false if it + // should be processed further. This way, a kernel have the power to + // choose an initial position for the item that is not on the NFP. + template + static bool on_start_packing(Kernel &k, + ArrItem &itm, + const Bed &bed, + const Ctx &packing_context, + const Range &remaining_items) + { + return k.on_start_packing(itm, bed, packing_context, remaining_items); + } + + // Called when an item has been succesfully packed. itm should have the + // final translation and rotation already set. + // Can return false to discard the item after the optimization. + template + static bool on_item_packed(Kernel &k, ArrItem &itm) + { + return k.on_item_packed(itm); + } +}; + +template using KernelTraits = KernelTraits_>; + +}} // namespace Slic3r::arr2 + +#endif // KERNELTRAITS_HPP diff --git a/src/libslic3r/Arrange/Core/NFP/Kernels/KernelUtils.hpp b/src/libslic3r/Arrange/Core/NFP/Kernels/KernelUtils.hpp new file mode 100644 index 0000000000..fd37a0fb37 --- /dev/null +++ b/src/libslic3r/Arrange/Core/NFP/Kernels/KernelUtils.hpp @@ -0,0 +1,75 @@ +#ifndef ARRANGEKERNELUTILS_HPP +#define ARRANGEKERNELUTILS_HPP + +#include + +#include "libslic3r/Arrange/Core/NFP/NFPArrangeItemTraits.hpp" +#include "libslic3r/Arrange/Core/Beds.hpp" +#include "libslic3r/Arrange/Core/DataStoreTraits.hpp" + +namespace Slic3r { namespace arr2 { + +template +bool find_initial_position(Itm &itm, + const Vec2crd &sink, + const Bed &bed, + const Context &packing_context) +{ + bool ret = false; + + if constexpr (std::is_convertible_v || + std::is_convertible_v || + std::is_convertible_v) + { + if (all_items_range(packing_context).empty()) { + auto rotations = allowed_rotations(itm); + auto chull = envelope_convex_hull(itm); + + for (double rot : rotations) { + auto chullcpy = chull; + chullcpy.rotate(rot); + auto bbitm = bounding_box(chullcpy); + + Vec2crd cb = sink; + Vec2crd ci = bbitm.center(); + + Vec2crd d = cb - ci; + bbitm.translate(d); + + if (bounding_box(bed).contains(bbitm)) { + rotate(itm, rot); + translate(itm, d); + ret = true; + break; + } + } + } + } + + return ret; +} + +template std::optional get_gravity_sink(const ArrItem &itm) +{ + constexpr const char * SinkKey = "sink"; + + std::optional ret; + + auto ptr = get_data(itm, SinkKey); + + if (ptr) + ret = *ptr; + + return ret; +} + +template bool is_wipe_tower(const ArrItem &itm) +{ + constexpr const char * Key = "is_wipe_tower"; + + return has_key(itm, Key); +} + +}} // namespace Slic3r::arr2 + +#endif // ARRANGEKERNELUTILS_HPP diff --git a/src/libslic3r/Arrange/Core/NFP/Kernels/RectangleOverfitKernelWrapper.hpp b/src/libslic3r/Arrange/Core/NFP/Kernels/RectangleOverfitKernelWrapper.hpp new file mode 100644 index 0000000000..ca9cc0721a --- /dev/null +++ b/src/libslic3r/Arrange/Core/NFP/Kernels/RectangleOverfitKernelWrapper.hpp @@ -0,0 +1,94 @@ +#ifndef RECTANGLEOVERFITKERNELWRAPPER_HPP +#define RECTANGLEOVERFITKERNELWRAPPER_HPP + +#include "KernelTraits.hpp" + +#include "libslic3r/Arrange/Core/NFP/NFPArrangeItemTraits.hpp" +#include "libslic3r/Arrange/Core/Beds.hpp" + +namespace Slic3r { namespace arr2 { + +// This is a kernel wrapper that will apply a penality to the object function +// if the result cannot fit into the given rectangular bounds. This can be used +// to arrange into rectangular boundaries without calculating the IFP of the +// rectangle bed. Note that after the arrangement, what is garanteed is that +// the resulting pile will fit into the rectangular boundaries, but it will not +// be within the given rectangle. The items need to be moved afterwards manually. +// Use RectangeOverfitPackingStrategy to automate this post process step. +template +struct RectangleOverfitKernelWrapper { + Kernel &k; + BoundingBox binbb; + BoundingBox pilebb; + + RectangleOverfitKernelWrapper(Kernel &kern, const BoundingBox &limits) + : k{kern} + , binbb{limits} + {} + + double overfit(const BoundingBox &itmbb) const + { + auto fullbb = pilebb; + fullbb.merge(itmbb); + auto fullbbsz = fullbb.size(); + auto binbbsz = binbb.size(); + + auto wdiff = fullbbsz.x() - binbbsz.x() - SCALED_EPSILON; + auto hdiff = fullbbsz.y() - binbbsz.y() - SCALED_EPSILON; + double miss = .0; + if (wdiff > 0) + miss += double(wdiff); + if (hdiff > 0) + miss += double(hdiff); + + miss = miss > 0? miss : 0; + + return miss; + } + + template + double placement_fitness(const ArrItem &item, const Vec2crd &transl) const + { + double score = KernelTraits::placement_fitness(k, item, transl); + + auto itmbb = envelope_bounding_box(item); + itmbb.translate(transl); + double miss = overfit(itmbb); + score -= miss * miss; + + return score; + } + + template + bool on_start_packing(ArrItem &itm, + const Bed &bed, + const Ctx &packing_context, + const Range &remaining_items) + { + pilebb = BoundingBox{}; + + for (auto &fitm : all_items_range(packing_context)) + pilebb.merge(fixed_bounding_box(fitm)); + + return KernelTraits::on_start_packing(k, itm, RectangleBed{binbb}, + packing_context, + remaining_items); + } + + template + bool on_item_packed(ArrItem &itm) + { + bool ret = KernelTraits::on_item_packed(k, itm); + + double miss = overfit(envelope_bounding_box(itm)); + + if (miss > 0.) + ret = false; + + return ret; + } +}; + +}} // namespace Slic3r::arr2 + +#endif // RECTANGLEOVERFITKERNELWRAPPER_H diff --git a/src/libslic3r/Arrange/Core/NFP/Kernels/SVGDebugOutputKernelWrapper.hpp b/src/libslic3r/Arrange/Core/NFP/Kernels/SVGDebugOutputKernelWrapper.hpp new file mode 100644 index 0000000000..d52a46c226 --- /dev/null +++ b/src/libslic3r/Arrange/Core/NFP/Kernels/SVGDebugOutputKernelWrapper.hpp @@ -0,0 +1,96 @@ +#ifndef SVGDEBUGOUTPUTKERNELWRAPPER_HPP +#define SVGDEBUGOUTPUTKERNELWRAPPER_HPP + +#include + +#include "KernelTraits.hpp" + +#include "libslic3r/Arrange/Core/PackingContext.hpp" +#include "libslic3r/Arrange/Core/NFP/NFPArrangeItemTraits.hpp" +#include "libslic3r/Arrange/Core/Beds.hpp" + +#include + +namespace Slic3r { namespace arr2 { + +template +struct SVGDebugOutputKernelWrapper { + Kernel &k; + std::unique_ptr svg; + BoundingBox drawbounds; + + template + SVGDebugOutputKernelWrapper(const BoundingBox &bounds, Kernel &kern) + : k{kern}, drawbounds{bounds} + {} + + template + bool on_start_packing(ArrItem &itm, + const Bed &bed, + const Context &packing_context, + const Range &rem) + { + using namespace Slic3r; + + bool ret = KernelTraits::on_start_packing(k, itm, bed, + packing_context, + rem); + + if (arr2::get_bed_index(itm) < 0) + return ret; + + svg.reset(); + auto bounds = drawbounds; + auto fixed = all_items_range(packing_context); + svg = std::make_unique(std::string("arrange_bed") + + std::to_string( + arr2::get_bed_index(itm)) + + "_" + std::to_string(fixed.size()) + + ".svg", + bounds, 0, false); + + svg->draw(ExPolygon{arr2::to_rectangle(drawbounds)}, "blue", .2f); + + auto nfp = calculate_nfp(itm, packing_context, bed); + svg->draw_outline(nfp); + svg->draw(nfp, "green", 0.2f); + + for (const auto &fixeditm : fixed) { + ExPolygons fixeditm_outline = to_expolygons(fixed_outline(fixeditm)); + svg->draw_outline(fixeditm_outline); + svg->draw(fixeditm_outline, "yellow", 0.5f); + } + + return ret; + } + + template + double placement_fitness(const ArrItem &item, const Vec2crd &transl) const + { + return KernelTraits::placement_fitness(k, item, transl); + } + + template + bool on_item_packed(ArrItem &itm) + { + using namespace Slic3r; + using namespace Slic3r::arr2; + + bool ret = KernelTraits::on_item_packed(k, itm); + + if (svg) { + ExPolygons itm_outline = to_expolygons(fixed_outline(itm)); + + svg->draw_outline(itm_outline); + svg->draw(itm_outline, "grey"); + + svg->Close(); + } + + return ret; + } +}; + +}} // namespace Slic3r::arr2 + +#endif // SVGDEBUGOUTPUTKERNELWRAPPER_HPP diff --git a/src/libslic3r/Arrange/Core/NFP/Kernels/TMArrangeKernel.hpp b/src/libslic3r/Arrange/Core/NFP/Kernels/TMArrangeKernel.hpp new file mode 100644 index 0000000000..f5dba769d5 --- /dev/null +++ b/src/libslic3r/Arrange/Core/NFP/Kernels/TMArrangeKernel.hpp @@ -0,0 +1,268 @@ +#ifndef TMARRANGEKERNEL_HPP +#define TMARRANGEKERNEL_HPP + +#include "libslic3r/Arrange/Core/NFP/NFPArrangeItemTraits.hpp" +#include "libslic3r/Arrange/Core/Beds.hpp" + +#include "KernelUtils.hpp" + +#include +#include + +namespace Slic3r { namespace arr2 { + +// Summon the spatial indexing facilities from boost +namespace bgi = boost::geometry::index; +using SpatElement = std::pair; +using SpatIndex = bgi::rtree >; + +class TMArrangeKernel { + SpatIndex m_rtree; // spatial index for the normal (bigger) objects + SpatIndex m_smallsrtree; // spatial index for only the smaller items + BoundingBox m_pilebb; + double m_bin_area = NaNd; + double m_norm; + size_t m_rem_cnt = 0; + size_t m_item_cnt = 0; + + + struct ItemStats { double area = 0.; BoundingBox bb; }; + std::vector m_itemstats; + + // A coefficient used in separating bigger items and smaller items. + static constexpr double BigItemTreshold = 0.02; + + template ArithmeticOnly norm(T val) const + { + return double(val) / m_norm; + } + + // Treat big items (compared to the print bed) differently + bool is_big(double a) const { return a / m_bin_area > BigItemTreshold; } + +protected: + std::optional sink; + std::optional item_sink; + Point active_sink; + + const BoundingBox & pilebb() const { return m_pilebb; } + +public: + TMArrangeKernel() = default; + TMArrangeKernel(Vec2crd gravity_center, size_t itm_cnt, double bedarea = NaNd) + : sink{gravity_center} + , m_bin_area(bedarea) + , m_item_cnt{itm_cnt} + {} + + TMArrangeKernel(size_t itm_cnt, double bedarea = NaNd) + : m_bin_area(bedarea), m_item_cnt{itm_cnt} + {} + + template + double placement_fitness(const ArrItem &item, const Vec2crd &transl) const + { + // Candidate item bounding box + auto ibb = envelope_bounding_box(item); + ibb.translate(transl); + + // Calculate the full bounding box of the pile with the candidate item + auto fullbb = m_pilebb; + fullbb.merge(ibb); + + // The bounding box of the big items (they will accumulate in the center + // of the pile + BoundingBox bigbb; + if(m_rtree.empty()) { + bigbb = fullbb; + } + else { + auto boostbb = m_rtree.bounds(); + boost::geometry::convert(boostbb, bigbb); + } + + // Will hold the resulting score + double score = 0; + + // Density is the pack density: how big is the arranged pile + double density = 0; + + // Distinction of cases for the arrangement scene + enum e_cases { + // This branch is for big items in a mixed (big and small) scene + // OR for all items in a small-only scene. + BIG_ITEM, + + // This branch is for the last big item in a mixed scene + LAST_BIG_ITEM, + + // For small items in a mixed scene. + SMALL_ITEM, + + WIPE_TOWER, + } compute_case; + + bool is_wt = is_wipe_tower(item); + bool bigitems = is_big(envelope_area(item)) || m_rtree.empty(); + if (is_wt) + compute_case = WIPE_TOWER; + else if (bigitems && m_rem_cnt > 0) + compute_case = BIG_ITEM; + else if (bigitems && m_rem_cnt == 0) + compute_case = LAST_BIG_ITEM; + else + compute_case = SMALL_ITEM; + + switch (compute_case) { + case WIPE_TOWER: { + score = (unscaled(ibb.center()) - unscaled(active_sink)).squaredNorm(); + break; + } + case BIG_ITEM: { + const Point& minc = ibb.min; // bottom left corner + const Point& maxc = ibb.max; // top right corner + + // top left and bottom right corners + Point top_left{minc.x(), maxc.y()}; + Point bottom_right{maxc.x(), minc.y()}; + + // Now the distance of the gravity center will be calculated to the + // five anchor points and the smallest will be chosen. + std::array dists; + auto cc = fullbb.center(); // The gravity center + dists[0] = (minc - cc).cast().norm(); + dists[1] = (maxc - cc).cast().norm(); + dists[2] = (ibb.center() - cc).template cast().norm(); + dists[3] = (top_left - cc).cast().norm(); + dists[4] = (bottom_right - cc).cast().norm(); + + // The smalles distance from the arranged pile center: + double dist = norm(*(std::min_element(dists.begin(), dists.end()))); + double bindist = norm((ibb.center() - active_sink).template cast().norm()); + dist = 0.8 * dist + 0.2 * bindist; + + // Prepare a variable for the alignment score. + // This will indicate: how well is the candidate item + // aligned with its neighbors. We will check the alignment + // with all neighbors and return the score for the best + // alignment. So it is enough for the candidate to be + // aligned with only one item. + auto alignment_score = 1.0; + + auto query = bgi::intersects(ibb); + auto& index = is_big(envelope_area(item)) ? m_rtree : m_smallsrtree; + + // Query the spatial index for the neighbors + std::vector result; + result.reserve(index.size()); + + index.query(query, std::back_inserter(result)); + + // now get the score for the best alignment + for(auto& e : result) { + auto idx = e.second; + const ItemStats& p = m_itemstats[idx]; + auto parea = p.area; + if(std::abs(1.0 - parea / fixed_area(item)) < 1e-6) { + auto bb = p.bb; + bb.merge(ibb); + auto bbarea = area(bb); + auto ascore = 1.0 - (fixed_area(item) + parea) / bbarea; + + if(ascore < alignment_score) + alignment_score = ascore; + } + } + + auto fullbbsz = fullbb.size(); + density = std::sqrt(norm(fullbbsz.x()) * norm(fullbbsz.y())); + double R = double(m_rem_cnt) / (m_item_cnt); + + // The final mix of the score is the balance between the + // distance from the full pile center, the pack density and + // the alignment with the neighbors + if (result.empty()) + score = 0.50 * dist + 0.50 * density; + else + // Let the density matter more when fewer objects remain + score = 0.50 * dist + (1.0 - R) * 0.20 * density + + 0.30 * alignment_score; + + break; + } + case LAST_BIG_ITEM: { + score = norm((ibb.center() - m_pilebb.center()).template cast().norm()); + break; + } + case SMALL_ITEM: { + // Here there are the small items that should be placed around the + // already processed bigger items. + // No need to play around with the anchor points, the center will be + // just fine for small items + score = norm((ibb.center() - bigbb.center()).template cast().norm()); + break; + } + } + + return -score; + } + + template + bool on_start_packing(ArrItem &itm, + const Bed &bed, + const Context &packing_context, + const Range &remaining_items) + { + item_sink = get_gravity_sink(itm); + + if (!sink) { + sink = bounding_box(bed).center(); + } + + if (item_sink) + active_sink = *item_sink; + else + active_sink = *sink; + + auto fixed = all_items_range(packing_context); + + bool ret = find_initial_position(itm, active_sink, bed, packing_context); + + m_rem_cnt = remaining_items.size(); + + if (m_item_cnt == 0) + m_item_cnt = m_rem_cnt + fixed.size() + 1; + + if (std::isnan(m_bin_area)) + m_bin_area = area(bed); + + m_norm = std::sqrt(m_bin_area); + + m_itemstats.clear(); + m_itemstats.reserve(fixed.size()); + m_rtree.clear(); + m_smallsrtree.clear(); + m_pilebb = {}; + unsigned idx = 0; + for (auto &fixitem : fixed) { + auto fixitmbb = fixed_bounding_box(fixitem); + m_itemstats.emplace_back(ItemStats{fixed_area(fixitem), fixitmbb}); + m_pilebb.merge(fixitmbb); + + if(is_big(fixed_area(fixitem))) + m_rtree.insert({fixitmbb, idx}); + + m_smallsrtree.insert({fixitmbb, idx}); + idx++; + } + + return ret; + } + + template + bool on_item_packed(ArrItem &itm) { return true; } +}; + +}} // namespace Slic3r::arr2 + +#endif // TMARRANGEKERNEL_HPP diff --git a/src/libslic3r/Arrange/Core/NFP/NFP.cpp b/src/libslic3r/Arrange/Core/NFP/NFP.cpp new file mode 100644 index 0000000000..5dfe559f73 --- /dev/null +++ b/src/libslic3r/Arrange/Core/NFP/NFP.cpp @@ -0,0 +1,412 @@ +#ifndef NFP_CPP +#define NFP_CPP + +#include "NFP.hpp" +#include "CircularEdgeIterator.hpp" + +#include "NFPConcave_Tesselate.hpp" + +#if !defined(_MSC_VER) && defined(__SIZEOF_INT128__) && !defined(__APPLE__) +namespace Slic3r { using LargeInt = __int128; } +#else +#include +namespace Slic3r { using LargeInt = boost::multiprecision::int128_t; } +#endif + +#include + +namespace Slic3r { + +static bool line_cmp(const Line& e1, const Line& e2) +{ + using Ratio = boost::rational; + + const Vec<2, int64_t> ax(1, 0); // Unit vector for the X axis + + Vec<2, int64_t> p1 = (e1.b - e1.a).cast(); + Vec<2, int64_t> p2 = (e2.b - e2.a).cast(); + + // Quadrant mapping array. The quadrant of a vector can be determined + // from the dot product of the vector and its perpendicular pair + // with the unit vector X axis. The products will carry the values + // lcos = dot(p, ax) = l * cos(phi) and + // lsin = -dotperp(p, ax) = l * sin(phi) where + // l is the length of vector p. From the signs of these values we can + // construct an index which has the sign of lcos as MSB and the + // sign of lsin as LSB. This index can be used to retrieve the actual + // quadrant where vector p resides using the following map: + // (+ is 0, - is 1) + // cos | sin | decimal | quadrant + // + | + | 0 | 0 + // + | - | 1 | 3 + // - | + | 2 | 1 + // - | - | 3 | 2 + std::array quadrants {0, 3, 1, 2 }; + + std::array q {0, 0}; // Quadrant indices for p1 and p2 + + using TDots = std::array; + TDots lcos { p1.dot(ax), p2.dot(ax) }; + TDots lsin { -dotperp(p1, ax), -dotperp(p2, ax) }; + + // Construct the quadrant indices for p1 and p2 + for(size_t i = 0; i < 2; ++i) { + if (lcos[i] == 0) + q[i] = lsin[i] > 0 ? 1 : 3; + else if (lsin[i] == 0) + q[i] = lcos[i] > 0 ? 0 : 2; + else + q[i] = quadrants[((lcos[i] < 0) << 1) + (lsin[i] < 0)]; + } + + if (q[0] == q[1]) { // only bother if p1 and p2 are in the same quadrant + auto lsq1 = p1.squaredNorm(); // squared magnitudes, avoid sqrt + auto lsq2 = p2.squaredNorm(); // squared magnitudes, avoid sqrt + + // We will actually compare l^2 * cos^2(phi) which saturates the + // cos function. But with the quadrant info we can get the sign back + int sign = q[0] == 1 || q[0] == 2 ? -1 : 1; + + // If Ratio is an actual rational type, there is no precision loss + auto pcos1 = Ratio(lcos[0]) / lsq1 * sign * lcos[0]; + auto pcos2 = Ratio(lcos[1]) / lsq2 * sign * lcos[1]; + + return q[0] < 2 ? pcos1 > pcos2 : pcos1 < pcos2; + } + + // If in different quadrants, compare the quadrant indices only. + return q[0] < q[1]; +} + +static inline bool vsort(const Vec2crd& v1, const Vec2crd& v2) +{ + return v1.y() == v2.y() ? v1.x() < v2.x() : v1.y() < v2.y(); +} + +ExPolygons ifp_convex(const arr2::RectangleBed &obed, const Polygon &convexpoly) +{ + ExPolygon ret; + + auto sbox = bounding_box(convexpoly); + auto sboxsize = sbox.size(); + coord_t sheight = sboxsize.y(); + coord_t swidth = sboxsize.x(); + Point sliding_top = reference_vertex(convexpoly); + auto leftOffset = sliding_top.x() - sbox.min.x(); + auto rightOffset = sliding_top.x() - sbox.max.x(); + coord_t topOffset = 0; + auto bottomOffset = sheight; + + auto bedbb = obed.bb; +// bedbb.offset(1); + auto bedsz = bedbb.size(); + auto boxWidth = bedsz.x(); + auto boxHeight = bedsz.y(); + + auto bedMinx = bedbb.min.x(); + auto bedMiny = bedbb.min.y(); + auto bedMaxx = bedbb.max.x(); + auto bedMaxy = bedbb.max.y(); + + Polygon innerNfp{ Point{bedMinx + leftOffset, bedMaxy + topOffset}, + Point{bedMaxx + rightOffset, bedMaxy + topOffset}, + Point{bedMaxx + rightOffset, bedMiny + bottomOffset}, + Point{bedMinx + leftOffset, bedMiny + bottomOffset}, + Point{bedMinx + leftOffset, bedMaxy + topOffset} }; + + if (sheight <= boxHeight && swidth <= boxWidth) + ret.contour = std::move(innerNfp); + + return {ret}; +} + +Polygon ifp_convex_convex(const Polygon &fixed, const Polygon &movable) +{ + auto subnfps = reserve_polygons(fixed.size()); + + // For each edge of the bed polygon, determine the nfp of convexpoly and + // the zero area polygon formed by the edge. The union of all these sub-nfps + // will contain a hole that is the actual ifp. + auto lrange = line_range(fixed); + for (const Line &l : lrange) { // Older mac compilers generate warnging if line_range is called in-place + Polygon fixed = {l.a, l.b}; + subnfps.emplace_back(nfp_convex_convex_legacy(fixed, movable)); + } + + // Do the union and then keep only the holes (should be only one or zero, if + // the convexpoly cannot fit into the bed) + Polygons ifp = union_(subnfps); + Polygon ret; + + // find the first hole + auto it = std::find_if(ifp.begin(), ifp.end(), [](const Polygon &subifp){ + return subifp.is_clockwise(); + }); + + if (it != ifp.end()) { + ret = std::move(*it); + std::reverse(ret.begin(), ret.end()); + } + + return ret; +} + +ExPolygons ifp_convex(const arr2::CircleBed &bed, const Polygon &convexpoly) +{ + Polygon circle = approximate_circle_with_polygon(bed); + + return {ExPolygon{ifp_convex_convex(circle, convexpoly)}}; +} + +ExPolygons ifp_convex(const arr2::IrregularBed &bed, const Polygon &convexpoly) +{ + auto bb = get_extents(bed.poly); + bb.offset(scaled(1.)); + + Polygon rect = arr2::to_rectangle(bb); + + ExPolygons blueprint = diff_ex(rect, bed.poly); + Polygons ifp; + for (const ExPolygon &part : blueprint) { + Polygons triangles = Slic3r::convex_decomposition_tess(part); + for (const Polygon &tr : triangles) { + Polygon subifp = nfp_convex_convex_legacy(tr, convexpoly); + ifp.emplace_back(std::move(subifp)); + } + } + + ifp = union_(ifp); + + Polygons ret; + + std::copy_if(ifp.begin(), ifp.end(), std::back_inserter(ret), + [](const Polygon &p) { return p.is_clockwise(); }); + + for (Polygon &p : ret) + std::reverse(p.begin(), p.end()); + + return to_expolygons(ret); +} + +Vec2crd reference_vertex(const Polygon &poly) +{ + Vec2crd ret{std::numeric_limits::min(), + std::numeric_limits::min()}; + + auto it = std::max_element(poly.points.begin(), poly.points.end(), vsort); + if (it != poly.points.end()) + ret = std::max(ret, static_cast(*it), vsort); + + return ret; +} + +Vec2crd reference_vertex(const ExPolygon &expoly) +{ + return reference_vertex(expoly.contour); +} + +Vec2crd reference_vertex(const Polygons &outline) +{ + Vec2crd ret{std::numeric_limits::min(), + std::numeric_limits::min()}; + + for (const Polygon &poly : outline) + ret = std::max(ret, reference_vertex(poly), vsort); + + return ret; +} + +Vec2crd reference_vertex(const ExPolygons &outline) +{ + Vec2crd ret{std::numeric_limits::min(), + std::numeric_limits::min()}; + + for (const ExPolygon &expoly : outline) + ret = std::max(ret, reference_vertex(expoly), vsort); + + return ret; +} + +Vec2crd min_vertex(const Polygon &poly) +{ + Vec2crd ret{std::numeric_limits::max(), + std::numeric_limits::max()}; + + auto it = std::min_element(poly.points.begin(), poly.points.end(), vsort); + if (it != poly.points.end()) + ret = std::min(ret, static_cast(*it), vsort); + + return ret; +} + +// Find the vertex corresponding to the edge with minimum angle to X axis. +// Only usable with CircularEdgeIterator<> template. +template It find_min_anglex_edge(It from) +{ + bool found = false; + auto it = from; + while (!found ) { + found = !line_cmp(*it, *std::next(it)); + ++it; + } + + return it; +} + +// Only usable if both fixed and movable polygon is convex. In that case, +// their edges are already sorted by angle to X axis, only the starting +// (lowest X axis) edge needs to be found first. +void nfp_convex_convex(const Polygon &fixed, const Polygon &movable, Polygon &poly) +{ + if (fixed.empty() || movable.empty()) + return; + + // Clear poly and adjust its capacity. Nothing happens if poly is + // already sufficiently large and and empty. + poly.clear(); + poly.points.reserve(fixed.size() + movable.size()); + + // Find starting positions on the fixed and moving polygons + auto it_fx = find_min_anglex_edge(CircularEdgeIterator{fixed}); + auto it_mv = find_min_anglex_edge(CircularReverseEdgeIterator{movable}); + + // End positions are at the same vertex after completing one full circle + auto end_fx = it_fx + fixed.size(); + auto end_mv = it_mv + movable.size(); + + // Pos zero is just fine as starting point: + poly.points.emplace_back(0, 0); + + // Output iterator adapter for std::merge + struct OutItAdaptor { + Polygon *outpoly; + OutItAdaptor(Polygon &out) : outpoly{&out} {} + + OutItAdaptor &operator *() { return *this; } + void operator=(const Line &l) + { + // Yielding l.b, offsetted so that l.a touches the last vertex in + // in outpoly + outpoly->points.emplace_back(l.b + outpoly->back() - l.a); + } + + OutItAdaptor& operator++() { return *this; }; + }; + + // Use std algo to merge the edges from the two polygons + std::merge(it_fx, end_fx, it_mv, end_mv, OutItAdaptor{poly}, line_cmp); +} + +Polygon nfp_convex_convex(const Polygon &fixed, const Polygon &movable) +{ + Polygon ret; + nfp_convex_convex(fixed, movable, ret); + + return ret; +} + +static void buildPolygon(const std::vector& edgelist, + Polygon& rpoly, + Point& top_nfp) +{ + auto& rsh = rpoly.points; + + rsh.reserve(2 * edgelist.size()); + + // Add the two vertices from the first edge into the final polygon. + rsh.emplace_back(edgelist.front().a); + rsh.emplace_back(edgelist.front().b); + + // Sorting function for the nfp reference vertex search + + // the reference (rightmost top) vertex so far + top_nfp = *std::max_element(std::cbegin(rsh), std::cend(rsh), vsort); + + auto tmp = std::next(std::begin(rsh)); + + // Construct final nfp by placing each edge to the end of the previous + for(auto eit = std::next(edgelist.begin()); eit != edgelist.end(); ++eit) { + auto d = *tmp - eit->a; + Vec2crd p = eit->b + d; + + rsh.emplace_back(p); + + // Set the new reference vertex + if (vsort(top_nfp, p)) + top_nfp = p; + + tmp = std::next(tmp); + } +} + +Polygon nfp_convex_convex_legacy(const Polygon &fixed, const Polygon &movable) +{ + assert (!fixed.empty()); + assert (!movable.empty()); + + Polygon rsh; // Final nfp placeholder + Point max_nfp; + std::vector edgelist; + + auto cap = fixed.points.size() + movable.points.size(); + + // Reserve the needed memory + edgelist.reserve(cap); + rsh.points.reserve(cap); + + auto add_edge = [&edgelist](const Point &v1, const Point &v2) { + Line e{v1, v2}; + if ((e.b - e.a).cast().squaredNorm() > 0) + edgelist.emplace_back(e); + }; + + Point max_fixed = fixed.points.front(); + { // place all edges from fixed into edgelist + auto first = std::cbegin(fixed); + auto next = std::next(first); + + while(next != std::cend(fixed)) { + add_edge(*(first), *(next)); + max_fixed = std::max(max_fixed, *first, vsort); + + ++first; ++next; + } + + add_edge(*std::crbegin(fixed), *std::cbegin(fixed)); + max_fixed = std::max(max_fixed, *std::crbegin(fixed), vsort); + } + + Point max_movable = movable.points.front(); + Point min_movable = movable.points.front(); + { // place all edges from movable into edgelist + auto first = std::cbegin(movable); + auto next = std::next(first); + + while(next != std::cend(movable)) { + add_edge(*(next), *(first)); + min_movable = std::min(min_movable, *first, vsort); + max_movable = std::max(max_movable, *first, vsort); + + ++first; ++next; + } + + add_edge(*std::cbegin(movable), *std::crbegin(movable)); + min_movable = std::min(min_movable, *std::crbegin(movable), vsort); + max_movable = std::max(max_movable, *std::crbegin(movable), vsort); + } + + std::sort(edgelist.begin(), edgelist.end(), line_cmp); + + buildPolygon(edgelist, rsh, max_nfp); + + auto dtouch = max_fixed - min_movable; + auto top_other = max_movable + dtouch; + auto dnfp = top_other - max_nfp; + rsh.translate(dnfp); + + return rsh; +} + +} // namespace Slic3r + +#endif // NFP_CPP diff --git a/src/libslic3r/Arrange/Core/NFP/NFP.hpp b/src/libslic3r/Arrange/Core/NFP/NFP.hpp new file mode 100644 index 0000000000..3f23ea133b --- /dev/null +++ b/src/libslic3r/Arrange/Core/NFP/NFP.hpp @@ -0,0 +1,50 @@ +#ifndef NFP_HPP +#define NFP_HPP + +#include +#include + +namespace Slic3r { + +template +Unit dotperp(const Vec<2, T> &a, const Vec<2, T> &b) +{ + return Unit(a.x()) * Unit(b.y()) - Unit(a.y()) * Unit(b.x()); +} + +// Convex-Convex nfp in linear time (fixed.size() + movable.size()), +// no memory allocations (if out param is used). +// FIXME: Currently broken for very sharp triangles. +Polygon nfp_convex_convex(const Polygon &fixed, const Polygon &movable); +void nfp_convex_convex(const Polygon &fixed, const Polygon &movable, Polygon &out); +Polygon nfp_convex_convex_legacy(const Polygon &fixed, const Polygon &movable); + +Polygon ifp_convex_convex(const Polygon &fixed, const Polygon &movable); + +ExPolygons ifp_convex(const arr2::RectangleBed &bed, const Polygon &convexpoly); +ExPolygons ifp_convex(const arr2::CircleBed &bed, const Polygon &convexpoly); +ExPolygons ifp_convex(const arr2::IrregularBed &bed, const Polygon &convexpoly); +inline ExPolygons ifp_convex(const arr2::InfiniteBed &bed, const Polygon &convexpoly) +{ + return {}; +} + +inline ExPolygons ifp_convex(const arr2::ArrangeBed &bed, const Polygon &convexpoly) +{ + ExPolygons ret; + auto visitor = [&ret, &convexpoly](const auto &b) { ret = ifp_convex(b, convexpoly); }; + boost::apply_visitor(visitor, bed); + + return ret; +} + +Vec2crd reference_vertex(const Polygon &outline); +Vec2crd reference_vertex(const ExPolygon &outline); +Vec2crd reference_vertex(const Polygons &outline); +Vec2crd reference_vertex(const ExPolygons &outline); + +Vec2crd min_vertex(const Polygon &outline); + +} // namespace Slic3r + +#endif // NFP_HPP diff --git a/src/libslic3r/Arrange/Core/NFP/NFPArrangeItemTraits.hpp b/src/libslic3r/Arrange/Core/NFP/NFPArrangeItemTraits.hpp new file mode 100644 index 0000000000..009162661c --- /dev/null +++ b/src/libslic3r/Arrange/Core/NFP/NFPArrangeItemTraits.hpp @@ -0,0 +1,176 @@ +#ifndef NFPARRANGEITEMTRAITS_HPP +#define NFPARRANGEITEMTRAITS_HPP + +#include + +#include "libslic3r/Arrange/Core/ArrangeBase.hpp" + +#include "libslic3r/ExPolygon.hpp" +#include "libslic3r/BoundingBox.hpp" + +namespace Slic3r { namespace arr2 { + +// Additional methods that an ArrangeItem object has to implement in order +// to be usable with PackStrategyNFP. +template struct NFPArrangeItemTraits_ +{ + template + static ExPolygons calculate_nfp(const ArrItem &item, + const Context &packing_context, + const Bed &bed, + StopCond stop_condition = {}) + { + static_assert(always_false::value, + "NFP unimplemented for this item type."); + return {}; + } + + static Vec2crd reference_vertex(const ArrItem &item) + { + return item.reference_vertex(); + } + + static BoundingBox envelope_bounding_box(const ArrItem &itm) + { + return itm.envelope_bounding_box(); + } + + static BoundingBox fixed_bounding_box(const ArrItem &itm) + { + return itm.fixed_bounding_box(); + } + + static const Polygons & envelope_outline(const ArrItem &itm) + { + return itm.envelope_outline(); + } + + static const Polygons & fixed_outline(const ArrItem &itm) + { + return itm.fixed_outline(); + } + + static const Polygon & envelope_convex_hull(const ArrItem &itm) + { + return itm.envelope_convex_hull(); + } + + static const Polygon & fixed_convex_hull(const ArrItem &itm) + { + return itm.fixed_convex_hull(); + } + + static double envelope_area(const ArrItem &itm) + { + return itm.envelope_area(); + } + + static double fixed_area(const ArrItem &itm) + { + return itm.fixed_area(); + } + + static auto allowed_rotations(const ArrItem &) + { + return std::array{0.}; + } +}; + +template +using NFPArrangeItemTraits = NFPArrangeItemTraits_>; + +template +ExPolygons calculate_nfp(const ArrItem &itm, + const Context &context, + const Bed &bed, + StopCond stopcond = {}) +{ + return NFPArrangeItemTraits::calculate_nfp(itm, context, bed, + std::move(stopcond)); +} + +template Vec2crd reference_vertex(const ArrItem &itm) +{ + return NFPArrangeItemTraits::reference_vertex(itm); +} + +template BoundingBox envelope_bounding_box(const ArrItem &itm) +{ + return NFPArrangeItemTraits::envelope_bounding_box(itm); +} + +template BoundingBox fixed_bounding_box(const ArrItem &itm) +{ + return NFPArrangeItemTraits::fixed_bounding_box(itm); +} + +template decltype(auto) envelope_convex_hull(const ArrItem &itm) +{ + return NFPArrangeItemTraits::envelope_convex_hull(itm); +} + +template decltype(auto) fixed_convex_hull(const ArrItem &itm) +{ + return NFPArrangeItemTraits::fixed_convex_hull(itm); +} + +template decltype(auto) envelope_outline(const ArrItem &itm) +{ + return NFPArrangeItemTraits::envelope_outline(itm); +} + +template decltype(auto) fixed_outline(const ArrItem &itm) +{ + return NFPArrangeItemTraits::fixed_outline(itm); +} + +template double envelope_area(const ArrItem &itm) +{ + return NFPArrangeItemTraits::envelope_area(itm); +} + +template double fixed_area(const ArrItem &itm) +{ + return NFPArrangeItemTraits::fixed_area(itm); +} + +template +auto allowed_rotations(const ArrItem &itm) +{ + return NFPArrangeItemTraits::allowed_rotations(itm); +} + +template +BoundingBox bounding_box(const Range &itms) noexcept +{ + auto pilebb = + std::accumulate(itms.begin(), itms.end(), BoundingBox{}, + [](BoundingBox bb, const auto &itm) { + bb.merge(fixed_bounding_box(itm)); + return bb; + }); + + return pilebb; +} + +template +BoundingBox bounding_box_on_bedidx(const Range &itms, int bed_index) noexcept +{ + auto pilebb = + std::accumulate(itms.begin(), itms.end(), BoundingBox{}, + [bed_index](BoundingBox bb, const auto &itm) { + if (bed_index == get_bed_index(itm)) + bb.merge(fixed_bounding_box(itm)); + + return bb; + }); + + return pilebb; +} + +}} // namespace Slic3r::arr2 + +#endif // ARRANGEITEMTRAITSNFP_HPP diff --git a/src/libslic3r/Arrange/Core/NFP/NFPConcave_CGAL.cpp b/src/libslic3r/Arrange/Core/NFP/NFPConcave_CGAL.cpp new file mode 100644 index 0000000000..96cec85b8b --- /dev/null +++ b/src/libslic3r/Arrange/Core/NFP/NFPConcave_CGAL.cpp @@ -0,0 +1,111 @@ +#include "NFP.hpp" +#include "NFPConcave_CGAL.hpp" + +#include +#include +#include +#include +#include + +#include "libslic3r/ClipperUtils.hpp" + +namespace Slic3r { + +using K = CGAL::Exact_predicates_inexact_constructions_kernel; +using Partition_traits_2 = CGAL::Partition_traits_2::type >; +using Point_2 = Partition_traits_2::Point_2; +using Polygon_2 = Partition_traits_2::Polygon_2; // a polygon of indices + +ExPolygons nfp_concave_concave_cgal(const ExPolygon &fixed, const ExPolygon &movable) +{ + Polygons fixed_decomp = convex_decomposition_cgal(fixed); + Polygons movable_decomp = convex_decomposition_cgal(movable); + + auto refs_mv = reserve_vector(movable_decomp.size()); + + for (const Polygon &p : movable_decomp) + refs_mv.emplace_back(reference_vertex(p)); + + auto nfps = reserve_polygons(fixed_decomp.size() *movable_decomp.size()); + + Vec2crd ref_whole = reference_vertex(movable); + for (const Polygon &fixed_part : fixed_decomp) { + size_t mvi = 0; + for (const Polygon &movable_part : movable_decomp) { + Polygon subnfp = nfp_convex_convex(fixed_part, movable_part); + const Vec2crd &ref_mp = refs_mv[mvi]; + auto d = ref_whole - ref_mp; + subnfp.translate(d); + nfps.emplace_back(subnfp); + mvi++; + } + } + + return union_ex(nfps); +} + +// TODO: holes +Polygons convex_decomposition_cgal(const ExPolygon &expoly) +{ + CGAL::Polygon_vertical_decomposition_2 decomp; + + CGAL::Polygon_2 contour; + for (auto &p : expoly.contour.points) + contour.push_back({unscaled(p.x()), unscaled(p.y())}); + + CGAL::Polygon_with_holes_2 cgalpoly{contour}; + for (const Polygon &h : expoly.holes) { + CGAL::Polygon_2 hole; + for (auto &p : h.points) + hole.push_back({unscaled(p.x()), unscaled(p.y())}); + + cgalpoly.add_hole(hole); + } + + std::vector> out; + decomp(cgalpoly, std::back_inserter(out)); + + Polygons ret; + for (auto &pwh : out) { + Polygon poly; + for (auto &p : pwh) + poly.points.emplace_back(scaled(p.x()), scaled(p.y())); + ret.emplace_back(std::move(poly)); + } + + return ret; //convex_decomposition_cgal(expoly.contour); +} + +Polygons convex_decomposition_cgal(const Polygon &poly) +{ + auto pts = reserve_vector(poly.size()); + + for (const Point &p : poly.points) + pts.emplace_back(unscaled(p.x()), unscaled(p.y())); + + Partition_traits_2 traits(CGAL::make_property_map(pts)); + + Polygon_2 polyidx; + for (size_t i = 0; i < pts.size(); ++i) + polyidx.push_back(i); + + std::vector outp; + + CGAL::optimal_convex_partition_2(polyidx.vertices_begin(), + polyidx.vertices_end(), + std::back_inserter(outp), + traits); + + Polygons ret; + for (const Polygon_2& poly : outp){ + Polygon r; + for(Point_2 p : poly.container()) + r.points.emplace_back(scaled(pts[p].x()), scaled(pts[p].y())); + + ret.emplace_back(std::move(r)); + } + + return ret; +} + +} // namespace Slic3r diff --git a/src/libslic3r/Arrange/Core/NFP/NFPConcave_CGAL.hpp b/src/libslic3r/Arrange/Core/NFP/NFPConcave_CGAL.hpp new file mode 100644 index 0000000000..89e73ea4d6 --- /dev/null +++ b/src/libslic3r/Arrange/Core/NFP/NFPConcave_CGAL.hpp @@ -0,0 +1,14 @@ +#ifndef NFPCONCAVE_CGAL_HPP +#define NFPCONCAVE_CGAL_HPP + +#include + +namespace Slic3r { + +Polygons convex_decomposition_cgal(const Polygon &expoly); +Polygons convex_decomposition_cgal(const ExPolygon &expoly); +ExPolygons nfp_concave_concave_cgal(const ExPolygon &fixed, const ExPolygon &movable); + +} // namespace Slic3r + +#endif // NFPCONCAVE_CGAL_HPP diff --git a/src/libslic3r/Arrange/Core/NFP/NFPConcave_Tesselate.cpp b/src/libslic3r/Arrange/Core/NFP/NFPConcave_Tesselate.cpp new file mode 100644 index 0000000000..4b3660a9cc --- /dev/null +++ b/src/libslic3r/Arrange/Core/NFP/NFPConcave_Tesselate.cpp @@ -0,0 +1,70 @@ +#include "NFPConcave_Tesselate.hpp" + +#include +#include + +#include "NFP.hpp" + +namespace Slic3r { + +Polygons convex_decomposition_tess(const Polygon &expoly) +{ + return convex_decomposition_tess(ExPolygon{expoly}); +} + +Polygons convex_decomposition_tess(const ExPolygon &expoly) +{ + std::vector tr = Slic3r::triangulate_expolygon_2d(expoly); + + auto ret = Slic3r::reserve_polygons(tr.size() / 3); + for (size_t i = 0; i < tr.size(); i += 3) { + ret.emplace_back( + Polygon{scaled(tr[i]), scaled(tr[i + 1]), scaled(tr[i + 2])}); + } + + return ret; +} + +Polygons convex_decomposition_tess(const ExPolygons &expolys) +{ + constexpr size_t AvgTriangleCountGuess = 50; + + auto ret = reserve_polygons(AvgTriangleCountGuess * expolys.size()); + for (const ExPolygon &expoly : expolys) { + Polygons convparts = convex_decomposition_tess(expoly); + std::move(convparts.begin(), convparts.end(), std::back_inserter(ret)); + } + + return ret; +} + +ExPolygons nfp_concave_concave_tess(const ExPolygon &fixed, + const ExPolygon &movable) +{ + Polygons fixed_decomp = convex_decomposition_tess(fixed); + Polygons movable_decomp = convex_decomposition_tess(movable); + + auto refs_mv = reserve_vector(movable_decomp.size()); + + for (const Polygon &p : movable_decomp) + refs_mv.emplace_back(reference_vertex(p)); + + auto nfps = reserve_polygons(fixed_decomp.size() * movable_decomp.size()); + + Vec2crd ref_whole = reference_vertex(movable); + for (const Polygon &fixed_part : fixed_decomp) { + size_t mvi = 0; + for (const Polygon &movable_part : movable_decomp) { + Polygon subnfp = nfp_convex_convex(fixed_part, movable_part); + const Vec2crd &ref_mp = refs_mv[mvi]; + auto d = ref_whole - ref_mp; + subnfp.translate(d); + nfps.emplace_back(subnfp); + mvi++; + } + } + + return union_ex(nfps); +} + +} // namespace Slic3r diff --git a/src/libslic3r/Arrange/Core/NFP/NFPConcave_Tesselate.hpp b/src/libslic3r/Arrange/Core/NFP/NFPConcave_Tesselate.hpp new file mode 100644 index 0000000000..05e7a48a50 --- /dev/null +++ b/src/libslic3r/Arrange/Core/NFP/NFPConcave_Tesselate.hpp @@ -0,0 +1,15 @@ +#ifndef NFPCONCAVE_TESSELATE_HPP +#define NFPCONCAVE_TESSELATE_HPP + +#include + +namespace Slic3r { + +Polygons convex_decomposition_tess(const Polygon &expoly); +Polygons convex_decomposition_tess(const ExPolygon &expoly); +Polygons convex_decomposition_tess(const ExPolygons &expolys); +ExPolygons nfp_concave_concave_tess(const ExPolygon &fixed, const ExPolygon &movable); + +} // namespace Slic3r + +#endif // NFPCONCAVE_TESSELATE_HPP diff --git a/src/libslic3r/Arrange/Core/NFP/PackStrategyNFP.hpp b/src/libslic3r/Arrange/Core/NFP/PackStrategyNFP.hpp new file mode 100644 index 0000000000..c042829dfb --- /dev/null +++ b/src/libslic3r/Arrange/Core/NFP/PackStrategyNFP.hpp @@ -0,0 +1,279 @@ +#ifndef PACKSTRATEGYNFP_HPP +#define PACKSTRATEGYNFP_HPP + +#include "libslic3r/Arrange/Core/ArrangeBase.hpp" + +#include "EdgeCache.hpp" +#include "Kernels/KernelTraits.hpp" + +#include "NFPArrangeItemTraits.hpp" + +#include "libslic3r/Optimize/NLoptOptimizer.hpp" +#include "libslic3r/Execution/ExecutionSeq.hpp" + +namespace Slic3r { namespace arr2 { + +struct NFPPackingTag{}; + +struct DummyArrangeKernel +{ + template + double placement_fitness(const ArrItem &itm, const Vec2crd &dest_pos) const + { + return NaNd; + } + + template + bool on_start_packing(ArrItem &itm, + const Bed &bed, + const Context &packing_context, + const Range &remaining_items) + { + return true; + } + + template bool on_item_packed(ArrItem &itm) { return true; } +}; + +template using OptAlg = typename Strategy::OptAlg; + +template +struct PackStrategyNFP { + using OptAlg = OptMethod; + + ArrangeKernel kernel; + ExecPolicy ep; + double accuracy = 1.; + opt::Optimizer solver; + StopCond stop_condition; + + PackStrategyNFP(opt::Optimizer slv, + ArrangeKernel k = {}, + ExecPolicy execpolicy = {}, + double accur = 1., + StopCond stop_cond = {}) + : kernel{std::move(k)}, + ep{std::move(execpolicy)}, + accuracy{accur}, + solver{std::move(slv)}, + stop_condition{std::move(stop_cond)} + {} + + PackStrategyNFP(ArrangeKernel k = {}, + ExecPolicy execpolicy = {}, + double accur = 1., + StopCond stop_cond = {}) + : PackStrategyNFP{opt::Optimizer{}, std::move(k), + std::move(execpolicy), accur, std::move(stop_cond)} + { + // Defaults for AlgNLoptSubplex + auto iters = static_cast(std::floor(1000 * accuracy)); + auto optparams = + opt::StopCriteria{}.max_iterations(iters).rel_score_diff( + 1e-20) /*.abs_score_diff(1e-20)*/; + + solver.set_criteria(optparams); + } +}; + +template +struct PackStrategyTag_> +{ + using Tag = NFPPackingTag; +}; + + +template +double pick_best_spot_on_nfp_verts_only(ArrItem &item, + const ExPolygons &nfp, + const Bed &bed, + const PStrategy &strategy) +{ + using KernelT = KernelTraits; + + auto score = -std::numeric_limits::infinity(); + Vec2crd orig_tr = get_translation(item); + Vec2crd translation{0, 0}; + + auto eval_fitness = [&score, &strategy, &item, &translation, + &orig_tr](const Vec2crd &p) { + set_translation(item, orig_tr); + Vec2crd ref_v = reference_vertex(item); + Vec2crd tr = p - ref_v; + double fitness = KernelT::placement_fitness(strategy.kernel, item, tr); + if (fitness > score) { + score = fitness; + translation = tr; + } + }; + + for (const ExPolygon &expoly : nfp) { + for (const Point &p : expoly.contour) { + eval_fitness(p); + } + + for (const Polygon &h : expoly.holes) + for (const Point &p : h.points) + eval_fitness(p); + } + + set_translation(item, orig_tr + translation); + + return score; +} + +struct CornerResult +{ + size_t contour_id; + opt::Result<1> oresult; +}; + +template +double pick_best_spot_on_nfp(ArrItem &item, + const ExPolygons &nfp, + const Bed &bed, + const PackStrategyNFP &strategy) +{ + auto &ex_policy = strategy.ep; + using KernelT = KernelTraits; + + auto score = -std::numeric_limits::infinity(); + Vec2crd orig_tr = get_translation(item); + Vec2crd translation{0, 0}; + Vec2crd ref_v = reference_vertex(item); + + auto edge_caches = reserve_vector(nfp.size()); + auto sample_sets = reserve_vector>( + nfp.size()); + + for (const ExPolygon &expoly : nfp) { + edge_caches.emplace_back(EdgeCache{&expoly}); + edge_caches.back().sample_contour(strategy.accuracy, + sample_sets.emplace_back()); + } + + auto nthreads = execution::max_concurrency(ex_policy); + + std::vector gresults(edge_caches.size()); + + auto resultcmp = [](auto &a, auto &b) { + return a.oresult.score < b.oresult.score; + }; + + execution::for_each( + ex_policy, size_t(0), edge_caches.size(), + [&](size_t edge_cache_idx) { + auto &ec_contour = edge_caches[edge_cache_idx]; + auto &corners = sample_sets[edge_cache_idx]; + std::vector results(corners.size()); + + auto cornerfn = [&](size_t i) { + ContourLocation cr = corners[i]; + auto objfn = [&](opt::Input<1> &in) { + Vec2crd p = ec_contour.coords(ContourLocation{cr.contour_id, in[0]}); + Vec2crd tr = p - ref_v; + + return KernelT::placement_fitness(strategy.kernel, item, tr); + }; + + // Assuming that solver is a lightweight object + auto solver = strategy.solver; + solver.to_max(); + auto oresult = solver.optimize(objfn, + opt::initvals({cr.dist}), + opt::bounds({{0., 1.}})); + + results[i] = CornerResult{cr.contour_id, oresult}; + }; + + execution::for_each(ex_policy, size_t(0), results.size(), + cornerfn, nthreads); + + auto it = std::max_element(results.begin(), results.end(), + resultcmp); + + if (it != results.end()) + gresults[edge_cache_idx] = *it; + }, + nthreads); + + auto it = std::max_element(gresults.begin(), gresults.end(), resultcmp); + if (it != gresults.end()) { + score = it->oresult.score; + size_t path_id = std::distance(gresults.begin(), it); + size_t contour_id = it->contour_id; + double dist = it->oresult.optimum[0]; + + Vec2crd pos = edge_caches[path_id].coords(ContourLocation{contour_id, dist}); + Vec2crd tr = pos - ref_v; + + set_translation(item, orig_tr + tr); + } + + return score; +} + +template +bool pack(Strategy &strategy, + const Bed &bed, + ArrItem &item, + const PackStrategyContext &packing_context, + const Range &remaining_items, + const NFPPackingTag &) +{ + using KernelT = KernelTraits; + + bool packed = KernelT::on_start_packing(strategy.kernel, item, bed, + packing_context, remaining_items); + + double orig_rot = get_rotation(item); + double final_rot = 0.; + double final_score = -std::numeric_limits::infinity(); + Vec2crd orig_tr = get_translation(item); + Vec2crd final_tr = orig_tr; + + bool cancelled = false; + const auto & rotations = allowed_rotations(item); + + for (auto rot_it = rotations.begin(); + !cancelled && !packed && rot_it != rotations.end(); ++rot_it) { + + double rot = *rot_it; + + set_rotation(item, orig_rot + rot); + set_translation(item, orig_tr); + + auto nfp = calculate_nfp(item, packing_context, bed, + strategy.stop_condition); + double score = NaNd; + if (!nfp.empty()) { + score = pick_best_spot_on_nfp(item, nfp, bed, strategy); + + cancelled = std::isnan(score) || strategy.stop_condition(); + if (score > final_score) { + final_score = score; + final_rot = rot; + final_tr = get_translation(item); + } + } else { + cancelled = true; + } + } + + packed = !cancelled; + + if (packed) { + set_translation(item, final_tr); + set_rotation(item, orig_rot + final_rot); + packed = KernelT::on_item_packed(strategy.kernel, item); + } + + return packed; +} + +}} // namespace Slic3r::arr2 + +#endif // PACKSTRATEGYNFP_HPP diff --git a/src/libslic3r/Arrange/Core/NFP/RectangleOverfitPackingStrategy.hpp b/src/libslic3r/Arrange/Core/NFP/RectangleOverfitPackingStrategy.hpp new file mode 100644 index 0000000000..eb41094df6 --- /dev/null +++ b/src/libslic3r/Arrange/Core/NFP/RectangleOverfitPackingStrategy.hpp @@ -0,0 +1,122 @@ +#ifndef RECTANGLEOVERFITPACKINGSTRATEGY_HPP +#define RECTANGLEOVERFITPACKINGSTRATEGY_HPP + +#include "Kernels/RectangleOverfitKernelWrapper.hpp" + +#include "libslic3r/Arrange/Core/NFP/PackStrategyNFP.hpp" +#include "libslic3r/Arrange/Core/Beds.hpp" + +namespace Slic3r { namespace arr2 { + +using PostAlignmentFn = std::function; + +struct CenterAlignmentFn { + Vec2crd operator() (const BoundingBox &bedbb, + const BoundingBox &pilebb) + { + return bedbb.center() - pilebb.center(); + } +}; + +// With rectange bed, and no fixed items, an infinite bed with +// RectangleOverfitKernelWrapper can produce better results than a pure +// RectangleBed with inner-fit polygon calculation. +template +struct RectangleOverfitPackingStrategy { + PackStrategyNFP base_strategy; + + PostAlignmentFn post_alignment_fn = CenterAlignmentFn{}; + + template + struct Context: public DefaultPackingContext { + BoundingBox limits; + int bed_index; + PostAlignmentFn post_alignment_fn; + + explicit Context(const BoundingBox limits, + int bedidx, + PostAlignmentFn alignfn = CenterAlignmentFn{}) + : limits{limits}, bed_index{bedidx}, post_alignment_fn{alignfn} + {} + + ~Context() + { + // Here, the post alignment can be safely done. No throwing + // functions are called! + if (fixed_items_range(*this).empty()) { + auto itms = packed_items_range(*this); + auto pilebb = bounding_box(itms); + + for (auto &itm : itms) { + translate(itm, post_alignment_fn(limits, pilebb)); + } + } + } + }; + + RectangleOverfitPackingStrategy(PackStrategyNFP s, + PostAlignmentFn post_align_fn) + : base_strategy{std::move(s)}, post_alignment_fn{post_align_fn} + {} + + RectangleOverfitPackingStrategy(PackStrategyNFP s) + : base_strategy{std::move(s)} + {} +}; + +struct RectangleOverfitPackingStrategyTag {}; + +template +struct PackStrategyTag_> { + using Tag = RectangleOverfitPackingStrategyTag; +}; + +template +struct PackStrategyTraits_> { + template + using Context = typename RectangleOverfitPackingStrategy< + Args...>::template Context>; + + template + static Context create_context( + RectangleOverfitPackingStrategy &ps, + const Bed &bed, + int bed_index) + { + return Context{bounding_box(bed), bed_index, + ps.post_alignment_fn}; + } +}; + +template +bool pack(Strategy &strategy, + const Bed &bed, + ArrItem &item, + const PackStrategyContext &packing_context, + const Range &remaining_items, + const RectangleOverfitPackingStrategyTag &) +{ + bool ret = false; + + if (fixed_items_range(packing_context).empty()) { + auto &base = strategy.base_strategy; + PackStrategyNFP modded_strategy{ + base.solver, + RectangleOverfitKernelWrapper{base.kernel, packing_context.limits}, + base.ep, base.accuracy}; + + ret = pack(modded_strategy, + InfiniteBed{packing_context.limits.center()}, item, + packing_context, remaining_items, NFPPackingTag{}); + } else { + ret = pack(strategy.base_strategy, bed, item, packing_context, + remaining_items, NFPPackingTag{}); + } + + return ret; +} + +}} // namespace Slic3r::arr2 + +#endif // RECTANGLEOVERFITPACKINGSTRATEGY_HPP diff --git a/src/libslic3r/Arrange/Core/PackingContext.hpp b/src/libslic3r/Arrange/Core/PackingContext.hpp new file mode 100644 index 0000000000..77aa87e5c6 --- /dev/null +++ b/src/libslic3r/Arrange/Core/PackingContext.hpp @@ -0,0 +1,124 @@ +#ifndef PACKINGCONTEXT_HPP +#define PACKINGCONTEXT_HPP + +#include "ArrangeItemTraits.hpp" + +namespace Slic3r { namespace arr2 { + +template +struct PackingContextTraits_ { + template + static void add_fixed_item(Ctx &ctx, const ArrItem &itm) + { + ctx.add_fixed_item(itm); + } + + template + static void add_packed_item(Ctx &ctx, ArrItem &itm) + { + ctx.add_packed_item(itm); + } + + // returns a range of all packed items in the context ctx + static auto all_items_range(const Ctx &ctx) + { + return ctx.all_items_range(); + } + + static auto fixed_items_range(const Ctx &ctx) + { + return ctx.fixed_items_range(); + } + + static auto packed_items_range(const Ctx &ctx) + { + return ctx.packed_items_range(); + } + + static auto packed_items_range(Ctx &ctx) + { + return ctx.packed_items_range(); + } +}; + +template +void add_fixed_item(Ctx &ctx, const ArrItem &itm) +{ + PackingContextTraits_>::add_fixed_item(ctx, itm); +} + +template +void add_packed_item(Ctx &ctx, ArrItem &itm) +{ + PackingContextTraits_>::add_packed_item(ctx, itm); +} + +template +auto all_items_range(const Ctx &ctx) +{ + return PackingContextTraits_>::all_items_range(ctx); +} + +template +auto fixed_items_range(const Ctx &ctx) +{ + return PackingContextTraits_>::fixed_items_range(ctx); +} + +template +auto packed_items_range(Ctx &&ctx) +{ + return PackingContextTraits_>::packed_items_range(ctx); +} + +template +class DefaultPackingContext { + using ArrItemRaw = StripCVRef; + std::vector> m_fixed; + std::vector> m_packed; + std::vector> m_items; + +public: + DefaultPackingContext() = default; + + template + explicit DefaultPackingContext(const Range &fixed_items) + { + std::copy(fixed_items.begin(), fixed_items.end(), std::back_inserter(m_fixed)); + std::copy(fixed_items.begin(), fixed_items.end(), std::back_inserter(m_items)); + } + + auto all_items_range() const noexcept { return crange(m_items); } + auto fixed_items_range() const noexcept { return crange(m_fixed); } + auto packed_items_range() const noexcept { return crange(m_packed); } + auto packed_items_range() noexcept { return range(m_packed); } + + void add_fixed_item(const ArrItem &itm) + { + m_fixed.emplace_back(itm); + m_items.emplace_back(itm); + } + + void add_packed_item(ArrItem &itm) + { + m_packed.emplace_back(itm); + m_items.emplace_back(itm); + } +}; + +template +auto default_context(const Range &items) +{ + using ArrItem = StripCVRef::value_type>; + return DefaultPackingContext{items}; +} + +template +auto default_context(const Cont &container) +{ + return DefaultPackingContext{crange(container)}; +} + +}} // namespace Slic3r::arr2 + +#endif // PACKINGCONTEXT_HPP diff --git a/src/libslic3r/Arrange/Items/ArbitraryDataStore.hpp b/src/libslic3r/Arrange/Items/ArbitraryDataStore.hpp new file mode 100644 index 0000000000..69cfe8bb58 --- /dev/null +++ b/src/libslic3r/Arrange/Items/ArbitraryDataStore.hpp @@ -0,0 +1,91 @@ +#ifndef ARBITRARYDATASTORE_HPP +#define ARBITRARYDATASTORE_HPP + +#include +#include +#include + +#include "libslic3r/Arrange/Core/DataStoreTraits.hpp" + +namespace Slic3r { namespace arr2 { + +// An associative container able to store and retrieve any data type. +// Based on std::any +class ArbitraryDataStore { + std::map m_data; + +public: + template void add(const std::string &key, T &&data) + { + m_data[key] = std::any{std::forward(data)}; + } + + void add(const std::string &key, std::any &&data) + { + m_data[key] = std::move(data); + } + + // Return nullptr if the key does not exist or the stored data has a + // type other then T. Otherwise returns a pointer to the stored data. + template const T *get(const std::string &key) const + { + auto it = m_data.find(key); + return it != m_data.end() ? std::any_cast(&(it->second)) : + nullptr; + } + + // Same as above just not const. + template T *get(const std::string &key) + { + auto it = m_data.find(key); + return it != m_data.end() ? std::any_cast(&(it->second)) : nullptr; + } + + bool has_key(const std::string &key) const + { + auto it = m_data.find(key); + return it != m_data.end(); + } +}; + +// Some items can be containers of arbitrary data stored under string keys. +template<> struct DataStoreTraits_ +{ + static constexpr bool Implemented = true; + + template + static const T *get(const ArbitraryDataStore &s, const std::string &key) + { + return s.get(key); + } + + // Same as above just not const. + template + static T *get(ArbitraryDataStore &s, const std::string &key) + { + return s.get(key); + } + + template + static bool has_key(ArbitraryDataStore &s, const std::string &key) + { + return s.has_key(key); + } +}; + +template<> struct WritableDataStoreTraits_ +{ + static constexpr bool Implemented = true; + + template + static void set(ArbitraryDataStore &store, + const std::string &key, + T &&data) + { + store.add(key, std::forward(data)); + } +}; + +}} // namespace Slic3r::arr2 + +#endif // ARBITRARYDATASTORE_HPP diff --git a/src/libslic3r/Arrange/Items/ArrangeItem.cpp b/src/libslic3r/Arrange/Items/ArrangeItem.cpp new file mode 100644 index 0000000000..ff8c761147 --- /dev/null +++ b/src/libslic3r/Arrange/Items/ArrangeItem.cpp @@ -0,0 +1,184 @@ +#include "ArrangeItem.hpp" + +#include "libslic3r/Arrange/Core/NFP/NFPConcave_Tesselate.hpp" + +#include "libslic3r/Arrange/ArrangeImpl.hpp" +#include "libslic3r/Arrange/Tasks/ArrangeTaskImpl.hpp" +#include "libslic3r/Arrange/Tasks/FillBedTaskImpl.hpp" +#include "libslic3r/Arrange/Tasks/MultiplySelectionTaskImpl.hpp" + +#include "libslic3r/Geometry/ConvexHull.hpp" + +namespace Slic3r { namespace arr2 { + +const Polygons &DecomposedShape::transformed_outline() const +{ + if (!m_transformed_outline_valid) { + m_transformed_outline = contours(); + for (Polygon &poly : m_transformed_outline) { + poly.rotate(rotation()); + poly.translate(translation()); + } + + auto sc = scaled(1.) * scaled(1.); + m_area = std::accumulate(m_transformed_outline.begin(), + m_transformed_outline.end(), 0., + [sc](double s, const auto &p) { + return s + p.area() / sc; + }); + + m_convex_hull = Geometry::convex_hull(m_transformed_outline); + m_bounding_box = get_extents(m_convex_hull); + + m_transformed_outline_valid = true; + } + + return m_transformed_outline; +} + +const Polygon &DecomposedShape::convex_hull() const +{ + if (!m_transformed_outline_valid) + transformed_outline(); + + return m_convex_hull; +} + +const BoundingBox &DecomposedShape::bounding_box() const +{ + if (!m_transformed_outline_valid) + transformed_outline(); + + return m_bounding_box; +} + +const Vec2crd &DecomposedShape::reference_vertex() const +{ + if (!m_reference_vertex_valid) { + m_reference_vertex = Slic3r::reference_vertex(transformed_outline()); + m_refs.clear(); + m_mins.clear(); + m_refs.reserve(m_transformed_outline.size()); + m_mins.reserve(m_transformed_outline.size()); + for (auto &poly : m_transformed_outline) { + m_refs.emplace_back(Slic3r::reference_vertex(poly)); + m_mins.emplace_back(Slic3r::min_vertex(poly)); + } + m_reference_vertex_valid = true; + } + + return m_reference_vertex; +} + +const Vec2crd &DecomposedShape::reference_vertex(size_t i) const +{ + if (!m_reference_vertex_valid) { + reference_vertex(); + } + + return m_refs[i]; +} + +const Vec2crd &DecomposedShape::min_vertex(size_t idx) const +{ + if (!m_reference_vertex_valid) { + reference_vertex(); + } + + return m_mins[idx]; +} + +DecomposedShape decompose(const ExPolygons &shape) +{ + ExPolygons shape_s = expolygons_simplify(shape, scaled(.2)); + return DecomposedShape{convex_decomposition_tess(shape_s)}; +} + +DecomposedShape decompose(const Polygon &shape) +{ + Polygons convex_shapes; + + bool is_convex = polygon_is_convex(shape); + if (is_convex) { + convex_shapes.emplace_back(shape); + } else { + Polygon shape_s = shape; + shape_s.simplify(scaled(.2)); + convex_shapes = convex_decomposition_tess(shape_s); + } + + return DecomposedShape{std::move(convex_shapes)}; +} + +ArrangeItem::ArrangeItem(const ExPolygons &shape) + : m_shape{decompose(shape)}, m_envelope{&m_shape} +{} + +ArrangeItem::ArrangeItem(Polygon shape) + : m_shape{decompose(shape)}, m_envelope{&m_shape} +{} + +ArrangeItem::ArrangeItem(const ArrangeItem &other) +{ + this->operator= (other); +} + +ArrangeItem::ArrangeItem(ArrangeItem &&other) noexcept +{ + this->operator=(std::move(other)); +} + +ArrangeItem &ArrangeItem::operator=(const ArrangeItem &other) +{ + m_shape = other.m_shape; + m_datastore = other.m_datastore; + m_bed_idx = other.m_bed_idx; + m_priority = other.m_priority; + + if (other.m_envelope.get() == &other.m_shape) + m_envelope = &m_shape; + else + m_envelope = std::make_unique(other.envelope()); + + return *this; +} + +void ArrangeItem::set_shape(DecomposedShape shape) +{ + m_shape = std::move(shape); + m_envelope = &m_shape; +} + +void ArrangeItem::set_envelope(DecomposedShape envelope) +{ + m_envelope = std::make_unique(std::move(envelope)); + + // Initial synch of transformations of envelope and shape. + // They need to be in synch all the time + m_envelope->translation(m_shape.translation()); + m_envelope->rotation(m_shape.rotation()); +} + +ArrangeItem &ArrangeItem::operator=(ArrangeItem &&other) noexcept +{ + m_shape = std::move(other.m_shape); + m_datastore = std::move(other.m_datastore); + m_bed_idx = other.m_bed_idx; + m_priority = other.m_priority; + + if (other.m_envelope.get() == &other.m_shape) + m_envelope = &m_shape; + else + m_envelope = std::move(other.m_envelope); + + return *this; +} + +template struct ImbueableItemTraits_; +template class ArrangeableToItemConverter; +template struct ArrangeTask; +template struct FillBedTask; +template struct MultiplySelectionTask; +template class Arranger; + +}} // namespace Slic3r::arr2 diff --git a/src/libslic3r/Arrange/Items/ArrangeItem.hpp b/src/libslic3r/Arrange/Items/ArrangeItem.hpp new file mode 100644 index 0000000000..1fb276a6be --- /dev/null +++ b/src/libslic3r/Arrange/Items/ArrangeItem.hpp @@ -0,0 +1,461 @@ +#ifndef ARRANGEITEM_HPP +#define ARRANGEITEM_HPP + +#include +#include + +#include "libslic3r/ExPolygon.hpp" +#include "libslic3r/BoundingBox.hpp" +#include "libslic3r/AnyPtr.hpp" + +#include "libslic3r/Arrange/Core/PackingContext.hpp" +#include "libslic3r/Arrange/Core/NFP/NFPArrangeItemTraits.hpp" +#include "libslic3r/Arrange/Core/NFP/NFP.hpp" + +#include "libslic3r/Arrange/Items/WritableItemTraits.hpp" + +#include "libslic3r/Arrange/Arrange.hpp" +#include "libslic3r/Arrange/Tasks/ArrangeTask.hpp" +#include "libslic3r/Arrange/Tasks/FillBedTask.hpp" +#include "libslic3r/Arrange/Tasks/MultiplySelectionTask.hpp" + +#include "libslic3r/Arrange/Items/ArbitraryDataStore.hpp" + +#include + +namespace Slic3r { namespace arr2 { + +inline bool check_polygons_are_convex(const Polygons &pp) { + return std::all_of(pp.begin(), pp.end(), [](const Polygon &p) { + return polygon_is_convex(p); + }); +} + +// A class that stores a set of polygons that are garanteed to be all convex. +// They collectively represent a decomposition of a more complex shape into +// its convex part. Note that this class only stores the result of the decomp, +// does not do the job itself. In debug mode, an explicit check is done for +// each component to be convex. +// +// Additionally class stores a translation vector and a rotation angle for the +// stored polygon, plus additional privitives that are all cached cached after +// appying a the transformations. The caching is not thread safe! +class DecomposedShape +{ + Polygons m_shape; + + Vec2crd m_translation{0, 0}; // The translation of the poly + double m_rotation{0.0}; // The rotation of the poly in radians + + mutable Polygons m_transformed_outline; + mutable bool m_transformed_outline_valid = false; + + mutable Point m_reference_vertex; + mutable std::vector m_refs; + mutable std::vector m_mins; + mutable bool m_reference_vertex_valid = false; + + mutable Polygon m_convex_hull; + mutable BoundingBox m_bounding_box; + mutable double m_area = 0; + +public: + DecomposedShape() = default; + + explicit DecomposedShape(Polygon sh) + { + m_shape.emplace_back(std::move(sh)); + assert(check_polygons_are_convex(m_shape)); + } + + explicit DecomposedShape(std::initializer_list pts) + : DecomposedShape(Polygon{pts}) + {} + + explicit DecomposedShape(Polygons sh) : m_shape{std::move(sh)} + { + assert(check_polygons_are_convex(m_shape)); + } + + const Polygons &contours() const { return m_shape; } + + const Vec2crd &translation() const { return m_translation; } + double rotation() const { return m_rotation; } + + void translation(const Vec2crd &v) + { + m_translation = v; + m_transformed_outline_valid = false; + m_reference_vertex_valid = false; + } + + void rotation(double v) + { + m_rotation = v; + m_transformed_outline_valid = false; + m_reference_vertex_valid = false; + } + + const Polygons &transformed_outline() const; + const Polygon &convex_hull() const; + const BoundingBox &bounding_box() const; + + // The cached reference vertex in the context of NFP creation. Always + // refers to the leftmost upper vertex. + const Vec2crd &reference_vertex() const; + const Vec2crd &reference_vertex(size_t idx) const; + + // Also for NFP calculations, the rightmost lowest vertex of the shape. + const Vec2crd &min_vertex(size_t idx) const; + + double area_unscaled() const + { + // update cache + transformed_outline(); + + return m_area; + } +}; + +DecomposedShape decompose(const ExPolygons &polys); +DecomposedShape decompose(const Polygon &p); + +class ArrangeItem +{ +private: + DecomposedShape m_shape; // Shape of item when it's not moving + AnyPtr m_envelope; // Possibly different shape when packed + + ArbitraryDataStore m_datastore; + + int m_bed_idx{Unarranged}; // To which logical bed does this item belong + int m_priority{0}; // For sorting + +public: + ArrangeItem() = default; + + explicit ArrangeItem(DecomposedShape shape) + : m_shape(std::move(shape)), m_envelope{&m_shape} + {} + + explicit ArrangeItem(DecomposedShape shape, DecomposedShape envelope) + : m_shape(std::move(shape)) + , m_envelope{std::make_unique(std::move(envelope))} + {} + + explicit ArrangeItem(const ExPolygons &shape); + explicit ArrangeItem(Polygon shape); + explicit ArrangeItem(std::initializer_list pts) + : ArrangeItem(Polygon{pts}) + {} + + ArrangeItem(const ArrangeItem &); + ArrangeItem(ArrangeItem &&) noexcept; + ArrangeItem & operator=(const ArrangeItem &); + ArrangeItem & operator=(ArrangeItem &&) noexcept; + + int bed_idx() const { return m_bed_idx; } + int priority() const { return m_priority; } + + void bed_idx(int v) { m_bed_idx = v; } + void priority(int v) { m_priority = v; } + + const ArbitraryDataStore &datastore() const { return m_datastore; } + ArbitraryDataStore &datastore() { return m_datastore; } + + const DecomposedShape & shape() const { return m_shape; } + void set_shape(DecomposedShape shape); + + const DecomposedShape & envelope() const { return *m_envelope; } + void set_envelope(DecomposedShape envelope); + + const Vec2crd &translation() const { return m_shape.translation(); } + double rotation() const { return m_shape.rotation(); } + + void translation(const Vec2crd &v) + { + m_shape.translation(v); + m_envelope->translation(v); + } + + void rotation(double v) + { + m_shape.rotation(v); + m_envelope->rotation(v); + } + + void update_caches() const + { + m_shape.reference_vertex(); + m_envelope->reference_vertex(); + } +}; + +template<> struct ArrangeItemTraits_ +{ + static const Vec2crd &get_translation(const ArrangeItem &itm) + { + return itm.translation(); + } + + static double get_rotation(const ArrangeItem &itm) + { + return itm.rotation(); + } + + static int get_bed_index(const ArrangeItem &itm) + { + return itm.bed_idx(); + } + + static int get_priority(const ArrangeItem &itm) + { + return itm.priority(); + } + + // Setters: + + static void set_translation(ArrangeItem &itm, const Vec2crd &v) + { + itm.translation(v); + } + + static void set_rotation(ArrangeItem &itm, double v) + { + itm.rotation(v); + } + + static void set_bed_index(ArrangeItem &itm, int v) + { + itm.bed_idx(v); + } +}; + +// Some items can be containers of arbitrary data stored under string keys. +template<> struct DataStoreTraits_ +{ + static constexpr bool Implemented = true; + + template + static const T *get(const ArrangeItem &itm, const std::string &key) + { + return itm.datastore().get(key); + } + + // Same as above just not const. + template + static T *get(ArrangeItem &itm, const std::string &key) + { + return itm.datastore().get(key); + } + + static bool has_key(const ArrangeItem &itm, const std::string &key) + { + return itm.datastore().has_key(key); + } +}; + +template<> struct WritableDataStoreTraits_ +{ + static constexpr bool Implemented = true; + + template + static void set(ArrangeItem &itm, + const std::string &key, + T &&data) + { + itm.datastore().add(key, std::forward(data)); + } +}; + +template +static Polygons calculate_nfp_unnormalized(const ArrangeItem &item, + const Range &fixed_items, + StopCond &&stop_cond = {}) +{ + size_t cap = 0; + + for (const ArrangeItem &fixitem : fixed_items) { + const Polygons &outlines = fixitem.shape().transformed_outline(); + cap += outlines.size(); + } + + const Polygons &item_outlines = item.envelope().transformed_outline(); + + auto nfps = reserve_polygons(cap * item_outlines.size()); + + Vec2crd ref_whole = item.envelope().reference_vertex(); + Polygon subnfp; + + for (const ArrangeItem &fixed : fixed_items) { + // fixed_polys should already be a set of strictly convex polygons, + // as ArrangeItem stores convex-decomposed polygons + const Polygons & fixed_polys = fixed.shape().transformed_outline(); + + for (const Polygon &fixed_poly : fixed_polys) { + Point max_fixed = Slic3r::reference_vertex(fixed_poly); + for (size_t mi = 0; mi < item_outlines.size(); ++mi) { + const Polygon &movable = item_outlines[mi]; + const Vec2crd &mref = item.envelope().reference_vertex(mi); + subnfp = nfp_convex_convex_legacy(fixed_poly, movable); + + Vec2crd min_movable = item.envelope().min_vertex(mi); + + Vec2crd dtouch = max_fixed - min_movable; + Vec2crd top_other = mref + dtouch; + Vec2crd max_nfp = Slic3r::reference_vertex(subnfp); + auto dnfp = top_other - max_nfp; + + auto d = ref_whole - mref + dnfp; + subnfp.translate(d); + nfps.emplace_back(subnfp); + } + + if (stop_cond()) + break; + + nfps = union_(nfps); + } + + if (stop_cond()) { + nfps.clear(); + break; + } + } + + return nfps; +} + +template<> struct NFPArrangeItemTraits_ { + template + static ExPolygons calculate_nfp(const ArrangeItem &item, + const Context &packing_context, + const Bed &bed, + StopCond &&stopcond) + { + auto static_items = all_items_range(packing_context); + Polygons nfps = arr2::calculate_nfp_unnormalized(item, static_items, stopcond); + + ExPolygons nfp_ex; + + if (!stopcond()) { + if constexpr (!std::is_convertible_v) { + ExPolygons ifpbed = ifp_convex(bed, item.envelope().convex_hull()); + nfp_ex = diff_ex(ifpbed, nfps); + } else { + nfp_ex = union_ex(nfps); + } + } + + item.update_caches(); + + return nfp_ex; + } + + static const Vec2crd& reference_vertex(const ArrangeItem &item) + { + return item.envelope().reference_vertex(); + } + + static BoundingBox envelope_bounding_box(const ArrangeItem &itm) + { + return itm.envelope().bounding_box(); + } + + static BoundingBox fixed_bounding_box(const ArrangeItem &itm) + { + return itm.shape().bounding_box(); + } + + static double envelope_area(const ArrangeItem &itm) + { + return itm.envelope().area_unscaled() * scaled(1.) * + scaled(1.); + } + + static double fixed_area(const ArrangeItem &itm) + { + return itm.shape().area_unscaled() * scaled(1.) * + scaled(1.); + } + + static const Polygons & envelope_outline(const ArrangeItem &itm) + { + return itm.envelope().transformed_outline(); + } + + static const Polygons & fixed_outline(const ArrangeItem &itm) + { + return itm.shape().transformed_outline(); + } + + static const Polygon & envelope_convex_hull(const ArrangeItem &itm) + { + return itm.envelope().convex_hull(); + } + + static const Polygon & fixed_convex_hull(const ArrangeItem &itm) + { + return itm.shape().convex_hull(); + } + + static const std::vector& allowed_rotations(const ArrangeItem &itm) + { + static const std::vector ret_zero = {0.}; + + const std::vector * ret_ptr = &ret_zero; + + auto rots = get_data>(itm, "rotations"); + if (rots) { + ret_ptr = rots; + } + + return *ret_ptr; + } +}; + +template<> struct IsWritableItem_: public std::true_type {}; + +template<> +struct WritableItemTraits_ { + + static void set_priority(ArrangeItem &itm, int p) { itm.priority(p); } + static void set_convex_shape(ArrangeItem &itm, const Polygon &shape) + { + itm.set_shape(DecomposedShape{shape}); + } + static void set_shape(ArrangeItem &itm, const ExPolygons &shape) + { + itm.set_shape(decompose(shape)); + } + static void set_convex_envelope(ArrangeItem &itm, const Polygon &envelope) + { + itm.set_envelope(DecomposedShape{envelope}); + } + static void set_envelope(ArrangeItem &itm, const ExPolygons &envelope) + { + itm.set_envelope(decompose(envelope)); + } + + template + static void set_arbitrary_data(ArrangeItem &itm, const std::string &key, T &&data) + { + set_data(itm, key, std::forward(data)); + } + + static void set_allowed_rotations(ArrangeItem &itm, const std::vector &rotations) + { + set_data(itm, "rotations", rotations); + } +}; + +extern template struct ImbueableItemTraits_; +extern template class ArrangeableToItemConverter; +extern template struct ArrangeTask; +extern template struct FillBedTask; +extern template struct MultiplySelectionTask; +extern template class Arranger; + +}} // namespace Slic3r::arr2 + +#endif // ARRANGEITEM_HPP diff --git a/src/libslic3r/Arrange/Items/SimpleArrangeItem.cpp b/src/libslic3r/Arrange/Items/SimpleArrangeItem.cpp new file mode 100644 index 0000000000..75ab1cf423 --- /dev/null +++ b/src/libslic3r/Arrange/Items/SimpleArrangeItem.cpp @@ -0,0 +1,15 @@ +#include "SimpleArrangeItem.hpp" +#include "libslic3r/Arrange/ArrangeImpl.hpp" + +namespace Slic3r { namespace arr2 { + +Polygon SimpleArrangeItem::outline() const +{ + Polygon ret = shape(); + ret.rotate(m_rotation); + ret.translate(m_translation); + + return ret; +} + +}} // namespace Slic3r::arr2 diff --git a/src/libslic3r/Arrange/Items/SimpleArrangeItem.hpp b/src/libslic3r/Arrange/Items/SimpleArrangeItem.hpp new file mode 100644 index 0000000000..4f321c93b0 --- /dev/null +++ b/src/libslic3r/Arrange/Items/SimpleArrangeItem.hpp @@ -0,0 +1,178 @@ +#ifndef SIMPLEARRANGEITEM_HPP +#define SIMPLEARRANGEITEM_HPP + +#include "libslic3r/Arrange/Core/PackingContext.hpp" + +#include "libslic3r/Arrange/Core/NFP/NFPArrangeItemTraits.hpp" +#include "libslic3r/Arrange/Core/NFP/NFP.hpp" + +#include "libslic3r/Arrange/Arrange.hpp" + +#include "libslic3r/Polygon.hpp" +#include "libslic3r/Geometry/ConvexHull.hpp" + +#include "WritableItemTraits.hpp" + +namespace Slic3r { namespace arr2 { + +class SimpleArrangeItem { + Polygon m_shape; + + Vec2crd m_translation = Vec2crd::Zero(); + double m_rotation = 0.; + int m_priority = 0; + int m_bed_idx = Unarranged; + + std::vector m_allowed_rotations = {0.}; + +public: + explicit SimpleArrangeItem(Polygon chull = {}): m_shape{std::move(chull)} {} + + void set_shape(Polygon chull) { m_shape = std::move(chull); } + + const Vec2crd& get_translation() const noexcept { return m_translation; } + double get_rotation() const noexcept { return m_rotation; } + int get_priority() const noexcept { return m_priority; } + int get_bed_index() const noexcept { return m_bed_idx; } + + void set_translation(const Vec2crd &v) { m_translation = v; } + void set_rotation(double v) noexcept { m_rotation = v; } + void set_priority(int v) noexcept { m_priority = v; } + void set_bed_index(int v) noexcept { m_bed_idx = v; } + + const Polygon &shape() const { return m_shape; } + Polygon outline() const; + + const auto &allowed_rotations() const noexcept + { + return m_allowed_rotations; + } + + void set_allowed_rotations(std::vector rots) + { + m_allowed_rotations = std::move(rots); + } +}; + +template<> struct NFPArrangeItemTraits_ +{ + template + static ExPolygons calculate_nfp(const SimpleArrangeItem &item, + const Context &packing_context, + const Bed &bed, + StopCond &&stop_cond) + { + auto fixed_items = all_items_range(packing_context); + auto nfps = reserve_polygons(fixed_items.size()); + for (const SimpleArrangeItem &fixed_part : fixed_items) { + Polygon subnfp = nfp_convex_convex_legacy(fixed_part.outline(), + item.outline()); + nfps.emplace_back(subnfp); + + + if (stop_cond()) { + nfps.clear(); + break; + } + } + + ExPolygons nfp_ex; + if (!stop_cond()) { + if constexpr (!std::is_convertible_v) { + ExPolygons ifpbed = ifp_convex(bed, item.outline()); + nfp_ex = diff_ex(ifpbed, nfps); + } else { + nfp_ex = union_ex(nfps); + } + } + + return nfp_ex; + } + + static Vec2crd reference_vertex(const SimpleArrangeItem &item) + { + return Slic3r::reference_vertex(item.outline()); + } + + static BoundingBox envelope_bounding_box(const SimpleArrangeItem &itm) + { + return get_extents(itm.outline()); + } + + static BoundingBox fixed_bounding_box(const SimpleArrangeItem &itm) + { + return get_extents(itm.outline()); + } + + static Polygons envelope_outline(const SimpleArrangeItem &itm) + { + return {itm.outline()}; + } + + static Polygons fixed_outline(const SimpleArrangeItem &itm) + { + return {itm.outline()}; + } + + static Polygon envelope_convex_hull(const SimpleArrangeItem &itm) + { + return Geometry::convex_hull(itm.outline()); + } + + static Polygon fixed_convex_hull(const SimpleArrangeItem &itm) + { + return Geometry::convex_hull(itm.outline()); + } + + static double envelope_area(const SimpleArrangeItem &itm) + { + return itm.shape().area(); + } + + static double fixed_area(const SimpleArrangeItem &itm) + { + return itm.shape().area(); + } + + static const auto& allowed_rotations(const SimpleArrangeItem &itm) noexcept + { + return itm.allowed_rotations(); + } +}; + +template<> struct IsWritableItem_: public std::true_type {}; + +template<> +struct WritableItemTraits_ { + + static void set_priority(SimpleArrangeItem &itm, int p) { itm.set_priority(p); } + static void set_convex_shape(SimpleArrangeItem &itm, const Polygon &shape) + { + itm.set_shape(shape); + } + static void set_shape(SimpleArrangeItem &itm, const ExPolygons &shape) + { + itm.set_shape(Geometry::convex_hull(shape)); + } + static void set_convex_envelope(SimpleArrangeItem &itm, const Polygon &envelope) + { + itm.set_shape(envelope); + } + static void set_envelope(SimpleArrangeItem &itm, const ExPolygons &envelope) + { + itm.set_shape(Geometry::convex_hull(envelope)); + } + + template + static void set_data(SimpleArrangeItem &itm, const std::string &key, T &&data) + {} + + static void set_allowed_rotations(SimpleArrangeItem &itm, const std::vector &rotations) + { + itm.set_allowed_rotations(rotations); + } +}; + +}} // namespace Slic3r::arr2 + +#endif // SIMPLEARRANGEITEM_HPP diff --git a/src/libslic3r/Arrange/Items/TrafoOnlyArrangeItem.hpp b/src/libslic3r/Arrange/Items/TrafoOnlyArrangeItem.hpp new file mode 100644 index 0000000000..90b6651563 --- /dev/null +++ b/src/libslic3r/Arrange/Items/TrafoOnlyArrangeItem.hpp @@ -0,0 +1,79 @@ +#ifndef TRAFOONLYARRANGEITEM_HPP +#define TRAFOONLYARRANGEITEM_HPP + +#include "libslic3r/Arrange/Core/ArrangeItemTraits.hpp" + +#include "libslic3r/Arrange/Items/ArbitraryDataStore.hpp" +#include "libslic3r/Arrange/Items/WritableItemTraits.hpp" + +namespace Slic3r { namespace arr2 { + +class TrafoOnlyArrangeItem { + int m_bed_idx = Unarranged; + int m_priority = 0; + Vec2crd m_translation = Vec2crd::Zero(); + double m_rotation = 0.; + + ArbitraryDataStore m_datastore; + +public: + TrafoOnlyArrangeItem() = default; + + template + explicit TrafoOnlyArrangeItem(const ArrItm &other) + : m_bed_idx{arr2::get_bed_index(other)}, + m_priority{arr2::get_priority(other)}, + m_translation(arr2::get_translation(other)), + m_rotation{arr2::get_rotation(other)} + {} + + const Vec2crd& get_translation() const noexcept { return m_translation; } + double get_rotation() const noexcept { return m_rotation; } + int get_bed_index() const noexcept { return m_bed_idx; } + int get_priority() const noexcept { return m_priority; } + + const ArbitraryDataStore &datastore() const noexcept { return m_datastore; } + ArbitraryDataStore &datastore() { return m_datastore; } +}; + +template<> struct DataStoreTraits_ +{ + static constexpr bool Implemented = true; + + template + static const T *get(const TrafoOnlyArrangeItem &itm, const std::string &key) + { + return itm.datastore().get(key); + } + + template + static T *get(TrafoOnlyArrangeItem &itm, const std::string &key) + { + return itm.datastore().get(key); + } + + static bool has_key(const TrafoOnlyArrangeItem &itm, const std::string &key) + { + return itm.datastore().has_key(key); + } +}; + +template<> struct IsWritableItem_: public std::true_type {}; + +template<> struct WritableDataStoreTraits_ +{ + static constexpr bool Implemented = true; + + template + static void set(TrafoOnlyArrangeItem &itm, + const std::string &key, + T &&data) + { + set_data(itm.datastore(), key, std::forward(data)); + } +}; + +} // namespace arr2 +} // namespace Slic3r + +#endif // TRAFOONLYARRANGEITEM_HPP diff --git a/src/libslic3r/Arrange/Items/WritableItemTraits.hpp b/src/libslic3r/Arrange/Items/WritableItemTraits.hpp new file mode 100644 index 0000000000..04281faa52 --- /dev/null +++ b/src/libslic3r/Arrange/Items/WritableItemTraits.hpp @@ -0,0 +1,112 @@ +#ifndef WRITABLEITEMTRAITS_HPP +#define WRITABLEITEMTRAITS_HPP + +#include "libslic3r/Arrange/Core/ArrangeItemTraits.hpp" +#include "libslic3r/Arrange/Core/DataStoreTraits.hpp" + +#include "libslic3r/ExPolygon.hpp" + +namespace Slic3r { namespace arr2 { + +template struct IsWritableItem_ : public std::false_type +{}; + +// Using this interface to set up any arrange item. Provides default +// implementation but it needs to be explicitly switched on with +// IsWritableItem_ or completely reimplement a specialization. +template struct WritableItemTraits_ +{ + static_assert(IsWritableItem_::value, "Not a Writable item type!"); + + static void set_priority(Itm &itm, int p) { itm.set_priority(p); } + + static void set_convex_shape(Itm &itm, const Polygon &shape) + { + itm.set_convex_shape(shape); + } + + static void set_shape(Itm &itm, const ExPolygons &shape) + { + itm.set_shape(shape); + } + + static void set_convex_envelope(Itm &itm, const Polygon &envelope) + { + itm.set_convex_envelope(envelope); + } + + static void set_envelope(Itm &itm, const ExPolygons &envelope) + { + itm.set_envelope(envelope); + } + + template + static void set_arbitrary_data(Itm &itm, const std::string &key, T &&data) + { + if constexpr (IsWritableDataStore) + set_data(itm, key, std::forward(data)); + } + + static void set_allowed_rotations(Itm &itm, + const std::vector &rotations) + { + itm.set_allowed_rotations(rotations); + } +}; + +template +using WritableItemTraits = WritableItemTraits_>; + +template constexpr bool IsWritableItem = IsWritableItem_::value; +template +using WritableItemOnly = std::enable_if_t, TT>; + +template void set_priority(Itm &itm, int p) +{ + WritableItemTraits::set_priority(itm, p); +} + +template void set_convex_shape(Itm &itm, const Polygon &shape) +{ + WritableItemTraits::set_convex_shape(itm, shape); +} + +template void set_shape(Itm &itm, const ExPolygons &shape) +{ + WritableItemTraits::set_shape(itm, shape); +} + +template +void set_convex_envelope(Itm &itm, const Polygon &envelope) +{ + WritableItemTraits::set_convex_envelope(itm, envelope); +} + +template void set_envelope(Itm &itm, const ExPolygons &envelope) +{ + WritableItemTraits::set_envelope(itm, envelope); +} + +template +void set_arbitrary_data(Itm &itm, const std::string &key, T &&data) +{ + WritableItemTraits::set_arbitrary_data(itm, key, std::forward(data)); +} + +template +void set_allowed_rotations(Itm &itm, const std::vector &rotations) +{ + WritableItemTraits::set_arbitrary_data(itm, "rotations", rotations); +} + +template int raise_priority(ArrItem &itm) +{ + int ret = get_priority(itm) + 1; + set_priority(itm, ret); + + return ret; +} + +}} // namespace Slic3r::arr2 + +#endif // WRITABLEITEMTRAITS_HPP diff --git a/src/libslic3r/Arrange/Scene.cpp b/src/libslic3r/Arrange/Scene.cpp new file mode 100644 index 0000000000..a59b3f9aec --- /dev/null +++ b/src/libslic3r/Arrange/Scene.cpp @@ -0,0 +1,64 @@ +#include "Scene.hpp" + +#include "Items/ArrangeItem.hpp" + +#include "Tasks/ArrangeTask.hpp" +#include "Tasks/FillBedTask.hpp" + +namespace Slic3r { namespace arr2 { + +std::vector Scene::selected_ids() const +{ + auto items = reserve_vector(model().arrangeable_count()); + + model().for_each_arrangeable([ &items](auto &arrbl) mutable { + if (arrbl.is_selected()) + items.emplace_back(arrbl.id()); + }); + + return items; +} + +using DefaultArrangeItem = ArrangeItem; + +std::unique_ptr ArrangeTaskBase::create(Tasks task_type, const Scene &sc) +{ + std::unique_ptr ret; + switch(task_type) { + case Tasks::Arrange: + ret = ArrangeTask::create(sc); + break; + case Tasks::FillBed: + ret = FillBedTask::create(sc); + break; + default: + ; + } + + return ret; +} + +std::set selected_geometry_ids(const Scene &sc) +{ + std::set result; + + std::vector selected_ids = sc.selected_ids(); + for (const ObjectID &id : selected_ids) { + sc.model().visit_arrangeable(id, [&result](const Arrangeable &arrbl) { + auto id = arrbl.geometry_id(); + if (id.valid()) + result.insert(arrbl.geometry_id()); + }); + } + + return result; +} + +void arrange(Scene &scene, ArrangeTaskCtl &ctl) +{ + auto task = ArrangeTaskBase::create(Tasks::Arrange, scene); + auto result = task->process(ctl); + result->apply_on(scene.model()); +} + +}} // namespace Slic3r::arr2 diff --git a/src/libslic3r/Arrange/Scene.hpp b/src/libslic3r/Arrange/Scene.hpp new file mode 100644 index 0000000000..07069f0bf8 --- /dev/null +++ b/src/libslic3r/Arrange/Scene.hpp @@ -0,0 +1,308 @@ +#ifndef ARR2_SCENE_HPP +#define ARR2_SCENE_HPP + +#include +#include + +#include "libslic3r/ObjectID.hpp" +#include "libslic3r/AnyPtr.hpp" +#include "libslic3r/Arrange/ArrangeSettingsView.hpp" +#include "libslic3r/Arrange/SegmentedRectangleBed.hpp" + +namespace Slic3r { namespace arr2 { + +class AnyWritable +{ +public: + virtual ~AnyWritable() = default; + + virtual void write(std::string_view key, std::any d) = 0; +}; + +class Arrangeable +{ +public: + virtual ~Arrangeable() = default; + + virtual ObjectID id() const = 0; + virtual ObjectID geometry_id() const = 0; + virtual ExPolygons full_outline() const = 0; + virtual Polygon convex_outline() const = 0; + + virtual ExPolygons full_envelope() const { return {}; } + virtual Polygon convex_envelope() const { return {}; } + + virtual void transform(const Vec2d &transl, double rot) = 0; + + virtual bool is_printable() const { return true; } + virtual bool is_selected() const { return true; } + virtual int priority() const { return 0; } + + virtual void imbue_data(AnyWritable &datastore) const {} + void imbue_data(AnyWritable &&datastore) const { imbue_data(datastore); } + + // Returns the bed index on which the given ModelInstance is sitting. + virtual int get_bed_index() const = 0; + + // Assign the ModelInstance to the given bed index. Note that this + // method can return false, indicating that the given bed is not available + // to be occupied (e.g. the handler has a limited amount of logical bed) + virtual bool assign_bed(int bed_idx) = 0; +}; + +class ArrangeableModel +{ +public: + virtual ~ArrangeableModel() = default; + + virtual void for_each_arrangeable(std::function) = 0; + virtual void for_each_arrangeable(std::function) const = 0; + + virtual void visit_arrangeable(const ObjectID &id, std::function) const = 0; + virtual void visit_arrangeable(const ObjectID &id, std::function) = 0; + + virtual ObjectID add_arrangeable(const ObjectID &prototype_id) = 0; + + size_t arrangeable_count() const + { + size_t cnt = 0; + for_each_arrangeable([&cnt](auto &) { ++cnt; }); + + return cnt; + } +}; + +using XLBed = SegmentedRectangleBed, + std::integral_constant>; + +template struct ExtendedBed_ +{ + using Type = + boost::variant; +}; + +template struct ExtendedBed_> +{ + using Type = boost::variant; +}; + +using ExtendedBed = typename ExtendedBed_::Type; + +template void visit_bed(BedFn &&fn, const ExtendedBed &bed) +{ + boost::apply_visitor(fn, bed); +} + +template void visit_bed(BedFn &&fn, ExtendedBed &bed) +{ + boost::apply_visitor(fn, bed); +} + +inline ExtendedBed to_extended_bed(const ArrangeBed &bed) +{ + ExtendedBed ret; + boost::apply_visitor([&ret](auto &rawbed) { ret = rawbed; }, bed); + + return ret; +} + +class Scene; + +// A little CRTP to implement fluent interface returning Subclass references +template +class SceneBuilderBase +{ +protected: + AnyPtr m_arrangeable_model; + + AnyPtr m_settings; + + ExtendedBed m_bed = arr2::InfiniteBed{}; + + coord_t m_brims_offs = 0; + coord_t m_skirt_offs = 0; + +public: + + virtual ~SceneBuilderBase() = default; + + SceneBuilderBase() = default; + SceneBuilderBase(const SceneBuilderBase &) = delete; + SceneBuilderBase& operator=(const SceneBuilderBase &) = delete; + SceneBuilderBase(SceneBuilderBase &&) = default; + SceneBuilderBase& operator=(SceneBuilderBase &&) = default; + + // All setters return an rvalue reference so that at the end, the + // build_scene method can be called fluently + + Subclass &&set_arrange_settings(AnyPtr settings) + { + m_settings = std::move(settings); + return std::move(static_cast(*this)); + } + + Subclass &&set_arrange_settings(const ArrangeSettingsView &settings) + { + m_settings = std::make_unique(settings); + return std::move(static_cast(*this)); + } + + Subclass &&set_bed(const Points &pts) + { + m_bed = arr2::to_arrange_bed(pts); + return std::move(static_cast(*this)); + } + + Subclass && set_bed(const arr2::ArrangeBed &bed) + { + m_bed = bed; + return std::move(static_cast(*this)); + } + + Subclass &&set_bed(const XLBed &bed) + { + m_bed = bed; + return std::move(static_cast(*this)); + } + + Subclass &&set_arrangeable_model(AnyPtr model) + { + m_arrangeable_model = std::move(model); + return std::move(static_cast(*this)); + } + + // Can only be called on an rvalue instance (hence the && at the end), + // the method will potentially move its content into sc + virtual void build_scene(Scene &sc) &&; +}; + +class BasicSceneBuilder: public SceneBuilderBase {}; + +class Scene +{ + template friend class SceneBuilderBase; + + AnyPtr m_amodel; + AnyPtr m_settings; + ExtendedBed m_bed; + +public: + // Can only be built from an rvalue SceneBuilder, as it's content will + // potentially be moved to the constructed ArrangeScene object + template + explicit Scene(SceneBuilderBase &&bld) + { + std::move(bld).build_scene(*this); + } + + const ArrangeableModel &model() const noexcept { return *m_amodel; } + ArrangeableModel &model() noexcept { return *m_amodel; } + + const ArrangeSettingsView &settings() const noexcept { return *m_settings; } + + template void visit_bed(BedFn &&fn) const + { + arr2::visit_bed(fn, m_bed); + } + + const ExtendedBed & bed() const { return m_bed; } + + std::vector selected_ids() const; +}; + +std::set selected_geometry_ids(const Scene &sc); + +class EmptyArrangeableModel: public ArrangeableModel +{ +public: + void for_each_arrangeable(std::function) override {} + void for_each_arrangeable(std::function) const override {} + void visit_arrangeable(const ObjectID &id, std::function) const override {} + void visit_arrangeable(const ObjectID &id, std::function) override {} + ObjectID add_arrangeable(const ObjectID &prototype_id) override { return {}; } +}; + +template +void SceneBuilderBase::build_scene(Scene &sc) && +{ + if (!m_arrangeable_model) + m_arrangeable_model = std::make_unique(); + + if (!m_settings) + m_settings = std::make_unique(); + + coord_t inset = std::max(scaled(m_settings->get_distance_from_bed()), + m_skirt_offs + m_brims_offs); + + coord_t md = scaled(m_settings->get_distance_from_objects()); + md = md / 2 - inset; + + visit_bed([md](auto &rawbed) { rawbed = offset(rawbed, md); }, m_bed); + + sc.m_settings = std::move(m_settings); + sc.m_amodel = std::move(m_arrangeable_model); + sc.m_bed = std::move(m_bed); +} + +class ArrangeResult +{ +public: + virtual ~ArrangeResult() = default; + + virtual bool apply_on(ArrangeableModel &mdlwt) = 0; +}; + +enum class Tasks { Arrange, FillBed }; + +class ArrangeTaskCtl +{ +public: + virtual ~ArrangeTaskCtl() = default; + + virtual void update_status(int st) = 0; + + virtual bool was_canceled() const = 0; +}; + +class DummyCtl : public ArrangeTaskCtl +{ +public: + void update_status(int) override {} + bool was_canceled() const override { return false; } +}; + +class ArrangeTaskBase +{ +public: + using Ctl = ArrangeTaskCtl; + + virtual ~ArrangeTaskBase() = default; + + [[nodiscard]] virtual std::unique_ptr process(Ctl &ctl) = 0; + + [[nodiscard]] virtual int item_count_to_process() const = 0; + + [[nodiscard]] static std::unique_ptr create( + Tasks task_type, const Scene &sc); + + [[nodiscard]] std::unique_ptr process(Ctl &&ctl) + { + return process(ctl); + } + + [[nodiscard]] std::unique_ptr process() + { + return process(DummyCtl{}); + } +}; + +void arrange(Scene &scene, ArrangeTaskCtl &ctl); +inline void arrange(Scene &scene, ArrangeTaskCtl &&ctl = DummyCtl{}) +{ + arrange(scene, ctl); +} + +} // namespace arr2 +} // namespace Slic3r + +#endif // ARR2_SCENE_HPP diff --git a/src/libslic3r/Arrange/SceneBuilder.cpp b/src/libslic3r/Arrange/SceneBuilder.cpp new file mode 100644 index 0000000000..ecebb5a8f7 --- /dev/null +++ b/src/libslic3r/Arrange/SceneBuilder.cpp @@ -0,0 +1,859 @@ +#ifndef SCENEBUILDER_CPP +#define SCENEBUILDER_CPP + +#include "SceneBuilder.hpp" + +#include "libslic3r/Model.hpp" +#include "libslic3r/Print.hpp" +#include "libslic3r/SLAPrint.hpp" + +#include "Core/ArrangeItemTraits.hpp" +#include "Geometry/ConvexHull.hpp" + +namespace Slic3r { namespace arr2 { + +coord_t get_skirt_inset(const Print &fffprint) +{ + float skirt_inset = 0.f; + + if (fffprint.has_skirt()) { + float skirtflow = fffprint.objects().empty() + ? 0 + : fffprint.skirt_flow().width(); + skirt_inset = fffprint.config().skirts.value * skirtflow + + fffprint.config().skirt_distance.value; + } + + return scaled(skirt_inset); +} + +coord_t brim_offset(const PrintObject &po) +{ + const BrimType brim_type = po.config().brim_type.value; + const float brim_separation = po.config().brim_separation.getFloat(); + const float brim_width = po.config().brim_width.getFloat(); + const bool has_outer_brim = brim_type == BrimType::btOuterOnly || + brim_type == BrimType::btOuterAndInner; + + // How wide is the brim? (in scaled units) + return has_outer_brim ? scaled(brim_width + brim_separation) : 0; +} + +size_t model_instance_count (const Model &m) +{ + return std::accumulate(m.objects.begin(), + m.objects.end(), + size_t(0), + [](size_t s, const Slic3r::ModelObject *mo) { + return s + mo->instances.size(); + }); +} + +void transform_instance(ModelInstance &mi, + const Vec2d &transl_unscaled, + double rot, + const Transform3d &physical_tr) +{ + auto trafo = mi.get_transformation().get_matrix(); + auto tr = Transform3d::Identity(); + tr.translate(to_3d(transl_unscaled, 0.)); + trafo = physical_tr.inverse() * tr * Eigen::AngleAxisd(rot, Vec3d::UnitZ()) * physical_tr * trafo; + + mi.set_transformation(Geometry::Transformation{trafo}); + + mi.invalidate_object_bounding_box(); +} + +BoundingBoxf3 instance_bounding_box(const ModelInstance &mi, bool dont_translate) +{ + BoundingBoxf3 bb; + const Transform3d inst_matrix + = dont_translate ? mi.get_transformation().get_matrix_no_offset() + : mi.get_transformation().get_matrix(); + + for (ModelVolume *v : mi.get_object()->volumes) { + if (v->is_model_part()) { + bb.merge(v->mesh().transformed_bounding_box(inst_matrix + * v->get_matrix())); + } + } + + return bb; +} + +ExPolygons extract_full_outline(const ModelInstance &inst, const Transform3d &tr) +{ + ExPolygons outline; + for (const ModelVolume *v : inst.get_object()->volumes) { + Polygons vol_outline; + vol_outline = project_mesh(v->mesh().its, + tr * inst.get_matrix() * v->get_matrix(), + [] {}); + switch (v->type()) { + case ModelVolumeType::MODEL_PART: + outline = union_ex(outline, vol_outline); + break; + case ModelVolumeType::NEGATIVE_VOLUME: + outline = diff_ex(outline, vol_outline); + break; + default:; + } + } + + return outline; +} + +Polygon extract_convex_outline(const ModelInstance &inst, const Transform3d &tr) +{ + return inst.get_object()->convex_hull_2d(tr * inst.get_matrix()); +} + +inline static bool is_infinite_bed(const ExtendedBed &ebed) noexcept +{ + bool ret = false; + visit_bed( + [&ret](auto &rawbed) { + ret = std::is_convertible_v; + }, + ebed); + + return ret; +} + +void SceneBuilder::set_brim_and_skirt() +{ + if (!m_fff_print) + return; + + m_brims_offs = 0; + + for (const PrintObject *po : m_fff_print->objects()) { + if (po) { + m_brims_offs = std::max(m_brims_offs, brim_offset(*po)); + } + } + + m_skirt_offs = get_skirt_inset(*m_fff_print); +} + +void SceneBuilder::build_scene(Scene &sc) && +{ + if (m_sla_print && !m_fff_print) { + m_arrangeable_model = std::make_unique(m_sla_print.get(), *this); + } else { + m_arrangeable_model = std::make_unique(*this); + } + + if (m_fff_print && !m_sla_print) { + if (is_infinite_bed(m_bed)) { + set_bed(*m_fff_print); + } else { + set_brim_and_skirt(); + } + } + + std::move(*this).SceneBuilderBase::build_scene(sc); +} + +void SceneBuilder::build_arrangeable_slicer_model(ArrangeableSlicerModel &amodel) +{ + if (!m_model) + m_model = std::make_unique(); + + if (!m_selection) + m_selection = std::make_unique(*m_model); + + if (!m_vbed_handler) { + m_vbed_handler = VirtualBedHandler::create(m_bed); + } + + if (!m_wipetower_handler) { + m_wipetower_handler = std::make_unique(); + } + + amodel.m_vbed_handler = std::move(m_vbed_handler); + amodel.m_model = std::move(m_model); + amodel.m_selmask = std::move(m_selection); + amodel.m_wth = std::move(m_wipetower_handler); + + amodel.m_wth->set_selection_predicate( + [&amodel] { return amodel.m_selmask->is_wipe_tower(); }); +} + +int XStriderVBedHandler::get_bed_index(const VBedPlaceable &obj) const +{ + int bedidx = 0; + auto stride_s = stride_scaled(); + if (stride_s > 0) { + double bedx = unscaled(m_start); + auto instance_bb = obj.bounding_box(); + auto reference_pos_x = (instance_bb.min.x() - bedx); + auto stride = unscaled(stride_s); + + bedidx = static_cast(std::floor(reference_pos_x / stride)); + } + + return bedidx; +} + +bool XStriderVBedHandler::assign_bed(VBedPlaceable &obj, int bed_index) +{ + bool ret = false; + auto stride_s = stride_scaled(); + if (bed_index == 0 || (bed_index > 0 && stride_s > 0)) { + auto current_bed_index = get_bed_index(obj); + auto stride = unscaled(stride_s); + auto transl = Vec2d{(bed_index - current_bed_index) * stride, 0.}; + obj.displace(transl, 0.); + + ret = true; + } + + return ret; +} + +Transform3d XStriderVBedHandler::get_physical_bed_trafo(int bed_index) const +{ + auto stride_s = stride_scaled(); + auto tr = Transform3d::Identity(); + tr.translate(Vec3d{-bed_index * unscaled(stride_s), 0., 0.}); + + return tr; +} + +int YStriderVBedHandler::get_bed_index(const VBedPlaceable &obj) const +{ + int bedidx = 0; + auto stride_s = stride_scaled(); + if (stride_s > 0) { + double ystart = unscaled(m_start); + auto instance_bb = obj.bounding_box(); + auto reference_pos_y = (instance_bb.min.y() - ystart); + auto stride = unscaled(stride_s); + + bedidx = static_cast(std::floor(reference_pos_y / stride)); + } + + return bedidx; +} + +bool YStriderVBedHandler::assign_bed(VBedPlaceable &obj, int bed_index) +{ + bool ret = false; + auto stride_s = stride_scaled(); + if (bed_index == 0 || (bed_index > 0 && stride_s > 0)) { + auto current_bed_index = get_bed_index(obj); + auto stride = unscaled(stride_s); + auto transl = Vec2d{0., (bed_index - current_bed_index) * stride}; + obj.displace(transl, 0.); + + ret = true; + } + + return ret; +} + +Transform3d YStriderVBedHandler::get_physical_bed_trafo(int bed_index) const +{ + auto stride_s = stride_scaled(); + auto tr = Transform3d::Identity(); + tr.translate(Vec3d{0., -bed_index * unscaled(stride_s), 0.}); + + return tr; +} + +FixedSelection::FixedSelection(const Model &m) : m_wp{true} +{ + m_seldata.resize(m.objects.size()); + for (size_t i = 0; i < m.objects.size(); ++i) { + m_seldata[i].resize(m.objects[i]->instances.size(), true); + } +} + +FixedSelection::FixedSelection(const SelectionMask &other) +{ + auto obj_sel = other.selected_objects(); + m_seldata.reserve(obj_sel.size()); + for (int oidx = 0; oidx < static_cast(obj_sel.size()); ++oidx) + m_seldata.emplace_back(other.selected_instances(oidx)); +} + +std::vector FixedSelection::selected_objects() const +{ + auto ret = Slic3r::reserve_vector(m_seldata.size()); + std::transform(m_seldata.begin(), + m_seldata.end(), + std::back_inserter(ret), + [](auto &a) { + return std::any_of(a.begin(), a.end(), [](bool b) { + return b; + }); + }); + return ret; +} + +static std::vector find_true_indices(const std::vector &v) +{ + auto ret = reserve_vector(v.size()); + + for (size_t i = 0; i < v.size(); ++i) + if (v[i]) + ret.emplace_back(i); + + return ret; +} + +std::vector selected_object_indices(const SelectionMask &sm) +{ + auto sel = sm.selected_objects(); + return find_true_indices(sel); +} + +std::vector selected_instance_indices(int obj_idx, const SelectionMask &sm) +{ + auto sel = sm.selected_instances(obj_idx); + return find_true_indices(sel); +} + +SceneBuilder::SceneBuilder() = default; +SceneBuilder::~SceneBuilder() = default; +SceneBuilder::SceneBuilder(SceneBuilder &&) = default; +SceneBuilder& SceneBuilder::operator=(SceneBuilder&&) = default; + +SceneBuilder &&SceneBuilder::set_model(AnyPtr mdl) +{ + m_model = std::move(mdl); + return std::move(*this); +} + +SceneBuilder &&SceneBuilder::set_model(Model &mdl) +{ + m_model = &mdl; + return std::move(*this); +} + +SceneBuilder &&SceneBuilder::set_fff_print(AnyPtr mdl_print) +{ + m_fff_print = std::move(mdl_print); + return std::move(*this); +} + +SceneBuilder &&SceneBuilder::set_sla_print(AnyPtr mdl_print) +{ + m_sla_print = std::move(mdl_print); + + return std::move(*this); +} + +SceneBuilder &&SceneBuilder::set_bed(const DynamicPrintConfig &cfg) +{ + Points bedpts = get_bed_shape(cfg); + + if (is_XL_printer(cfg)) { + m_bed = XLBed{get_extents(bedpts)}; + } else { + m_bed = arr2::to_arrange_bed(bedpts); + } + + return std::move(*this); +} + +SceneBuilder &&SceneBuilder::set_bed(const Print &print) +{ + Points bedpts = get_bed_shape(print.config()); + + if (is_XL_printer(print.config())) { + m_bed = XLBed{get_extents(bedpts)}; + } else { + m_bed = arr2::to_arrange_bed(bedpts); + } + + set_brim_and_skirt(); + + return std::move(*this); +} + +SceneBuilder &&SceneBuilder::set_sla_print(const SLAPrint *slaprint) +{ + m_sla_print = slaprint; + return std::move(*this); +} + +int ArrangeableWipeTowerBase::get_bed_index() const { return PhysicalBedId; } + +bool ArrangeableWipeTowerBase::assign_bed(int bed_idx) +{ + return bed_idx == PhysicalBedId; +} + +bool PhysicalOnlyVBedHandler::assign_bed(VBedPlaceable &inst, int bed_idx) +{ + return bed_idx == PhysicalBedId; +} + +ArrangeableSlicerModel::ArrangeableSlicerModel(SceneBuilder &builder) +{ + builder.build_arrangeable_slicer_model(*this); +} + +ArrangeableSlicerModel::~ArrangeableSlicerModel() = default; + +void ArrangeableSlicerModel::for_each_arrangeable( + std::function fn) +{ + for_each_arrangeable_(*this, fn); + + m_wth->visit(fn); +} + +void ArrangeableSlicerModel::for_each_arrangeable( + std::function fn) const +{ + for_each_arrangeable_(*this, fn); + + m_wth->visit(fn); +} + +ObjectID ArrangeableSlicerModel::add_arrangeable(const ObjectID &prototype_id) +{ + ObjectID ret; + + auto [inst, pos] = find_instance_by_id(*m_model, prototype_id); + if (inst) { + auto new_inst = inst->get_object()->add_instance(*inst); + if (new_inst) { + ret = new_inst->id(); + } + } + + return ret; +} + +template +void ArrangeableSlicerModel::for_each_arrangeable_(Self &&self, Fn &&fn) +{ + InstPos pos; + for (auto *obj : self.m_model->objects) { + for (auto *inst : obj->instances) { + ArrangeableModelInstance ainst{inst, self.m_vbed_handler.get(), + self.m_selmask.get(), pos}; + fn(ainst); + + ++pos.inst_idx; + } + pos.inst_idx = 0; + ++pos.obj_idx; + } +} + +template +void ArrangeableSlicerModel::visit_arrangeable_(Self &&self, const ObjectID &id, Fn &&fn) +{ + if (id == self.m_model->wipe_tower.id()) { + self.m_wth->visit(fn); + + return; + } + + auto [inst, pos] = find_instance_by_id(*self.m_model, id); + + if (inst) { + ArrangeableModelInstance ainst{inst, self.m_vbed_handler.get(), + self.m_selmask.get(), pos}; + + fn(ainst); + } +} + +void ArrangeableSlicerModel::visit_arrangeable( + const ObjectID &id, std::function fn) const +{ + visit_arrangeable_(*this, id, fn); +} + +void ArrangeableSlicerModel::visit_arrangeable( + const ObjectID &id, std::function fn) +{ + visit_arrangeable_(*this, id, fn); +} + +template +void ArrangeableSLAPrint::for_each_arrangeable_(Self &&self, Fn &&fn) +{ + InstPos pos; + for (auto *obj : self.m_model->objects) { + for (auto *inst : obj->instances) { + ArrangeableModelInstance ainst{inst, self.m_vbed_handler.get(), + self.m_selmask.get(), pos}; + + auto obj_id = inst->get_object()->id(); + const SLAPrintObject *po = + self.m_slaprint->get_print_object_by_model_object_id(obj_id); + + if (po) { + auto &vbh = self.m_vbed_handler; + auto phtr = vbh->get_physical_bed_trafo(vbh->get_bed_index(VBedPlaceableMI{*inst})); + ArrangeableSLAPrintObject ainst_po{po, &ainst, phtr * inst->get_matrix()}; + fn(ainst_po); + } else { + fn(ainst); + } + + ++pos.inst_idx; + } + pos.inst_idx = 0; + ++pos.obj_idx; + } +} + +void ArrangeableSLAPrint::for_each_arrangeable( + std::function fn) +{ + for_each_arrangeable_(*this, fn); + + m_wth->visit(fn); +} + +void ArrangeableSLAPrint::for_each_arrangeable( + std::function fn) const +{ + for_each_arrangeable_(*this, fn); + + m_wth->visit(fn); +} + +template +void ArrangeableSLAPrint::visit_arrangeable_(Self &&self, const ObjectID &id, Fn &&fn) +{ + auto [inst, pos] = find_instance_by_id(*self.m_model, id); + + if (inst) { + ArrangeableModelInstance ainst{inst, self.m_vbed_handler.get(), + self.m_selmask.get(), pos}; + + auto obj_id = inst->get_object()->id(); + const SLAPrintObject *po = + self.m_slaprint->get_print_object_by_model_object_id(obj_id); + + if (po) { + auto &vbh = self.m_vbed_handler; + auto phtr = vbh->get_physical_bed_trafo(vbh->get_bed_index(VBedPlaceableMI{*inst})); + ArrangeableSLAPrintObject ainst_po{po, &ainst, phtr * inst->get_matrix()}; + fn(ainst_po); + } else { + fn(ainst); + } + } +} + +void ArrangeableSLAPrint::visit_arrangeable( + const ObjectID &id, std::function fn) const +{ + visit_arrangeable_(*this, id, fn); +} + +void ArrangeableSLAPrint::visit_arrangeable( + const ObjectID &id, std::function fn) +{ + visit_arrangeable_(*this, id, fn); +} + +template +ExPolygons ArrangeableModelInstance::full_outline() const +{ + int bedidx = m_vbedh->get_bed_index(*this); + auto tr = m_vbedh->get_physical_bed_trafo(bedidx); + + return extract_full_outline(*m_mi, tr); +} + +template +Polygon ArrangeableModelInstance::convex_outline() const +{ + int bedidx = m_vbedh->get_bed_index(*this); + auto tr = m_vbedh->get_physical_bed_trafo(bedidx); + + return extract_convex_outline(*m_mi, tr); +} + +template +bool ArrangeableModelInstance::is_selected() const +{ + bool ret = false; + + if (m_selmask) { + auto sel = m_selmask->selected_instances(m_pos_within_model.obj_idx); + if (m_pos_within_model.inst_idx < sel.size() && + sel[m_pos_within_model.inst_idx]) + ret = true; + } + + return ret; +} + +template +void ArrangeableModelInstance::transform(const Vec2d &transl, double rot) +{ + if constexpr (!std::is_const_v && !std::is_const_v) { + int bedidx = m_vbedh->get_bed_index(*this); + auto physical_trafo = m_vbedh->get_physical_bed_trafo(bedidx); + + transform_instance(*m_mi, transl, rot, physical_trafo); + } +} + +template +bool ArrangeableModelInstance::assign_bed(int bed_idx) +{ + bool ret = false; + + if constexpr (!std::is_const_v && !std::is_const_v) + ret = m_vbedh->assign_bed(*this, bed_idx); + + return ret; +} + +template class ArrangeableModelInstance; +template class ArrangeableModelInstance; + +ExPolygons ArrangeableSLAPrintObject::full_outline() const +{ + ExPolygons ret; + + auto laststep = m_po->last_completed_step(); + if (laststep < slaposCount && laststep > slaposSupportTree) { + Polygons polys; + auto omesh = m_po->get_mesh_to_print(); + auto &smesh = m_po->support_mesh(); + + Transform3d trafo_instance = m_inst_trafo * m_po->trafo().inverse(); + + if (omesh) { + Polygons ptmp = project_mesh(*omesh, trafo_instance, [] {}); + std::move(ptmp.begin(), ptmp.end(), std::back_inserter(polys)); + } + + Polygons ptmp = project_mesh(smesh.its, trafo_instance, [] {}); + std::move(ptmp.begin(), ptmp.end(), std::back_inserter(polys)); + ret = union_ex(polys); + } + + return ret; +} + +ExPolygons ArrangeableSLAPrintObject::full_envelope() const +{ + ExPolygons ret = full_outline(); + + auto laststep = m_po->last_completed_step(); + if (laststep < slaposCount && laststep > slaposSupportTree) { + auto &pmesh = m_po->pad_mesh(); + if (!pmesh.empty()) { + + Transform3d trafo_instance = m_inst_trafo * m_po->trafo().inverse(); + + Polygons ptmp = project_mesh(pmesh.its, trafo_instance, [] {}); + ret = union_ex(ret, ptmp); + } + } else { + // The 1.1 multiplier is a safety gap, as the offset might be bigger + // in sharp edges of a polygon, depending on clipper's offset algorithm + coord_t pad_infl = 0; + { + double infl = m_po->config().pad_enable.getBool() * + (m_po->config().pad_brim_size.getFloat() + + m_po->config().pad_around_object.getBool() * + m_po->config().pad_object_gap.getFloat()); + + pad_infl = scaled(1.1 * infl); + } + + if (pad_infl > 0) { + ret = offset_ex(ret, pad_infl); + } + } + + return ret; +} + +Polygon ArrangeableSLAPrintObject::convex_outline() const +{ + Polygons polys; + + polys.emplace_back(m_arrbl->convex_outline()); + + auto laststep = m_po->last_completed_step(); + if (laststep < slaposCount && laststep > slaposSupportTree) { + auto omesh = m_po->get_mesh_to_print(); + auto &smesh = m_po->support_mesh(); + + Transform3f trafo_instance = m_inst_trafo.cast(); + trafo_instance = trafo_instance * m_po->trafo().cast().inverse(); + + Polygons polys; + polys.reserve(3); + auto zlvl = -m_po->get_elevation(); + + if (omesh) { + polys.emplace_back( + its_convex_hull_2d_above(*omesh, trafo_instance, zlvl)); + } + + polys.emplace_back( + its_convex_hull_2d_above(smesh.its, trafo_instance, zlvl)); + } + + return Geometry::convex_hull(polys); +} + +Polygon ArrangeableSLAPrintObject::convex_envelope() const +{ + Polygons polys; + + polys.emplace_back(convex_outline()); + + auto laststep = m_po->last_completed_step(); + if (laststep < slaposCount && laststep > slaposSupportTree) { + auto &pmesh = m_po->pad_mesh(); + if (!pmesh.empty()) { + + Transform3f trafo_instance = m_inst_trafo.cast(); + trafo_instance = trafo_instance * m_po->trafo().cast().inverse(); + auto zlvl = -m_po->get_elevation(); + + polys.emplace_back( + its_convex_hull_2d_above(pmesh.its, trafo_instance, zlvl)); + } + } else { + // The 1.1 multiplier is a safety gap, as the offset might be bigger + // in sharp edges of a polygon, depending on clipper's offset algorithm + coord_t pad_infl = 0; + { + double infl = m_po->config().pad_enable.getBool() * + (m_po->config().pad_brim_size.getFloat() + + m_po->config().pad_around_object.getBool() * + m_po->config().pad_object_gap.getFloat()); + + pad_infl = scaled(1.1 * infl); + } + + if (pad_infl > 0) { + polys = offset(polys, pad_infl); + } + } + + return Geometry::convex_hull(polys); +} + +DuplicableModel::DuplicableModel(AnyPtr mdl, AnyPtr vbh, const BoundingBox &bedbb) + : m_model{std::move(mdl)}, m_vbh{std::move(vbh)}, m_duplicates(1), m_bedbb{bedbb} +{ +} + +DuplicableModel::~DuplicableModel() = default; + +ObjectID DuplicableModel::add_arrangeable(const ObjectID &prototype_id) +{ + ObjectID ret; + if (prototype_id.valid()) { + size_t idx = prototype_id.id - 1; + if (idx < m_duplicates.size()) { + ModelDuplicate md = m_duplicates[idx]; + md.id = m_duplicates.size(); + ret = md.id.id + 1; + m_duplicates.emplace_back(std::move(md)); + } + } + + return ret; +} + +void DuplicableModel::apply_duplicates() +{ + for (ModelObject *o : m_model->objects) { + // make a copy of the pointers in order to avoid recursion + // when appending their copies + ModelInstancePtrs instances = o->instances; + o->instances.clear(); + for (const ModelInstance *i : instances) { + for (const ModelDuplicate &md : m_duplicates) { + ModelInstance *instance = o->add_instance(*i); + arr2::transform_instance(*instance, md.tr, md.rot); + } + } + for (auto *i : instances) + delete i; + + instances.clear(); + + o->invalidate_bounding_box(); + } +} + +template +ObjectID ArrangeableFullModel::geometry_id() const { return m_mdl->id(); } + +template +ExPolygons ArrangeableFullModel::full_outline() const +{ + auto ret = reserve_vector(arr2::model_instance_count(*m_mdl)); + + auto transl = Transform3d::Identity(); + transl.translate(to_3d(m_dup->tr, 0.)); + Transform3d trafo = transl* Eigen::AngleAxisd(m_dup->rot, Vec3d::UnitZ()); + + for (auto *mo : m_mdl->objects) { + for (auto *mi : mo->instances) { + auto expolys = arr2::extract_full_outline(*mi, trafo); + std::move(expolys.begin(), expolys.end(), std::back_inserter(ret)); + } + } + + return ret; +} + +template +Polygon ArrangeableFullModel::convex_outline() const +{ + auto ret = reserve_polygons(arr2::model_instance_count(*m_mdl)); + + auto transl = Transform3d::Identity(); + transl.translate(to_3d(m_dup->tr, 0.)); + Transform3d trafo = transl* Eigen::AngleAxisd(m_dup->rot, Vec3d::UnitZ()); + + for (auto *mo : m_mdl->objects) { + for (auto *mi : mo->instances) { + ret.emplace_back(arr2::extract_convex_outline(*mi, trafo)); + } + } + + return Geometry::convex_hull(ret); +} + +template class ArrangeableFullModel; +template class ArrangeableFullModel; + +std::unique_ptr VirtualBedHandler::create(const ExtendedBed &bed) +{ + std::unique_ptr ret; + if (is_infinite_bed(bed)) { + ret = std::make_unique(); + } else { + // The gap between logical beds in the x axis expressed in ratio of + // the current bed width. + constexpr double LogicalBedGap = 1. / 10.; + + BoundingBox bedbb; + visit_bed([&bedbb](auto &rawbed) { bedbb = bounding_box(rawbed); }, bed); + + auto bedwidth = bedbb.size().x(); + coord_t xgap = LogicalBedGap * bedwidth; + ret = std::make_unique(bedbb, xgap); + } + + return ret; +} + +}} // namespace Slic3r::arr2 + +#endif // SCENEBUILDER_CPP diff --git a/src/libslic3r/Arrange/SceneBuilder.hpp b/src/libslic3r/Arrange/SceneBuilder.hpp new file mode 100644 index 0000000000..7939ba83d3 --- /dev/null +++ b/src/libslic3r/Arrange/SceneBuilder.hpp @@ -0,0 +1,645 @@ +#ifndef SCENEBUILDER_HPP +#define SCENEBUILDER_HPP + +#include "Scene.hpp" +#include "Core/ArrangeItemTraits.hpp" + +namespace Slic3r { + +class Model; +class ModelInstance; +class ModelWipeTower; +class Print; +class SLAPrint; +class SLAPrintObject; +class PrintObject; +class DynamicPrintConfig; + +namespace arr2 { + +using SelectionPredicate = std::function; + +class WipeTowerHandler +{ +public: + virtual ~WipeTowerHandler() = default; + + virtual void visit(std::function) = 0; + virtual void visit(std::function) const = 0; + virtual void set_selection_predicate(SelectionPredicate pred) = 0; +}; + +class VBedPlaceable { +public: + virtual ~VBedPlaceable() = default; + + virtual BoundingBoxf bounding_box() const = 0; + virtual void displace(const Vec2d &transl, double rot) = 0; +}; + +// An interface to handle virtual beds for ModelInstances. A ModelInstance +// may be assigned to a logical bed identified by an integer index value (zero +// is the actual physical bed). The ModelInstance may still be outside of it's +// bed, regardless of being assigned to it. The handler object should provide +// means to read the assigned bed index of a ModelInstance, to assign a +// different bed index and to provide a trafo that maps it to the physical bed +// given a logical bed index. The reason is that the arrangement expects items +// to be in the coordinate system of the physical bed. +class VirtualBedHandler +{ +public: + virtual ~VirtualBedHandler() = default; + + // Returns the bed index on which the given ModelInstance is sitting. + virtual int get_bed_index(const VBedPlaceable &obj) const = 0; + + // The returned trafo can be used to move the outline of the ModelInstance + // to the coordinate system of the physical bed, should that differ from + // the coordinate space of a logical bed. + virtual Transform3d get_physical_bed_trafo(int bed_index) const = 0; + + // Assign the ModelInstance to the given bed index. Note that this + // method can return false, indicating that the given bed is not available + // to be occupied (e.g. the handler has a limited amount of logical bed) + virtual bool assign_bed(VBedPlaceable &obj, int bed_idx) = 0; + + bool assign_bed(VBedPlaceable &&obj, int bed_idx) + { + return assign_bed(obj, bed_idx); + } + + static std::unique_ptr create(const ExtendedBed &bed); +}; + +class SelectionMask +{ +public: + virtual ~SelectionMask() = default; + + virtual std::vector selected_objects() const = 0; + virtual std::vector selected_instances(int obj_id) const = 0; + virtual bool is_wipe_tower() const = 0; +}; + +class FixedSelection : public Slic3r::arr2::SelectionMask +{ + std::vector> m_seldata; + bool m_wp = false; + +public: + FixedSelection() = default; + + explicit FixedSelection(std::initializer_list> seld, + bool wp = false) + : m_seldata{std::move(seld)}, m_wp{wp} + {} + + explicit FixedSelection(const Model &m); + + explicit FixedSelection(const SelectionMask &other); + + std::vector selected_objects() const override; + + std::vector selected_instances(int obj_id) const override + { + return obj_id < int(m_seldata.size()) ? m_seldata[obj_id] : + std::vector{}; + } + + bool is_wipe_tower() const override { return m_wp; } +}; + +struct ArrangeableWipeTowerBase: public Arrangeable +{ + ObjectID oid; + + Polygon poly; + Point pos = Point::Zero(); + double rot = 0.; + SelectionPredicate selection_pred; + + ArrangeableWipeTowerBase( + const ObjectID &objid, + Polygon shape, + const Point &p, + double r, + SelectionPredicate selection_predicate = [] { return false; }) + : oid{objid}, + poly{std::move(shape)}, + pos{p}, + rot{r}, + selection_pred{std::move(selection_predicate)} + {} + + ObjectID id() const override { return oid; } + ObjectID geometry_id() const override { return {}; } + + ExPolygons full_outline() const override + { + auto cpy = poly; + cpy.translate(pos); + return {ExPolygon{cpy}}; + } + + Polygon convex_outline() const override + { + auto cpy = poly; + cpy.translate(pos); + return cpy; + } + + bool is_selected() const override + { + return selection_pred(); + } + + int get_bed_index() const override; + bool assign_bed(int /*bed_idx*/) override; + + int priority() const override { return 1; } + + void transform(const Vec2d &transl, double rot) override {} + + void imbue_data(AnyWritable &datastore) const override + { + datastore.write("is_wipe_tower", {}); + } +}; + +class SceneBuilder; + +class ArrangeableSlicerModel: public ArrangeableModel +{ +protected: + AnyPtr m_model; + AnyPtr m_wth; + AnyPtr m_vbed_handler; + AnyPtr m_selmask; + +private: + friend class SceneBuilder; + + template + static void for_each_arrangeable_(Self &&self, Fn &&fn); + + template + static void visit_arrangeable_(Self &&self, const ObjectID &id, Fn &&fn); + +public: + explicit ArrangeableSlicerModel(SceneBuilder &builder); + ~ArrangeableSlicerModel(); + + void for_each_arrangeable(std::function) override; + void for_each_arrangeable(std::function) const override; + + void visit_arrangeable(const ObjectID &id, std::function) const override; + void visit_arrangeable(const ObjectID &id, std::function) override; + + ObjectID add_arrangeable(const ObjectID &prototype_id) override; +}; + +class SceneBuilder: public SceneBuilderBase +{ +protected: + AnyPtr m_model; + AnyPtr m_wipetower_handler; + AnyPtr m_vbed_handler; + AnyPtr m_selection; + + AnyPtr m_sla_print; + AnyPtr m_fff_print; + + void set_brim_and_skirt(); + +public: + SceneBuilder(); + ~SceneBuilder(); + SceneBuilder(SceneBuilder&&); + SceneBuilder& operator=(SceneBuilder&&); + + SceneBuilder && set_model(AnyPtr mdl); + + SceneBuilder && set_model(Model &mdl); + + SceneBuilder && set_fff_print(AnyPtr fffprint); + SceneBuilder && set_sla_print(AnyPtr mdl_print); + + using SceneBuilderBase::set_bed; + + SceneBuilder &&set_bed(const DynamicPrintConfig &cfg); + SceneBuilder &&set_bed(const Print &print); + + SceneBuilder && set_wipe_tower_handler(WipeTowerHandler &wth) + { + m_wipetower_handler = &wth; + return std::move(*this); + } + + SceneBuilder && set_wipe_tower_handler(AnyPtr wth) + { + m_wipetower_handler = std::move(wth); + return std::move(*this); + } + + SceneBuilder && set_virtual_bed_handler(AnyPtr vbedh) + { + m_vbed_handler = std::move(vbedh); + return std::move(*this); + } + + SceneBuilder && set_sla_print(const SLAPrint *slaprint); + + SceneBuilder && set_selection(AnyPtr sel) + { + m_selection = std::move(sel); + return std::move(*this); + } + + // Can only be called on an rvalue instance (hence the && at the end), + // the method will potentially move its content into sc + void build_scene(Scene &sc) && override; + + void build_arrangeable_slicer_model(ArrangeableSlicerModel &amodel); +}; + +struct MissingWipeTowerHandler : public WipeTowerHandler +{ + void visit(std::function) override {} + void visit(std::function) const override {} + void set_selection_predicate(std::function) override {} +}; + +// Only a physical bed, non-zero bed index values are discarded. +class PhysicalOnlyVBedHandler final : public VirtualBedHandler +{ +public: + using VirtualBedHandler::assign_bed; + + int get_bed_index(const VBedPlaceable &obj) const override { return 0; } + + Transform3d get_physical_bed_trafo(int bed_index) const override + { + return Transform3d::Identity(); + } + + bool assign_bed(VBedPlaceable &inst, int bed_idx) override; +}; + +// A virtual bed handler implementation, that defines logical beds to be created +// on the right side of the physical bed along the X axis in a row +class XStriderVBedHandler final : public VirtualBedHandler +{ + coord_t m_stride_scaled; + coord_t m_start; + +public: + explicit XStriderVBedHandler(const BoundingBox &bedbb, coord_t xgap) + : m_stride_scaled{bedbb.size().x() + 2 * std::max(0, xgap)}, + m_start{bedbb.min.x() - std::max(0, xgap)} + { + } + + coord_t stride_scaled() const { return m_stride_scaled; } + + // Can return negative indices when the instance is to the left of the + // physical bed + int get_bed_index(const VBedPlaceable &obj) const override; + + // Only positive beds are accepted + bool assign_bed(VBedPlaceable &inst, int bed_idx) override; + + using VirtualBedHandler::assign_bed; + + Transform3d get_physical_bed_trafo(int bed_index) const override; +}; + +// Same as XStriderVBedHandler only that it lays out vbeds on the Y axis +class YStriderVBedHandler final : public VirtualBedHandler +{ + coord_t m_stride_scaled; + coord_t m_start; + +public: + coord_t stride_scaled() const { return m_stride_scaled; } + + explicit YStriderVBedHandler(const BoundingBox &bedbb, coord_t ygap) + : m_stride_scaled{bedbb.size().y() + 2 * std::max(0, ygap)} + , m_start{bedbb.min.y() - std::max(0, ygap)} + {} + + int get_bed_index(const VBedPlaceable &obj) const override; + bool assign_bed(VBedPlaceable &inst, int bed_idx) override; + + Transform3d get_physical_bed_trafo(int bed_index) const override; +}; + +std::vector selected_object_indices(const SelectionMask &sm); +std::vector selected_instance_indices(int obj_idx, const SelectionMask &sm); + +coord_t get_skirt_inset(const Print &fffprint); + +coord_t brim_offset(const PrintObject &po); + +// unscaled coords are necessary to be able to handle bigger coordinate range +// than what is available with scaled coords. This is useful when working with +// virtual beds. +void transform_instance(ModelInstance &mi, + const Vec2d &transl_unscaled, + double rot, + const Transform3d &physical_tr = Transform3d::Identity()); + +BoundingBoxf3 instance_bounding_box(const ModelInstance &mi, + bool dont_translate = false); + + +ExPolygons extract_full_outline(const ModelInstance &inst, + const Transform3d &tr = Transform3d::Identity()); + +Polygon extract_convex_outline(const ModelInstance &inst, + const Transform3d &tr = Transform3d::Identity()); + +size_t model_instance_count (const Model &m); + +class VBedPlaceableMI : public VBedPlaceable +{ + ModelInstance *m_mi; + +public: + explicit VBedPlaceableMI(ModelInstance &mi) : m_mi{&mi} {} + + BoundingBoxf bounding_box() const override { return to_2d(instance_bounding_box(*m_mi)); } + void displace(const Vec2d &transl, double rot) override + { + transform_instance(*m_mi, transl, rot); + } +}; + +struct InstPos { size_t obj_idx = 0, inst_idx = 0; }; + +template +class ArrangeableModelInstance : public Arrangeable, VBedPlaceable +{ + InstPtr *m_mi; + VBedHPtr *m_vbedh; + const SelectionMask *m_selmask; + InstPos m_pos_within_model; + +public: + explicit ArrangeableModelInstance(InstPtr *mi, + VBedHPtr *vbedh, + const SelectionMask *selmask, + const InstPos &pos) + : m_mi{mi}, m_vbedh{vbedh}, m_selmask{selmask}, m_pos_within_model{pos} + { + assert(m_mi != nullptr && m_vbedh != nullptr); + } + + // Arrangeable: + ObjectID id() const override { return m_mi->id(); } + ObjectID geometry_id() const override { return m_mi->get_object()->id(); } + ExPolygons full_outline() const override; + Polygon convex_outline() const override; + bool is_printable() const override { return m_mi->printable; } + bool is_selected() const override; + void transform(const Vec2d &tr, double rot) override; + + int get_bed_index() const override { return m_vbedh->get_bed_index(*this); } + bool assign_bed(int bed_idx) override; + + // VBedPlaceable: + BoundingBoxf bounding_box() const override { return to_2d(instance_bounding_box(*m_mi)); } + void displace(const Vec2d &transl, double rot) override + { + if constexpr (!std::is_const_v) + transform_instance(*m_mi, transl, rot); + } +}; + +extern template class ArrangeableModelInstance; +extern template class ArrangeableModelInstance; + +class ArrangeableSLAPrintObject : public Arrangeable +{ + const SLAPrintObject *m_po; + Arrangeable *m_arrbl; + Transform3d m_inst_trafo; + +public: + ArrangeableSLAPrintObject(const SLAPrintObject *po, + Arrangeable *arrbl, + const Transform3d &inst_tr = Transform3d::Identity()) + : m_po{po}, m_arrbl{arrbl}, m_inst_trafo{inst_tr} + {} + + ObjectID id() const override { return m_arrbl->id(); } + ObjectID geometry_id() const override { return m_arrbl->geometry_id(); } + + ExPolygons full_outline() const override; + ExPolygons full_envelope() const override; + + Polygon convex_outline() const override; + Polygon convex_envelope() const override; + + void transform(const Vec2d &transl, double rot) override + { + m_arrbl->transform(transl, rot); + } + int get_bed_index() const override { return m_arrbl->get_bed_index(); } + bool assign_bed(int bedidx) override + { + return m_arrbl->assign_bed(bedidx); + } + + bool is_printable() const override { return m_arrbl->is_printable(); } + bool is_selected() const override { return m_arrbl->is_selected(); } + int priority() const override { return m_arrbl->priority(); } +}; + +class ArrangeableSLAPrint : public ArrangeableSlicerModel { + const SLAPrint *m_slaprint; + + friend class SceneBuilder; + + template + static void for_each_arrangeable_(Self &&self, Fn &&fn); + + template + static void visit_arrangeable_(Self &&self, const ObjectID &id, Fn &&fn); + +public: + explicit ArrangeableSLAPrint(const SLAPrint *slaprint, + SceneBuilder &builder) + : m_slaprint{slaprint}, ArrangeableSlicerModel{builder} + { + assert(slaprint != nullptr); + } + + void for_each_arrangeable(std::function) override; + + void for_each_arrangeable( + std::function) const override; + + void visit_arrangeable( + const ObjectID &id, + std::function) const override; + + void visit_arrangeable(const ObjectID &id, + std::function) override; +}; + +template +auto find_instance_by_id(Mdl &&model, const ObjectID &id) +{ + std::remove_reference_t< + decltype(std::declval().objects[0]->instances[0])> + ret = nullptr; + + InstPos pos; + + for (auto * obj : model.objects) { + for (auto *inst : obj->instances) { + if (inst->id() == id) { + ret = inst; + break; + } + ++pos.inst_idx; + } + + if (ret) + break; + + ++pos.obj_idx; + pos.inst_idx = 0; + } + + return std::make_pair(ret, pos); +} + +struct ModelDuplicate +{ + ObjectID id; + Vec2d tr = Vec2d::Zero(); + double rot = 0.; + int bed_idx = Unarranged; +}; + +// Implementing the Arrangeable interface with the whole Model being one outline +// with all its objects and instances. +template +class ArrangeableFullModel: public Arrangeable, VBedPlaceable +{ + Mdl *m_mdl; + Dup *m_dup; + VBH *m_vbh; + +public: + explicit ArrangeableFullModel(Mdl *mdl, + Dup *md, + VBH *vbh) + : m_mdl{mdl}, m_dup{md}, m_vbh{vbh} + { + assert(m_mdl != nullptr); + } + + ObjectID id() const override { return m_dup->id.id + 1; } + ObjectID geometry_id() const override; + + ExPolygons full_outline() const override; + + Polygon convex_outline() const override; + + bool is_printable() const override { return true; } + bool is_selected() const override { return m_dup->id == 0; } + + int get_bed_index() const override + { + return m_vbh->get_bed_index(*this); + } + + void transform(const Vec2d &tr, double rot) override + { + if constexpr (!std::is_const_v && !std::is_const_v) { + m_dup->tr += tr; + m_dup->rot += rot; + } + } + + bool assign_bed(int bed_idx) override + { + bool ret = false; + + if constexpr (!std::is_const_v && !std::is_const_v) { + if ((ret = m_vbh->assign_bed(*this, bed_idx))) + m_dup->bed_idx = bed_idx; + } + + return ret; + } + + BoundingBoxf bounding_box() const override { return unscaled(get_extents(convex_outline())); } + void displace(const Vec2d &transl, double rot) override + { + transform(transl, rot); + } +}; + +extern template class ArrangeableFullModel; +extern template class ArrangeableFullModel; + +class DuplicableModel: public ArrangeableModel { + AnyPtr m_model; + AnyPtr m_vbh; + std::vector m_duplicates; + BoundingBox m_bedbb; + + template + static void visit_arrangeable_(Self &&self, const ObjectID &id, Fn &&fn) + { + if (id.valid()) { + size_t idx = id.id - 1; + if (idx < self.m_duplicates.size()) { + auto &md = self.m_duplicates[idx]; + ArrangeableFullModel arrbl{self.m_model.get(), &md, self.m_vbh.get()}; + fn(arrbl); + } + } + } + +public: + explicit DuplicableModel(AnyPtr mdl, + AnyPtr vbh, + const BoundingBox &bedbb); + ~DuplicableModel(); + + void for_each_arrangeable(std::function fn) override + { + for (ModelDuplicate &md : m_duplicates) { + ArrangeableFullModel arrbl{m_model.get(), &md, m_vbh.get()}; + fn(arrbl); + } + } + void for_each_arrangeable(std::function fn) const override + { + for (const ModelDuplicate &md : m_duplicates) { + ArrangeableFullModel arrbl{m_model.get(), &md, m_vbh.get()}; + fn(arrbl); + } + } + void visit_arrangeable(const ObjectID &id, std::function fn) const override + { + visit_arrangeable_(*this, id, fn); + } + void visit_arrangeable(const ObjectID &id, std::function fn) override + { + visit_arrangeable_(*this, id, fn); + } + + ObjectID add_arrangeable(const ObjectID &prototype_id) override; + + void apply_duplicates(); +}; + +} // namespace arr2 +} // namespace Slic3r + +#endif // SCENEBUILDER_HPP diff --git a/src/libslic3r/Arrange/SegmentedRectangleBed.hpp b/src/libslic3r/Arrange/SegmentedRectangleBed.hpp new file mode 100644 index 0000000000..40d051e504 --- /dev/null +++ b/src/libslic3r/Arrange/SegmentedRectangleBed.hpp @@ -0,0 +1,105 @@ +#ifndef SEGMENTEDRECTANGLEBED_HPP +#define SEGMENTEDRECTANGLEBED_HPP + +#include "libslic3r/Arrange/Core/Beds.hpp" + +namespace Slic3r { namespace arr2 { + +enum class RectPivots { + Center, BottomLeft, BottomRight, TopLeft, TopRight +}; + +template struct IsSegmentedBed_ : public std::false_type {}; +template constexpr bool IsSegmentedBed = IsSegmentedBed_>::value; + +template +struct SegmentedRectangleBed { + Vec<2, size_t> segments = Vec<2, size_t>::Ones(); + BoundingBox bb; + RectPivots pivot = RectPivots::Center; + + SegmentedRectangleBed() = default; + SegmentedRectangleBed(const BoundingBox &bb, + size_t segments_x, + size_t segments_y, + const RectPivots pivot = RectPivots::Center) + : segments{segments_x, segments_y}, bb{bb}, pivot{pivot} + {} + + size_t segments_x() const noexcept { return segments.x(); } + size_t segments_y() const noexcept { return segments.y(); } + + auto alignment() const noexcept { return pivot; } +}; + +template +struct SegmentedRectangleBed, + std::integral_constant> +{ + BoundingBox bb; + RectPivots pivot = RectPivots::Center; + + SegmentedRectangleBed() = default; + + explicit SegmentedRectangleBed(const BoundingBox &b, + const RectPivots pivot = RectPivots::Center) + : bb{b} + {} + + size_t segments_x() const noexcept { return SegX; } + size_t segments_y() const noexcept { return SegY; } + + auto alignment() const noexcept { return pivot; } +}; + +template +struct SegmentedRectangleBed, + std::integral_constant, + std::integral_constant> +{ + BoundingBox bb; + + SegmentedRectangleBed() = default; + + explicit SegmentedRectangleBed(const BoundingBox &b) : bb{b} {} + + size_t segments_x() const noexcept { return SegX; } + size_t segments_y() const noexcept { return SegY; } + + auto alignment() const noexcept { return pivot; } +}; + +template +struct IsSegmentedBed_> + : public std::true_type {}; + +template +auto offset(const SegmentedRectangleBed &bed, coord_t val_scaled) +{ + auto cpy = bed; + cpy.bb.offset(val_scaled); + + return cpy; +} + +template +auto bounding_box(const SegmentedRectangleBed &bed) +{ + return bed.bb; +} + +template +auto area(const SegmentedRectangleBed &bed) +{ + return arr2::area(bed.bb); +} + +template +ExPolygons to_expolygons(const SegmentedRectangleBed &bed) +{ + return to_expolygons(RectangleBed{bed.bb}); +} + +}} // namespace Slic3r::arr2 + +#endif // SEGMENTEDRECTANGLEBED_HPP diff --git a/src/libslic3r/Arrange/Tasks/ArrangeTask.hpp b/src/libslic3r/Arrange/Tasks/ArrangeTask.hpp new file mode 100644 index 0000000000..1f3b9013d2 --- /dev/null +++ b/src/libslic3r/Arrange/Tasks/ArrangeTask.hpp @@ -0,0 +1,81 @@ +#ifndef ARRANGETASK_HPP +#define ARRANGETASK_HPP + +#include "libslic3r/Arrange/Arrange.hpp" +#include "libslic3r/Arrange/Items/TrafoOnlyArrangeItem.hpp" + +namespace Slic3r { namespace arr2 { + +struct ArrangeTaskResult : public ArrangeResult +{ + std::vector items; + + bool apply_on(ArrangeableModel &mdl) override + { + bool ret = true; + for (auto &itm : items) { + if (is_arranged(itm)) + ret = ret && apply_arrangeitem(itm, mdl); + } + + return ret; + } + + template + void add_item(const ArrItem &itm) + { + items.emplace_back(itm); + if (auto id = retrieve_id(itm)) + imbue_id(items.back(), *id); + } + + template + void add_items(const Range &items_range) + { + for (auto &itm : items_range) + add_item(itm); + } +}; + +template struct ArrangeTask : public ArrangeTaskBase +{ + struct ArrangeSet + { + std::vector selected, unselected; + } printable, unprintable; + + ExtendedBed bed; + ArrangeSettings settings; + + static std::unique_ptr create( + const Scene &sc, + const ArrangeableToItemConverter &converter); + + static std::unique_ptr create(const Scene &sc) + { + auto conv = ArrangeableToItemConverter::create(sc); + return create(sc, *conv); + } + + std::unique_ptr process(Ctl &ctl) override + { + return process_native(ctl); + } + + std::unique_ptr process_native(Ctl &ctl); + std::unique_ptr process_native(Ctl &&ctl) + { + return process_native(ctl); + } + + int item_count_to_process() const override + { + return static_cast(printable.selected.size() + + unprintable.selected.size()); + } +}; + +} // namespace arr2 +} // namespace Slic3r + +#endif // ARRANGETASK_HPP diff --git a/src/libslic3r/Arrange/Tasks/ArrangeTaskImpl.hpp b/src/libslic3r/Arrange/Tasks/ArrangeTaskImpl.hpp new file mode 100644 index 0000000000..d4ff0244d2 --- /dev/null +++ b/src/libslic3r/Arrange/Tasks/ArrangeTaskImpl.hpp @@ -0,0 +1,109 @@ +#ifndef ARRANGETASK_IMPL_HPP +#define ARRANGETASK_IMPL_HPP + +#include + +#include "ArrangeTask.hpp" + +namespace Slic3r { namespace arr2 { + +// Prepare the selected and unselected items separately. If nothing is +// selected, behaves as if everything would be selected. +template +void extract_selected(ArrangeTask &task, + const ArrangeableModel &mdl, + const ArrangeableToItemConverter &itm_conv) +{ + // Go through the objects and check if inside the selection + mdl.for_each_arrangeable( + [&task, &itm_conv](const Arrangeable &arrbl) { + bool selected = arrbl.is_selected(); + bool printable = arrbl.is_printable(); + + auto itm = itm_conv.convert(arrbl, selected ? 0 : -SCALED_EPSILON); + + auto &container_parent = printable ? task.printable : + task.unprintable; + + auto &container = selected ? + container_parent.selected : + container_parent.unselected; + + container.emplace_back(std::move(itm)); + }); + + // If the selection was empty arrange everything + if (task.printable.selected.empty() && task.unprintable.selected.empty()) { + task.printable.selected.swap(task.printable.unselected); + task.unprintable.selected.swap(task.unprintable.unselected); + } +} + +template +std::unique_ptr> ArrangeTask::create( + const Scene &sc, const ArrangeableToItemConverter &converter) +{ + auto task = std::make_unique>(); + + task->settings.set_from(sc.settings()); + + task->bed = sc.bed(); + + extract_selected(*task, sc.model(), converter); + + return task; +} + +template +std::unique_ptr +ArrangeTask::process_native(Ctl &ctl) +{ + auto result = std::make_unique(); + + auto arranger = Arranger::create(settings); + + class TwoStepArrangeCtl: public Ctl + { + Ctl &parent; + ArrangeTask &self; + public: + TwoStepArrangeCtl(Ctl &p, ArrangeTask &slf) : parent{p}, self{slf} {} + + void update_status(int remaining) override + { + parent.update_status(remaining + self.unprintable.selected.size()); + } + + bool was_canceled() const override { return parent.was_canceled(); } + + } subctl{ctl, *this}; + + arranger->arrange(printable.selected, printable.unselected, bed, subctl); + arranger->arrange(unprintable.selected, unprintable.unselected, bed, ctl); + + // Unprintable items should go to the first bed not containing any printable + // items + auto beds = std::max(get_bed_count(crange(printable.selected)), + get_bed_count(crange(printable.unselected))); + + // If there are no printables, leave the physical bed empty + beds = std::max(beds, size_t{1}); + + result->add_items(crange(printable.selected)); + + for (auto &itm : unprintable.selected) { + if (is_arranged(itm)) { + int bedidx = get_bed_index(itm) + beds; + arr2::set_bed_index(itm, bedidx); + } + + result->add_item(itm); + } + + return result; +} + +} // namespace arr2 +} // namespace Slic3r + +#endif //ARRANGETASK_IMPL_HPP diff --git a/src/libslic3r/Arrange/Tasks/FillBedTask.hpp b/src/libslic3r/Arrange/Tasks/FillBedTask.hpp new file mode 100644 index 0000000000..2931d85fc5 --- /dev/null +++ b/src/libslic3r/Arrange/Tasks/FillBedTask.hpp @@ -0,0 +1,53 @@ +#ifndef FILLBEDTASK_HPP +#define FILLBEDTASK_HPP + +#include "MultiplySelectionTask.hpp" + +#include "libslic3r/Arrange/Arrange.hpp" + +namespace Slic3r { namespace arr2 { + +struct FillBedTaskResult: public MultiplySelectionTaskResult {}; + +template +struct FillBedTask: public ArrangeTaskBase +{ + std::optional prototype_item; + + std::vector selected, unselected; + + ArrangeSettings settings; + ExtendedBed bed; + size_t selected_existing_count = 0; + + std::unique_ptr process_native(Ctl &ctl); + std::unique_ptr process_native(Ctl &&ctl) + { + return process_native(ctl); + } + + std::unique_ptr process(Ctl &ctl) override + { + return process_native(ctl); + } + + int item_count_to_process() const override + { + return selected.size(); + } + + static std::unique_ptr create( + const Scene &sc, + const ArrangeableToItemConverter &converter); + + static std::unique_ptr create(const Scene &sc) + { + auto conv = ArrangeableToItemConverter::create(sc); + return create(sc, *conv); + } +}; + +} // namespace arr2 +} // namespace Slic3r + +#endif // FILLBEDTASK_HPP diff --git a/src/libslic3r/Arrange/Tasks/FillBedTaskImpl.hpp b/src/libslic3r/Arrange/Tasks/FillBedTaskImpl.hpp new file mode 100644 index 0000000000..9c76a35c81 --- /dev/null +++ b/src/libslic3r/Arrange/Tasks/FillBedTaskImpl.hpp @@ -0,0 +1,173 @@ +#ifndef FILLBEDTASKIMPL_HPP +#define FILLBEDTASKIMPL_HPP + +#include "FillBedTask.hpp" + +#include "Arrange/Core/NFP/NFPArrangeItemTraits.hpp" + +namespace Slic3r { namespace arr2 { + +template +int calculate_items_needed_to_fill_bed(const ExtendedBed &bed, + const ArrItem &prototype_item, + size_t prototype_count, + const std::vector &fixed) +{ + double poly_area = fixed_area(prototype_item); + + auto area_sum_fn = [](double s, const auto &itm) { + return s + (get_bed_index(itm) == 0) * fixed_area(itm); + }; + + double unsel_area = std::accumulate(fixed.begin(), + fixed.end(), + 0., + area_sum_fn); + + double fixed_area = unsel_area + prototype_count * poly_area; + double bed_area = 0.; + + visit_bed([&bed_area] (auto &realbed) { bed_area = area(realbed); }, bed); + + // This is the maximum number of items, + // the real number will always be close but less. + auto needed_items = static_cast( + std::ceil((bed_area - fixed_area) / poly_area)); + + return needed_items; +} + +template +void extract(FillBedTask &task, + const Scene &scene, + const ArrangeableToItemConverter &itm_conv) +{ + task.prototype_item = {}; + + auto selected_ids = scene.selected_ids(); + + if (selected_ids.empty()) + return; + + std::set selected_objects = selected_geometry_ids(scene); + + if (selected_objects.size() != 1) + return; + + ObjectID prototype_geometry_id = *(selected_objects.begin()); + + auto set_prototype_item = [&task, &itm_conv](const Arrangeable &arrbl) { + if (arrbl.is_printable()) + task.prototype_item = itm_conv.convert(arrbl); + }; + + scene.model().visit_arrangeable(selected_ids.front(), set_prototype_item); + + if (!task.prototype_item) + return; + + set_bed_index(*task.prototype_item, Unarranged); + + auto collect_task_items = [&prototype_geometry_id, &task, + &itm_conv](const Arrangeable &arrbl) { + if (arrbl.geometry_id() == prototype_geometry_id) { + if (arrbl.is_printable()) { + auto itm = itm_conv.convert(arrbl); + raise_priority(itm); + task.selected.emplace_back(std::move(itm)); + } + } else { + auto itm = itm_conv.convert(arrbl, -SCALED_EPSILON); + task.unselected.emplace_back(std::move(itm)); + } + }; + + scene.model().for_each_arrangeable(collect_task_items); + + int needed_items = calculate_items_needed_to_fill_bed(task.bed, + *task.prototype_item, + task.selected.size(), + task.unselected); + + task.selected_existing_count = task.selected.size(); + task.selected.reserve(task.selected.size() + needed_items); + std::fill_n(std::back_inserter(task.selected), needed_items, + *task.prototype_item); +} + + +template +std::unique_ptr> FillBedTask::create( + const Scene &sc, const ArrangeableToItemConverter &converter) +{ + auto task = std::make_unique>(); + + task->settings.set_from(sc.settings()); + + task->bed = sc.bed(); + + extract(*task, sc, converter); + + return task; +} + +template +std::unique_ptr FillBedTask::process_native( + Ctl &ctl) +{ + auto result = std::make_unique(); + + if (!prototype_item) + return result; + + result->prototype_id = retrieve_id(*prototype_item).value_or(ObjectID{}); + + class FillBedCtl: public ArrangerCtl + { + ArrangeTaskCtl &parent; + FillBedTask &self; + bool do_stop = false; + + public: + FillBedCtl(ArrangeTaskCtl &p, FillBedTask &slf) : parent{p}, self{slf} {} + + void update_status(int remaining) override + { + parent.update_status(remaining); + } + + bool was_canceled() const override + { + return parent.was_canceled() || do_stop; + } + + void on_packed(ArrItem &itm) override + { + do_stop = get_bed_index(itm) > PhysicalBedId && get_priority(itm) == 0; + } + + } subctl(ctl, *this); + + auto arranger = Arranger::create(settings); + + arranger->arrange(selected, unselected, bed, subctl); + + auto arranged_range = Range{selected.begin(), + selected.begin() + selected_existing_count}; + + result->add_arranged_items(arranged_range); + + auto to_add_range = Range{selected.begin() + selected_existing_count, + selected.end()}; + + for (auto &itm : to_add_range) + if (get_bed_index(itm) == PhysicalBedId) + result->add_new_item(itm); + + return result; +} + +} // namespace arr2 +} // namespace Slic3r + +#endif // FILLBEDTASKIMPL_HPP diff --git a/src/libslic3r/Arrange/Tasks/MultiplySelectionTask.hpp b/src/libslic3r/Arrange/Tasks/MultiplySelectionTask.hpp new file mode 100644 index 0000000000..156ff2f57a --- /dev/null +++ b/src/libslic3r/Arrange/Tasks/MultiplySelectionTask.hpp @@ -0,0 +1,108 @@ +#ifndef MULTIPLYSELECTIONTASK_HPP +#define MULTIPLYSELECTIONTASK_HPP + +#include "libslic3r/Arrange/Arrange.hpp" +#include "libslic3r/Arrange/Items/TrafoOnlyArrangeItem.hpp" + +namespace Slic3r { namespace arr2 { + +struct MultiplySelectionTaskResult: public ArrangeResult { + ObjectID prototype_id; + + std::vector arranged_items; + std::vector to_add; + + bool apply_on(ArrangeableModel &mdl) override + { + bool ret = prototype_id.valid(); + + if (!ret) + return ret; + + for (auto &itm : to_add) { + auto id = mdl.add_arrangeable(prototype_id); + imbue_id(itm, id); + ret = ret && apply_arrangeitem(itm, mdl); + } + + for (auto &itm : arranged_items) { + if (is_arranged(itm)) + ret = ret && apply_arrangeitem(itm, mdl); + } + + return ret; + } + + template + void add_arranged_item(const ArrItem &itm) + { + arranged_items.emplace_back(itm); + if (auto id = retrieve_id(itm)) + imbue_id(arranged_items.back(), *id); + } + + template + void add_arranged_items(const Range &items_range) + { + arranged_items.reserve(items_range.size()); + for (auto &itm : items_range) + add_arranged_item(itm); + } + + template void add_new_item(const ArrItem &itm) + { + to_add.emplace_back(itm); + } + + template void add_new_items(const Range &items_range) + { + to_add.reserve(items_range.size()); + for (auto &itm : items_range) { + to_add.emplace_back(itm); + } + } +}; + +template +struct MultiplySelectionTask: public ArrangeTaskBase +{ + std::optional prototype_item; + + std::vector selected, unselected; + + ArrangeSettings settings; + ExtendedBed bed; + size_t selected_existing_count = 0; + + std::unique_ptr process_native(Ctl &ctl); + std::unique_ptr process_native(Ctl &&ctl) + { + return process_native(ctl); + } + + std::unique_ptr process(Ctl &ctl) override + { + return process_native(ctl); + } + + int item_count_to_process() const override + { + return selected.size(); + } + + static std::unique_ptr create( + const Scene &sc, + size_t multiply_count, + const ArrangeableToItemConverter &converter); + + static std::unique_ptr create(const Scene &sc, + size_t multiply_count) + { + auto conv = ArrangeableToItemConverter::create(sc); + return create(sc, multiply_count, *conv); + } +}; + +}} // namespace Slic3r::arr2 + +#endif // MULTIPLYSELECTIONTASK_HPP diff --git a/src/libslic3r/Arrange/Tasks/MultiplySelectionTaskImpl.hpp b/src/libslic3r/Arrange/Tasks/MultiplySelectionTaskImpl.hpp new file mode 100644 index 0000000000..0abf31376d --- /dev/null +++ b/src/libslic3r/Arrange/Tasks/MultiplySelectionTaskImpl.hpp @@ -0,0 +1,120 @@ +#ifndef MULTIPLYSELECTIONTASKIMPL_HPP +#define MULTIPLYSELECTIONTASKIMPL_HPP + +#include "MultiplySelectionTask.hpp" + +namespace Slic3r { namespace arr2 { + +template +std::unique_ptr> MultiplySelectionTask::create( + const Scene &scene, size_t count, const ArrangeableToItemConverter &itm_conv) +{ + auto task_ptr = std::make_unique>(); + + auto &task = *task_ptr; + + task.settings.set_from(scene.settings()); + + task.bed = scene.bed(); + + task.prototype_item = {}; + + auto selected_ids = scene.selected_ids(); + + if (selected_ids.empty()) + return task_ptr; + + std::set selected_objects = selected_geometry_ids(scene); + + if (selected_objects.size() != 1) + return task_ptr; + + ObjectID prototype_geometry_id = *(selected_objects.begin()); + + auto set_prototype_item = [&task, &itm_conv](const Arrangeable &arrbl) { + if (arrbl.is_printable()) + task.prototype_item = itm_conv.convert(arrbl); + }; + + scene.model().visit_arrangeable(selected_ids.front(), set_prototype_item); + + if (!task.prototype_item) + return task_ptr; + + set_bed_index(*task.prototype_item, Unarranged); + + auto collect_task_items = [&prototype_geometry_id, &task, + &itm_conv](const Arrangeable &arrbl) { + if (arrbl.geometry_id() == prototype_geometry_id) { + if (arrbl.is_printable()) { + auto itm = itm_conv.convert(arrbl); + raise_priority(itm); + task.selected.emplace_back(std::move(itm)); + } + } else { + auto itm = itm_conv.convert(arrbl, -SCALED_EPSILON); + task.unselected.emplace_back(std::move(itm)); + } + }; + + scene.model().for_each_arrangeable(collect_task_items); + + task.selected_existing_count = task.selected.size(); + task.selected.reserve(task.selected.size() + count); + std::fill_n(std::back_inserter(task.selected), count, *task.prototype_item); + + return task_ptr; +} + +template +std::unique_ptr +MultiplySelectionTask::process_native(Ctl &ctl) +{ + auto result = std::make_unique(); + + if (!prototype_item) + return result; + + result->prototype_id = retrieve_id(*prototype_item).value_or(ObjectID{}); + + class MultiplySelectionCtl: public ArrangerCtl + { + ArrangeTaskCtl &parent; + MultiplySelectionTask &self; + + public: + MultiplySelectionCtl(ArrangeTaskCtl &p, MultiplySelectionTask &slf) + : parent{p}, self{slf} {} + + void update_status(int remaining) override + { + parent.update_status(remaining); + } + + bool was_canceled() const override + { + return parent.was_canceled(); + } + + } subctl(ctl, *this); + + auto arranger = Arranger::create(settings); + + arranger->arrange(selected, unselected, bed, subctl); + + auto arranged_range = Range{selected.begin(), + selected.begin() + selected_existing_count}; + + result->add_arranged_items(arranged_range); + + auto to_add_range = Range{selected.begin() + selected_existing_count, + selected.end()}; + + result->add_new_items(to_add_range); + + return result; +} + +}} // namespace Slic3r::arr2 + +#endif // MULTIPLYSELECTIONTASKIMPL_HPP diff --git a/src/libslic3r/BoostAdapter.hpp b/src/libslic3r/BoostAdapter.hpp index 36256213f9..07ab42eefb 100644 --- a/src/libslic3r/BoostAdapter.hpp +++ b/src/libslic3r/BoostAdapter.hpp @@ -3,6 +3,8 @@ #include #include +#include +#include #include @@ -126,13 +128,146 @@ struct indexed_access, 1, d> { } }; -} -} + +/* ************************************************************************** */ +/* Segment concept adaptaion ************************************************ */ +/* ************************************************************************** */ + +template<> struct tag { + using type = segment_tag; +}; + +template<> struct point_type { + using type = Slic3r::Point; +}; + +template<> struct indexed_access { + static inline coord_t get(Slic3r::Line const& l) { return l.a.x(); } + static inline void set(Slic3r::Line &l, coord_t c) { l.a.x() = c; } +}; + +template<> struct indexed_access { + static inline coord_t get(Slic3r::Line const& l) { return l.a.y(); } + static inline void set(Slic3r::Line &l, coord_t c) { l.a.y() = c; } +}; + +template<> struct indexed_access { + static inline coord_t get(Slic3r::Line const& l) { return l.b.x(); } + static inline void set(Slic3r::Line &l, coord_t c) { l.b.x() = c; } +}; + +template<> struct indexed_access { + static inline coord_t get(Slic3r::Line const& l) { return l.b.y(); } + static inline void set(Slic3r::Line &l, coord_t c) { l.b.y() = c; } +}; + +/* ************************************************************************** */ +/* Polyline concept adaptation ********************************************** */ +/* ************************************************************************** */ + +template<> struct tag { + using type = linestring_tag; +}; + +/* ************************************************************************** */ +/* Polygon concept adaptation *********************************************** */ +/* ************************************************************************** */ + +// Ring implementation ///////////////////////////////////////////////////////// + +// Boost would refer to ClipperLib::Path (alias Slic3r::ExPolygon) as a ring +template<> struct tag { + using type = ring_tag; +}; + +template<> struct point_order { + static const order_selector value = counterclockwise; +}; + +// All our Paths should be closed for the bin packing application +template<> struct closure { + static const constexpr closure_selector value = closure_selector::open; +}; + +// Polygon implementation ////////////////////////////////////////////////////// + +template<> struct tag { + using type = polygon_tag; +}; + +template<> struct exterior_ring { + static inline Slic3r::Polygon& get(Slic3r::ExPolygon& p) + { + return p.contour; + } + static inline Slic3r::Polygon const& get(Slic3r::ExPolygon const& p) + { + return p.contour; + } +}; + +template<> struct ring_const_type { + using type = const Slic3r::Polygon&; +}; + +template<> struct ring_mutable_type { + using type = Slic3r::Polygon&; +}; + +template<> struct interior_const_type { + using type = const Slic3r::Polygons&; +}; + +template<> struct interior_mutable_type { + using type = Slic3r::Polygons&; +}; + +template<> +struct interior_rings { + + static inline Slic3r::Polygons& get(Slic3r::ExPolygon& p) { return p.holes; } + + static inline const Slic3r::Polygons& get(Slic3r::ExPolygon const& p) + { + return p.holes; + } +}; + +/* ************************************************************************** */ +/* MultiPolygon concept adaptation ****************************************** */ +/* ************************************************************************** */ + +template<> struct tag { + using type = multi_polygon_tag; +}; + +}} // namespace geometry::traits template<> struct range_value> { using type = Slic3r::Vec2d; }; +template<> +struct range_value { + using type = Slic3r::Point; +}; + +// This is an addition to the ring implementation of Polygon concept +template<> +struct range_value { + using type = Slic3r::Point; +}; + +template<> +struct range_value { + using type = Slic3r::Polygon; +}; + +template<> +struct range_value { + using type = Slic3r::ExPolygon; +}; + } // namespace boost #endif // SLABOOSTADAPTER_HPP diff --git a/src/libslic3r/BoundingBox.hpp b/src/libslic3r/BoundingBox.hpp index fc1b500740..9a4c68a147 100644 --- a/src/libslic3r/BoundingBox.hpp +++ b/src/libslic3r/BoundingBox.hpp @@ -54,8 +54,8 @@ public: return ! (this->max.x() < other.min.x() || this->min.x() > other.max.x() || this->max.y() < other.min.y() || this->min.y() > other.max.y()); } - bool operator==(const BoundingBoxBase &rhs) { return this->min == rhs.min && this->max == rhs.max; } - bool operator!=(const BoundingBoxBase &rhs) { return ! (*this == rhs); } + bool operator==(const BoundingBoxBase &rhs) const noexcept { return this->min == rhs.min && this->max == rhs.max; } + bool operator!=(const BoundingBoxBase &rhs) const noexcept { return ! (*this == rhs); } private: // to access construct() @@ -192,6 +192,7 @@ public: BoundingBox() : BoundingBoxBase() {} BoundingBox(const Point &pmin, const Point &pmax) : BoundingBoxBase(pmin, pmax) {} + BoundingBox(const BoundingBoxBase &bb): BoundingBox(bb.min, bb.max) {} BoundingBox(const Points &points) : BoundingBoxBase(points) {} BoundingBox inflated(coordf_t delta) const throw() { BoundingBox out(*this); out.offset(delta); return out; } @@ -215,6 +216,7 @@ public: BoundingBoxf() : BoundingBoxBase() {} BoundingBoxf(const Vec2d &pmin, const Vec2d &pmax) : BoundingBoxBase(pmin, pmax) {} BoundingBoxf(const std::vector &points) : BoundingBoxBase(points) {} + BoundingBoxf(const BoundingBoxBase &bb): BoundingBoxf{bb.min, bb.max} {} }; class BoundingBoxf3 : public BoundingBox3Base @@ -239,17 +241,23 @@ inline bool empty(const BoundingBox3Base &bb) inline BoundingBox scaled(const BoundingBoxf &bb) { return {scaled(bb.min), scaled(bb.max)}; } -template -BoundingBoxBase> scaled(const BoundingBoxf &bb) { return {scaled(bb.min), scaled(bb.max)}; } +template +BoundingBoxBase> scaled(const BoundingBoxBase> &bb) { return {scaled(bb.min), scaled(bb.max)}; } template -BoundingBox3Base> scaled(const BoundingBoxf3 &bb) { return {scaled(bb.min), scaled(bb.max)}; } +BoundingBoxBase> scaled(const BoundingBox &bb) { return {scaled(bb.min), scaled(bb.max)}; } + +template +BoundingBox3Base> scaled(const BoundingBox3Base> &bb) { return {scaled(bb.min), scaled(bb.max)}; } + +template +BoundingBoxBase> unscaled(const BoundingBoxBase> &bb) { return {unscaled(bb.min), unscaled(bb.max)}; } template BoundingBoxBase> unscaled(const BoundingBox &bb) { return {unscaled(bb.min), unscaled(bb.max)}; } -template -BoundingBox3Base> unscaled(const BoundingBox3 &bb) { return {unscaled(bb.min), unscaled(bb.max)}; } +template +BoundingBox3Base> unscaled(const BoundingBox3Base> &bb) { return {unscaled(bb.min), unscaled(bb.max)}; } template auto cast(const BoundingBoxBase &b) @@ -298,6 +306,19 @@ inline double bbox_point_distance_squared(const BoundingBox &bbox, const Point & coord_t(0)); } +template +BoundingBoxBase> to_2d(const BoundingBox3Base> &bb) +{ + return {to_2d(bb.min), to_2d(bb.max)}; +} + +template +BoundingBoxBase> to_2d(const BoundingBox3Base> &bb) +{ + return {to_2d(bb.min), to_2d(bb.max)}; +} + + } // namespace Slic3r // Serialization through the Cereal library diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index a31e4cc7c8..75cef23e6d 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -216,8 +216,55 @@ set(SLIC3R_SOURCES MeasureUtils.hpp CustomGCode.cpp CustomGCode.hpp - Arrange.hpp - Arrange.cpp + Arrange/Arrange.hpp + Arrange/ArrangeImpl.hpp + Arrange/Items/ArrangeItem.hpp + Arrange/Items/ArrangeItem.cpp + Arrange/Items/SimpleArrangeItem.hpp + Arrange/Items/SimpleArrangeItem.cpp + Arrange/Items/TrafoOnlyArrangeItem.hpp + Arrange/Items/WritableItemTraits.hpp + Arrange/Items/ArbitraryDataStore.hpp + Arrange/ArrangeSettingsView.hpp + Arrange/ArrangeSettingsDb_AppCfg.hpp + Arrange/ArrangeSettingsDb_AppCfg.cpp + Arrange/Scene.hpp + Arrange/Scene.cpp + Arrange/SceneBuilder.hpp + Arrange/SceneBuilder.cpp + Arrange/Tasks/ArrangeTask.hpp + Arrange/Tasks/ArrangeTaskImpl.hpp + Arrange/Tasks/FillBedTask.hpp + Arrange/Tasks/FillBedTaskImpl.hpp + Arrange/Tasks/MultiplySelectionTask.hpp + Arrange/Tasks/MultiplySelectionTaskImpl.hpp + Arrange/SegmentedRectangleBed.hpp + Arrange/Core/ArrangeItemTraits.hpp + Arrange/Core/DataStoreTraits.hpp + Arrange/Core/ArrangeBase.hpp + Arrange/Core/PackingContext.hpp + Arrange/Core/ArrangeFirstFit.hpp + Arrange/Core/Beds.hpp + Arrange/Core/Beds.cpp + Arrange/Core/NFP/NFP.hpp + Arrange/Core/NFP/NFP.cpp + Arrange/Core/NFP/NFPConcave_CGAL.hpp + Arrange/Core/NFP/NFPConcave_CGAL.cpp + Arrange/Core/NFP/NFPConcave_Tesselate.hpp + Arrange/Core/NFP/NFPConcave_Tesselate.cpp + Arrange/Core/NFP/EdgeCache.hpp + Arrange/Core/NFP/EdgeCache.cpp + Arrange/Core/NFP/CircularEdgeIterator.hpp + Arrange/Core/NFP/NFPArrangeItemTraits.hpp + Arrange/Core/NFP/PackStrategyNFP.hpp + Arrange/Core/NFP/RectangleOverfitPackingStrategy.hpp + Arrange/Core/NFP/Kernels/KernelTraits.hpp + Arrange/Core/NFP/Kernels/GravityKernel.hpp + Arrange/Core/NFP/Kernels/TMArrangeKernel.hpp + Arrange/Core/NFP/Kernels/CompactifyKernel.hpp + Arrange/Core/NFP/Kernels/RectangleOverfitKernelWrapper.hpp + Arrange/Core/NFP/Kernels/SVGDebugOutputKernelWrapper.hpp + Arrange/Core/NFP/Kernels/KernelUtils.hpp MultiPoint.cpp MultiPoint.hpp MutablePriorityQueue.hpp @@ -433,6 +480,10 @@ set(SLIC3R_SOURCES add_library(libslic3r STATIC ${SLIC3R_SOURCES}) +if (WIN32) + target_compile_definitions(libslic3r PUBLIC NOMINMAX) +endif() + foreach(_source IN ITEMS ${SLIC3R_SOURCES}) get_filename_component(_source_path "${_source}" PATH) string(REPLACE "/" "\\" _group_path "${_source_path}") diff --git a/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp index 85ef53c888..e942423947 100644 --- a/src/libslic3r/ClipperUtils.cpp +++ b/src/libslic3r/ClipperUtils.cpp @@ -773,8 +773,16 @@ Slic3r::ExPolygons intersection_ex(const Slic3r::SurfacesPtr &subject, const Sli // May be used to "heal" unusual models (3DLabPrints etc.) by providing fill_type (pftEvenOdd, pftNonZero, pftPositive, pftNegative). Slic3r::ExPolygons union_ex(const Slic3r::Polygons &subject, ClipperLib::PolyFillType fill_type) { return _clipper_ex(ClipperLib::ctUnion, ClipperUtils::PolygonsProvider(subject), ClipperUtils::EmptyPathsProvider(), ApplySafetyOffset::No, fill_type); } +Slic3r::ExPolygons union_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &subject2, ClipperLib::PolyFillType fill_type) + { return _clipper_ex(ClipperLib::ctUnion, ClipperUtils::PolygonsProvider(subject), ClipperUtils::PolygonsProvider(subject2), ApplySafetyOffset::No, fill_type); } Slic3r::ExPolygons union_ex(const Slic3r::ExPolygons &subject) { return PolyTreeToExPolygons(clipper_do_polytree(ClipperLib::ctUnion, ClipperUtils::ExPolygonsProvider(subject), ClipperUtils::EmptyPathsProvider(), ClipperLib::pftNonZero)); } +Slic3r::ExPolygons union_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &subject2) + { return PolyTreeToExPolygons(clipper_do_polytree(ClipperLib::ctUnion, ClipperUtils::ExPolygonsProvider(subject), ClipperUtils::ExPolygonsProvider(subject2), ClipperLib::pftNonZero)); } +Slic3r::ExPolygons union_ex(const Slic3r::Polygons &subject, const Slic3r::ExPolygons &subject2) + { return PolyTreeToExPolygons(clipper_do_polytree(ClipperLib::ctUnion, ClipperUtils::PolygonsProvider(subject), ClipperUtils::ExPolygonsProvider(subject2), ClipperLib::pftNonZero)); } +Slic3r::ExPolygons union_ex(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &subject2) + { return PolyTreeToExPolygons(clipper_do_polytree(ClipperLib::ctUnion, ClipperUtils::ExPolygonsProvider(subject), ClipperUtils::PolygonsProvider(subject2), ClipperLib::pftNonZero)); } Slic3r::ExPolygons union_ex(const Slic3r::Surfaces &subject) { return PolyTreeToExPolygons(clipper_do_polytree(ClipperLib::ctUnion, ClipperUtils::SurfacesProvider(subject), ClipperUtils::EmptyPathsProvider(), ClipperLib::pftNonZero)); } diff --git a/src/libslic3r/ClipperUtils.hpp b/src/libslic3r/ClipperUtils.hpp index 774e9cb42f..7935034b7b 100644 --- a/src/libslic3r/ClipperUtils.hpp +++ b/src/libslic3r/ClipperUtils.hpp @@ -498,7 +498,11 @@ Slic3r::Polygons union_(const Slic3r::Polygons &subject, const Slic3r::Polygons Slic3r::Polygons union_(const Slic3r::Polygons &subject, const Slic3r::ExPolygon &subject2); // May be used to "heal" unusual models (3DLabPrints etc.) by providing fill_type (pftEvenOdd, pftNonZero, pftPositive, pftNegative). Slic3r::ExPolygons union_ex(const Slic3r::Polygons &subject, ClipperLib::PolyFillType fill_type = ClipperLib::pftNonZero); +Slic3r::ExPolygons union_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &subject2, ClipperLib::PolyFillType fill_type = ClipperLib::pftNonZero); Slic3r::ExPolygons union_ex(const Slic3r::ExPolygons &subject); +Slic3r::ExPolygons union_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &subject2); +Slic3r::ExPolygons union_ex(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &subject2); +Slic3r::ExPolygons union_ex(const Slic3r::Polygons &subject, const Slic3r::ExPolygons &subject2); Slic3r::ExPolygons union_ex(const Slic3r::Surfaces &subject); // Convert polygons / expolygons into ClipperLib::PolyTree using ClipperLib::pftEvenOdd, thus union will NOT be performed. diff --git a/src/libslic3r/Geometry/ConvexHull.hpp b/src/libslic3r/Geometry/ConvexHull.hpp index eb0be4fe1f..fc5de5349b 100644 --- a/src/libslic3r/Geometry/ConvexHull.hpp +++ b/src/libslic3r/Geometry/ConvexHull.hpp @@ -3,11 +3,10 @@ #include -#include "../Polygon.hpp" +#include "../ExPolygon.hpp" namespace Slic3r { -class ExPolygon; using ExPolygons = std::vector; namespace Geometry { @@ -16,7 +15,9 @@ Pointf3s convex_hull(Pointf3s points); Polygon convex_hull(Points points); Polygon convex_hull(const Polygons &polygons); Polygon convex_hull(const ExPolygons &expolygons); -Polygon convex_hulll(const Polylines &polylines); +Polygon convex_hull(const Polylines &polylines); +inline Polygon convex_hull(const Polygon &poly) { return convex_hull(poly.points); } +inline Polygon convex_hull(const ExPolygon &poly) { return convex_hull(poly.contour.points); } // Returns true if the intersection of the two convex polygons A and B // is not an empty set. diff --git a/src/libslic3r/Line.hpp b/src/libslic3r/Line.hpp index d90757bedd..325828fe20 100644 --- a/src/libslic3r/Line.hpp +++ b/src/libslic3r/Line.hpp @@ -38,6 +38,32 @@ template using Scalar = typename Traits>::Scalar; template auto get_a(L &&l) { return Traits>::get_a(l); } template auto get_b(L &&l) { return Traits>::get_b(l); } +template auto sqlength(L &&l) +{ + return (get_b(l) - get_a(l)).squaredNorm(); +} + +template +auto sqlength(L &&l) +{ + return (get_b(l).template cast() - get_a(l).template cast()).squaredNorm(); +} + +template == 2> > +auto angle_to_x(const L &l) +{ + auto dx = double(get_b(l).x()) - get_a(l).x(); + auto dy = double(get_b(l).y()) - get_a(l).y(); + + double a = std::atan2(dy, dx); + auto s = std::signbit(a); + + if(s) + a += 2. * PI; + + return a; +} + // Distance to the closest point of line. template inline double distance_to_squared(const L &line, const Vec, Scalar> &point, Vec, Scalar> *nearest_point) @@ -162,7 +188,7 @@ public: void translate(double x, double y) { this->translate(Point(x, y)); } void rotate(double angle, const Point ¢er) { this->a.rotate(angle, center); this->b.rotate(angle, center); } void reverse() { std::swap(this->a, this->b); } - double length() const { return (b - a).cast().norm(); } + double length() const { return (b.cast() - a.cast()).norm(); } Point midpoint() const { return (this->a + this->b) / 2; } bool intersection_infinite(const Line &other, Point* point) const; bool operator==(const Line &rhs) const { return this->a == rhs.a && this->b == rhs.b; } diff --git a/src/libslic3r/MinAreaBoundingBox.cpp b/src/libslic3r/MinAreaBoundingBox.cpp index 51fd8a45e7..da27634205 100644 --- a/src/libslic3r/MinAreaBoundingBox.cpp +++ b/src/libslic3r/MinAreaBoundingBox.cpp @@ -1,6 +1,7 @@ #include "MinAreaBoundingBox.hpp" #include +#include #if defined(_MSC_VER) && defined(__clang__) #define BOOST_NO_CXX17_HDR_STRING_VIEW @@ -103,4 +104,16 @@ void remove_collinear_points(ExPolygon &p) { p = libnest2d::removeCollinearPoints(p, Unit(0)); } + +double fit_into_box_rotation(const Polygon &shape, const BoundingBox &bb) +{ + using namespace libnest2d; + + _Box box{{bb.min.x(), bb.min.y()}, {bb.max.x(), bb.max.y()}}; + + return fitIntoBoxRotation, Rational>(shape, + box, + EPSILON); +} + } // namespace Slic3r diff --git a/src/libslic3r/MinAreaBoundingBox.hpp b/src/libslic3r/MinAreaBoundingBox.hpp index 242fc96111..10c71c5f1c 100644 --- a/src/libslic3r/MinAreaBoundingBox.hpp +++ b/src/libslic3r/MinAreaBoundingBox.hpp @@ -50,6 +50,8 @@ public: const Point& axis() const { return m_axis; } }; -} +double fit_into_box_rotation(const Polygon &shape, const BoundingBox &box); + +} // namespace Slic3r #endif // MINAREABOUNDINGBOX_HPP diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 2a8c78729b..bc02e06f67 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1062,7 +1062,8 @@ Polygon ModelObject::convex_hull_2d(const Transform3d& trafo_instance) const tbb::parallel_for(tbb::blocked_range(0, volumes.size()), [&](const tbb::blocked_range& range) { for (size_t i = range.begin(); i < range.end(); ++i) { const ModelVolume* v = volumes[i]; - chs.emplace_back(its_convex_hull_2d_above(v->mesh().its, (trafo_instance * v->get_matrix()).cast(), 0.0f)); + if (v->is_model_part()) + chs.emplace_back(its_convex_hull_2d_above(v->mesh().its, (trafo_instance * v->get_matrix()).cast(), 0.0f)); } }); @@ -1997,38 +1998,6 @@ void ModelInstance::transform_polygon(Polygon* polygon) const polygon->scale(get_scaling_factor(X), get_scaling_factor(Y)); // scale around polygon origin } -arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const -{ -// static const double SIMPLIFY_TOLERANCE_MM = 0.1; - - Polygon p = get_object()->convex_hull_2d(this->get_matrix()); - -// if (!p.points.empty()) { -// Polygons pp{p}; -// pp = p.simplify(scaled(SIMPLIFY_TOLERANCE_MM)); -// if (!pp.empty()) p = pp.front(); -// } - - arrangement::ArrangePolygon ret; - ret.poly.contour = std::move(p); - ret.translation = Vec2crd::Zero(); - ret.rotation = 0.; - - return ret; -} - -void ModelInstance::apply_arrange_result(const Vec2d &offs, double rotation) -{ - // write the transformation data into the model instance - auto trafo = get_transformation().get_matrix(); - auto tr = Transform3d::Identity(); - tr.translate(to_3d(unscaled(offs), 0.)); - trafo = tr * Eigen::AngleAxisd(rotation, Vec3d::UnitZ()) * trafo; - m_transformation.set_matrix(trafo); - - this->object->invalidate_bounding_box(); -} - indexed_triangle_set FacetsAnnotation::get_facets(const ModelVolume& mv, EnforcerBlockerType type) const { TriangleSelector selector(mv.mesh()); diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 221033c521..2df612f03b 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -11,7 +11,6 @@ #include "SLA/SupportPoint.hpp" #include "SLA/Hollowing.hpp" #include "TriangleMesh.hpp" -#include "Arrange.hpp" #include "CustomGCode.hpp" #include "enum_bitmask.hpp" #include "TextConfiguration.hpp" @@ -1155,11 +1154,7 @@ public: bool is_printable() const { return object->printable && printable && (print_volume_state == ModelInstancePVS_Inside); } - // Getting the input polygon for arrange - arrangement::ArrangePolygon get_arrange_polygon() const; - - // Apply the arrange result on the ModelInstance - void apply_arrange_result(const Vec2d& offs, double rotation); + void invalidate_object_bounding_box() { object->invalidate_bounding_box(); } protected: friend class Print; diff --git a/src/libslic3r/ModelArrange.cpp b/src/libslic3r/ModelArrange.cpp index 01a89a8e56..2755102b5a 100644 --- a/src/libslic3r/ModelArrange.cpp +++ b/src/libslic3r/ModelArrange.cpp @@ -1,77 +1,17 @@ #include "ModelArrange.hpp" + +#include + #include #include +#include "Arrange/Core/ArrangeItemTraits.hpp" +#include "Arrange/Items/ArrangeItem.hpp" + #include "MTUtils.hpp" namespace Slic3r { -arrangement::ArrangePolygons get_arrange_polys(const Model &model, ModelInstancePtrs &instances) -{ - size_t count = 0; - for (auto obj : model.objects) count += obj->instances.size(); - - ArrangePolygons input; - input.reserve(count); - instances.clear(); instances.reserve(count); - for (ModelObject *mo : model.objects) - for (ModelInstance *minst : mo->instances) { - input.emplace_back(minst->get_arrange_polygon()); - instances.emplace_back(minst); - } - - return input; -} - -bool apply_arrange_polys(ArrangePolygons &input, ModelInstancePtrs &instances, VirtualBedFn vfn) -{ - bool ret = true; - - for(size_t i = 0; i < input.size(); ++i) { - if (input[i].bed_idx != 0) { ret = false; if (vfn) vfn(input[i]); } - if (input[i].bed_idx >= 0) - instances[i]->apply_arrange_result(input[i].translation.cast(), - input[i].rotation); - } - - return ret; -} - -Slic3r::arrangement::ArrangePolygon get_arrange_poly(const Model &model) -{ - ArrangePolygon ap; - Points &apts = ap.poly.contour.points; - for (const ModelObject *mo : model.objects) - for (const ModelInstance *minst : mo->instances) { - ArrangePolygon obj_ap = minst->get_arrange_polygon(); - ap.poly.contour.rotate(obj_ap.rotation); - ap.poly.contour.translate(obj_ap.translation.x(), obj_ap.translation.y()); - const Points &pts = obj_ap.poly.contour.points; - std::copy(pts.begin(), pts.end(), std::back_inserter(apts)); - } - - apts = std::move(Geometry::convex_hull(apts).points); - return ap; -} - -void duplicate(Model &model, Slic3r::arrangement::ArrangePolygons &copies, VirtualBedFn vfn) -{ - for (ModelObject *o : model.objects) { - // make a copy of the pointers in order to avoid recursion when appending their copies - ModelInstancePtrs instances = o->instances; - o->instances.clear(); - for (const ModelInstance *i : instances) { - for (arrangement::ArrangePolygon &ap : copies) { - if (ap.bed_idx != 0) vfn(ap); - ModelInstance *instance = o->add_instance(*i); - Vec2d pos = unscale(ap.translation); - instance->set_offset(instance->get_offset() + to_3d(pos, 0.)); - } - } - o->invalidate_bounding_box(); - } -} - void duplicate_objects(Model &model, size_t copies_num) { for (ModelObject *o : model.objects) { @@ -83,4 +23,49 @@ void duplicate_objects(Model &model, size_t copies_num) } } +bool arrange_objects(Model &model, + const arr2::ArrangeBed &bed, + const arr2::ArrangeSettingsView &settings) +{ + arr2::Scene scene{arr2::SceneBuilder{} + .set_bed(bed) + .set_arrange_settings(settings) + .set_model(model)}; + + auto task = arr2::ArrangeTaskBase::create(arr2::Tasks::Arrange, scene); + auto result = task->process(); + return result->apply_on(scene.model()); +} + +void duplicate_objects(Model &model, + size_t copies_num, + const arr2::ArrangeBed &bed, + const arr2::ArrangeSettingsView &settings) +{ + duplicate_objects(model, copies_num); + arrange_objects(model, bed, settings); +} + +void duplicate(Model &model, + size_t copies_num, + const arr2::ArrangeBed &bed, + const arr2::ArrangeSettingsView &settings) +{ + auto vbh = arr2::VirtualBedHandler::create(arr2::to_extended_bed(bed)); + arr2::DuplicableModel dup_model{&model, std::move(vbh), bounding_box(bed)}; + + arr2::Scene scene{arr2::BasicSceneBuilder{} + .set_arrangeable_model(&dup_model) + .set_arrange_settings(&settings) + .set_bed(bed)}; + + if (copies_num >= 1) + copies_num -= 1; + + auto task = arr2::MultiplySelectionTask::create(scene, copies_num); + auto result = task->process_native(arr2::DummyCtl{}); + if (result->apply_on(scene.model())) + dup_model.apply_duplicates(); +} + } // namespace Slic3r diff --git a/src/libslic3r/ModelArrange.hpp b/src/libslic3r/ModelArrange.hpp index 124c5c018f..420f102fd9 100644 --- a/src/libslic3r/ModelArrange.hpp +++ b/src/libslic3r/ModelArrange.hpp @@ -1,7 +1,7 @@ #ifndef MODELARRANGE_HPP #define MODELARRANGE_HPP -#include +#include namespace Slic3r { @@ -9,63 +9,23 @@ class Model; class ModelInstance; using ModelInstancePtrs = std::vector; -using arrangement::ArrangePolygon; -using arrangement::ArrangePolygons; -using arrangement::ArrangeParams; -using arrangement::InfiniteBed; -using arrangement::CircleBed; - -// Do something with ArrangePolygons in virtual beds -using VirtualBedFn = std::function; - -[[noreturn]] inline void throw_if_out_of_bed(arrangement::ArrangePolygon&) -{ - throw Slic3r::RuntimeError("Objects could not fit on the bed"); -} - -ArrangePolygons get_arrange_polys(const Model &model, ModelInstancePtrs &instances); -ArrangePolygon get_arrange_poly(const Model &model); -bool apply_arrange_polys(ArrangePolygons &polys, ModelInstancePtrs &instances, VirtualBedFn); - -void duplicate(Model &model, ArrangePolygons &copies, VirtualBedFn); +//void duplicate(Model &model, ArrangePolygons &copies, VirtualBedFn); void duplicate_objects(Model &model, size_t copies_num); -template -bool arrange_objects(Model & model, - const TBed & bed, - const ArrangeParams ¶ms, - VirtualBedFn vfn = throw_if_out_of_bed) -{ - ModelInstancePtrs instances; - auto&& input = get_arrange_polys(model, instances); - arrangement::arrange(input, bed, params); - - return apply_arrange_polys(input, instances, vfn); -} +bool arrange_objects(Model &model, + const arr2::ArrangeBed &bed, + const arr2::ArrangeSettingsView &settings); -template -void duplicate(Model & model, - size_t copies_num, - const TBed & bed, - const ArrangeParams ¶ms, - VirtualBedFn vfn = throw_if_out_of_bed) -{ - ArrangePolygons copies(copies_num, get_arrange_poly(model)); - arrangement::arrange(copies, bed, params); - duplicate(model, copies, vfn); -} - -template void duplicate_objects(Model & model, size_t copies_num, - const TBed & bed, - const ArrangeParams ¶ms, - VirtualBedFn vfn = throw_if_out_of_bed) -{ - duplicate_objects(model, copies_num); - arrange_objects(model, bed, params, vfn); -} + const arr2::ArrangeBed &bed, + const arr2::ArrangeSettingsView &settings); -} +void duplicate(Model & model, + size_t copies_num, + const arr2::ArrangeBed &bed, + const arr2::ArrangeSettingsView &settings); + +} // namespace Slic3r #endif // MODELARRANGE_HPP diff --git a/src/libslic3r/MultiPoint.hpp b/src/libslic3r/MultiPoint.hpp index 62b53255b4..637b059220 100644 --- a/src/libslic3r/MultiPoint.hpp +++ b/src/libslic3r/MultiPoint.hpp @@ -90,6 +90,12 @@ public: inline auto end() const { return points.end(); } inline auto cbegin() const { return points.begin(); } inline auto cend() const { return points.end(); } + inline auto rbegin() { return points.rbegin(); } + inline auto rbegin() const { return points.rbegin(); } + inline auto rend() { return points.rend(); } + inline auto rend() const { return points.rend(); } + inline auto crbegin()const { return points.crbegin(); } + inline auto crend() const { return points.crend(); } }; class MultiPoint3 diff --git a/src/libslic3r/Optimize/NLoptOptimizer.hpp b/src/libslic3r/Optimize/NLoptOptimizer.hpp index 9e423ff919..d6cea718b3 100644 --- a/src/libslic3r/Optimize/NLoptOptimizer.hpp +++ b/src/libslic3r/Optimize/NLoptOptimizer.hpp @@ -13,6 +13,8 @@ #include +#include + #include "Optimizer.hpp" namespace Slic3r { namespace opt { @@ -104,29 +106,6 @@ struct NLoptRAII { // Helper RAII class for nlopt_opt ~NLoptRAII() { nlopt_destroy(ptr); } }; -// Map a generic function to each argument following the mapping function -template -Fn for_each_argument(Fn &&fn, Args&&...args) -{ - // see https://www.fluentcpp.com/2019/03/05/for_each_arg-applying-a-function-to-each-argument-of-a-function-in-cpp/ - (fn(std::forward(args)),...); - - return fn; -} - -// Call fn on each element of the input tuple tup. -template -Fn for_each_in_tuple(Fn fn, Tup &&tup) -{ - auto mpfn = [&fn](auto&...pack) { - for_each_argument(fn, pack...); - }; - - std::apply(mpfn, tup); - - return fn; -} - // Wrap each element of the tuple tup into a wrapper class W and return // a new tuple with each element being of type W where T_i is the type of // i-th element of tup. diff --git a/src/libslic3r/Optimize/Optimizer.hpp b/src/libslic3r/Optimize/Optimizer.hpp index 6212a5f59d..a82d15d42e 100644 --- a/src/libslic3r/Optimize/Optimizer.hpp +++ b/src/libslic3r/Optimize/Optimizer.hpp @@ -1,5 +1,5 @@ -#ifndef OPTIMIZER_HPP -#define OPTIMIZER_HPP +#ifndef PRUSASLICER_OPTIMIZER_HPP +#define PRUSASLICER_OPTIMIZER_HPP #include #include @@ -10,6 +10,11 @@ #include #include +#ifdef WIN32 +#undef min +#undef max +#endif + namespace Slic3r { namespace opt { template diff --git a/src/libslic3r/Polygon.hpp b/src/libslic3r/Polygon.hpp index e0c3958fd9..f1e72bbe2c 100644 --- a/src/libslic3r/Polygon.hpp +++ b/src/libslic3r/Polygon.hpp @@ -268,6 +268,12 @@ bool polygons_match(const Polygon &l, const Polygon &r); Polygon make_circle(double radius, double error); Polygon make_circle_num_segments(double radius, size_t num_segments); +// To replace reserve_vector where it's used for Polygons +template IntegerOnly reserve_polygons(I cap) +{ + return reserve_vector(cap); +} + } // Slic3r // start Boost diff --git a/src/libslic3r/Polyline.hpp b/src/libslic3r/Polyline.hpp index 703e50cfa2..0e6dcff041 100644 --- a/src/libslic3r/Polyline.hpp +++ b/src/libslic3r/Polyline.hpp @@ -78,6 +78,9 @@ public: void split_at(const Point &point, Polyline* p1, Polyline* p2) const; bool is_straight() const; bool is_closed() const { return this->points.front() == this->points.back(); } + + using iterator = Points::iterator; + using const_iterator = Points::const_iterator; }; inline bool operator==(const Polyline &lhs, const Polyline &rhs) { return lhs.points == rhs.points; } diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 3da804066b..f09a4493d6 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -4651,9 +4651,11 @@ std::string validate(const FullPrintConfig &cfg) BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CACHE_ELEMENT_DEFINITION, _, BOOST_PP_TUPLE_TO_SEQ(CLASSES_SEQ)) \ int print_config_static_initializer() { \ /* Putting a trace here to avoid the compiler to optimize out this function. */ \ - BOOST_LOG_TRIVIAL(trace) << "Initializing StaticPrintConfigs"; \ + /*BOOST_LOG_TRIVIAL(trace) << "Initializing StaticPrintConfigs";*/ \ + /* Tamas: alternative solution through a static volatile int. Boost log pollutes stdout and prevents tests from generating clean output */ \ + static volatile int ret = 1; \ BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CACHE_ELEMENT_INITIALIZATION, _, BOOST_PP_TUPLE_TO_SEQ(CLASSES_SEQ)) \ - return 1; \ + return ret; \ } PRINT_CONFIG_CACHE_INITIALIZE(( PrintObjectConfig, PrintRegionConfig, MachineEnvelopeConfig, GCodeConfig, PrintConfig, FullPrintConfig, @@ -4949,15 +4951,6 @@ Points get_bed_shape(const DynamicPrintConfig &config) return to_points(bed_shape_opt->values); } -void get_bed_shape(const DynamicPrintConfig &cfg, arrangement::ArrangeBed &out) -{ - if (is_XL_printer(cfg)) { - out = arrangement::SegmentedRectangleBed{get_extents(get_bed_shape(cfg)), 4, 4}; - } else { - out = arrangement::to_arrange_bed(get_bed_shape(cfg)); - } -} - Points get_bed_shape(const PrintConfig &cfg) { return to_points(cfg.bed_shape.values); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 54a835fe79..78c0ca03b4 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -19,7 +19,6 @@ #include "libslic3r.h" #include "Config.hpp" #include "SLA/SupportTreeStrategies.hpp" -#include "libslic3r/Arrange.hpp" #include #include @@ -1200,8 +1199,6 @@ Points get_bed_shape(const DynamicPrintConfig &cfg); Points get_bed_shape(const PrintConfig &cfg); Points get_bed_shape(const SLAPrinterConfig &cfg); -void get_bed_shape(const DynamicPrintConfig &cfg, arrangement::ArrangeBed &out); - std::string get_sla_suptree_prefix(const DynamicPrintConfig &config); // ModelConfig is a wrapper around DynamicPrintConfig with an addition of a timestamp. diff --git a/src/libslic3r/libslic3r.h b/src/libslic3r/libslic3r.h index 83089fefee..9377a35d8d 100644 --- a/src/libslic3r/libslic3r.h +++ b/src/libslic3r/libslic3r.h @@ -321,7 +321,8 @@ template // Arbitrary allocator can be used IntegerOnly> reserve_vector(I capacity) { std::vector ret; - if (capacity > I(0)) ret.reserve(size_t(capacity)); + if (capacity > I(0)) + ret.reserve(size_t(capacity)); return ret; } @@ -330,6 +331,18 @@ IntegerOnly> reserve_vector(I capacity) template using remove_cvref_t = std::remove_cv_t>; +namespace detail_strip_ref_wrappers { +template struct StripCVRef_ { using type = remove_cvref_t; }; +template struct StripCVRef_> +{ + using type = std::remove_cv_t; +}; +} // namespace detail + +// Removes reference wrappers as well +template using StripCVRef = + typename detail_strip_ref_wrappers::StripCVRef_>::type; + // A very simple range concept implementation with iterator-like objects. // This should be replaced by std::ranges::subrange (C++20) template class Range @@ -358,6 +371,48 @@ template auto range(Cont &&cont) return Range{std::begin(cont), std::end(cont)}; } +template auto crange(Cont &&cont) +{ + return Range{std::cbegin(cont), std::cend(cont)}; +} + +template> +class IntIterator { + IntType m_val; +public: + using iterator_category = std::bidirectional_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = IntType; + using pointer = IntType*; // or also value_type* + using reference = IntType&; // or also value_type& + + IntIterator(IntType v): m_val{v} {} + + IntIterator & operator++() { ++m_val; return *this; } + IntIterator operator++(int) { auto cpy = *this; ++m_val; return cpy; } + IntIterator & operator--() { --m_val; return *this; } + IntIterator operator--(int) { auto cpy = *this; --m_val; return cpy; } + + IntType operator*() const { return m_val; } + IntType operator->() const { return m_val; } + + bool operator==(const IntIterator& other) const + { + return m_val == other.m_val; + } + + bool operator!=(const IntIterator& other) const + { + return !(*this == other); + } +}; + +template> +auto range(IntType from, IntType to) +{ + return Range{IntIterator{from}, IntIterator{to}}; +} + template> constexpr T NaN = std::numeric_limits::quiet_NaN(); @@ -385,6 +440,32 @@ inline IntegerOnly fast_round_up(double a) template using SamePair = std::pair; +// Helper to be used in static_assert. +template struct always_false { enum { value = false }; }; + +// Map a generic function to each argument following the mapping function +template +Fn for_each_argument(Fn &&fn, Args&&...args) +{ + // see https://www.fluentcpp.com/2019/03/05/for_each_arg-applying-a-function-to-each-argument-of-a-function-in-cpp/ + (fn(std::forward(args)),...); + + return fn; +} + +// Call fn on each element of the input tuple tup. +template +Fn for_each_in_tuple(Fn fn, Tup &&tup) +{ + auto mpfn = [&fn](auto&...pack) { + for_each_argument(fn, pack...); + }; + + std::apply(mpfn, tup); + + return fn; +} + } // namespace Slic3r #endif // _libslic3r_h_ diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 5d7b2c0ef7..c5d9cbb075 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -8,6 +8,8 @@ set(SLIC3R_GUI_SOURCES pchheader.hpp GUI/AboutDialog.cpp GUI/AboutDialog.hpp + GUI/ArrangeSettingsDialogImgui.hpp + GUI/ArrangeSettingsDialogImgui.cpp GUI/SysInfoDialog.cpp GUI/SysInfoDialog.hpp GUI/KBShortcutsDialog.cpp @@ -194,8 +196,8 @@ set(SLIC3R_GUI_SOURCES GUI/Jobs/BusyCursorJob.hpp GUI/Jobs/CancellableJob.hpp GUI/Jobs/PlaterWorker.hpp - GUI/Jobs/ArrangeJob.hpp - GUI/Jobs/ArrangeJob.cpp + GUI/Jobs/ArrangeJob2.hpp + GUI/Jobs/ArrangeJob2.cpp GUI/Jobs/CreateFontNameImageJob.cpp GUI/Jobs/CreateFontNameImageJob.hpp GUI/Jobs/CreateFontStyleImagesJob.cpp @@ -204,8 +206,6 @@ set(SLIC3R_GUI_SOURCES GUI/Jobs/EmbossJob.hpp GUI/Jobs/RotoptimizeJob.hpp GUI/Jobs/RotoptimizeJob.cpp - GUI/Jobs/FillBedJob.hpp - GUI/Jobs/FillBedJob.cpp GUI/Jobs/SLAImportJob.hpp GUI/Jobs/SLAImportJob.cpp GUI/Jobs/ProgressIndicator.hpp diff --git a/src/slic3r/GUI/ArrangeSettingsDialogImgui.cpp b/src/slic3r/GUI/ArrangeSettingsDialogImgui.cpp new file mode 100644 index 0000000000..9c75ce1cfa --- /dev/null +++ b/src/slic3r/GUI/ArrangeSettingsDialogImgui.cpp @@ -0,0 +1,134 @@ +#include "ArrangeSettingsDialogImgui.hpp" +#include "I18N.hpp" +#include "slic3r/GUI/format.hpp" +#include "slic3r/GUI/GUI.hpp" + +namespace Slic3r { namespace GUI { + +struct Settings { + float d_obj; + float d_bed; + bool rotations; + int xl_align; + int geom_handling; + int arr_strategy; +}; + +static void read_settings(Settings &s, const arr2::ArrangeSettingsDb *db) +{ + assert(db); + s.d_obj = db->get_distance_from_objects(); + s.d_bed = db->get_distance_from_bed(); + s.rotations = db->is_rotation_enabled(); + s.xl_align = db->get_xl_alignment(); + s.geom_handling = db->get_geometry_handling(); + s.arr_strategy = db->get_arrange_strategy(); +} + +ArrangeSettingsDialogImgui::ArrangeSettingsDialogImgui( + ImGuiWrapper *imgui, AnyPtr db) + : m_imgui{imgui}, m_db{std::move(db)} +{} + +void ArrangeSettingsDialogImgui::render(float pos_x, float pos_y) +{ + assert(m_imgui && m_db); + + m_imgui->set_next_window_pos(pos_x, pos_y, ImGuiCond_Always, 0.5f, 0.0f); + + m_imgui->begin(_L("Arrange options"), + ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | + ImGuiWindowFlags_NoCollapse); + + Settings settings; + read_settings(settings, m_db.get()); + + m_imgui->text(GUI::format_wxstr( + _L("Press %1%left mouse button to enter the exact value"), + shortkey_ctrl_prefix())); + + float dobj_min, dobj_max; + float dbed_min, dbed_max; + + m_db->distance_from_obj_range(dobj_min, dobj_max); + m_db->distance_from_bed_range(dbed_min, dbed_max); + + if (m_imgui->slider_float(_L("Spacing"), &settings.d_obj, dobj_min, + dobj_max, "%5.2f") || + dobj_min > settings.d_obj) { + settings.d_obj = std::max(dobj_min, settings.d_obj); + m_db->set_distance_from_objects(settings.d_obj); + } + + if (m_imgui->slider_float(_L("Spacing from bed"), &settings.d_bed, + dbed_min, dbed_max, "%5.2f") || + dbed_min > settings.d_bed) { + settings.d_bed = std::max(dbed_min, settings.d_bed); + m_db->set_distance_from_bed(settings.d_bed); + } + + if (m_imgui->checkbox(_L("Enable rotations (slow)"), settings.rotations)) { + m_db->set_rotation_enabled(settings.rotations); + } + +// Points bed = m_config ? get_bed_shape(*m_config) : Points{}; + if (/*arrangement::is_box(bed) */ m_show_xl_combo_predicate() && + settings.xl_align >= 0 && + m_imgui->combo(_L("Alignment"), + {_u8L("Center"), _u8L("Rear left"), _u8L("Front left"), + _u8L("Front right"), _u8L("Rear right"), + _u8L("Random")}, + settings.xl_align)) { + if (settings.xl_align >= 0 && + settings.xl_align < ArrangeSettingsView::xlpCount) + m_db->set_xl_alignment(static_cast( + settings.xl_align)); + } + + if (m_imgui->combo(_L("Geometry handling"), + {_u8L("Fast"), _u8L("Balanced"), _u8L("Full complexity")}, + settings.geom_handling)) { + if (settings.geom_handling >= 0 && + settings.geom_handling < ArrangeSettingsView::ghCount) + m_db->set_geometry_handling( + static_cast( + settings.geom_handling)); + } + + if (m_imgui->combo(_L("Strategy"), + {_u8L("Automatic"), _u8L("Pull to center")}, + settings.arr_strategy)) { + if (settings.arr_strategy >= 0 && + settings.arr_strategy < ArrangeSettingsView::asCount) + m_db->set_arrange_strategy( + static_cast( + settings.arr_strategy)); + } + + ImGui::Separator(); + + if (m_imgui->button(_L("Reset defaults"))) { + arr2::ArrangeSettingsDb::Values df = m_db->get_defaults(); + m_db->set_distance_from_objects(df.d_obj); + m_db->set_distance_from_bed(df.d_bed); + m_db->set_rotation_enabled(df.rotations); + if (m_show_xl_combo_predicate()) + m_db->set_xl_alignment(df.xl_align); + + m_db->set_geometry_handling(df.geom_handling); + m_db->set_arrange_strategy(df.arr_strategy); + + if (m_on_reset_btn) + m_on_reset_btn(); + } + + ImGui::SameLine(); + + if (m_imgui->button(_L("Arrange")) && m_on_arrange_btn) { + m_on_arrange_btn(); + } + + m_imgui->end(); +} + +}} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/ArrangeSettingsDialogImgui.hpp b/src/slic3r/GUI/ArrangeSettingsDialogImgui.hpp new file mode 100644 index 0000000000..2c46a0732b --- /dev/null +++ b/src/slic3r/GUI/ArrangeSettingsDialogImgui.hpp @@ -0,0 +1,53 @@ +#ifndef ARRANGESETTINGSDIALOGIMGUI_HPP +#define ARRANGESETTINGSDIALOGIMGUI_HPP + +#include "libslic3r/Arrange/ArrangeSettingsView.hpp" +#include "ImGuiWrapper.hpp" +#include "libslic3r/AnyPtr.hpp" + +namespace Slic3r { +namespace GUI { + +class ArrangeSettingsDialogImgui: public arr2::ArrangeSettingsView { + ImGuiWrapper *m_imgui; + AnyPtr m_db; + + std::function m_on_arrange_btn; + std::function m_on_reset_btn; + + std::function m_show_xl_combo_predicate = [] { return true; }; + +public: + ArrangeSettingsDialogImgui(ImGuiWrapper *imgui, AnyPtr db); + + void render(float pos_x, float pos_y); + + void show_xl_align_combo(std::function pred) + { + m_show_xl_combo_predicate = pred; + } + + void on_arrange_btn(std::function on_arrangefn) + { + m_on_arrange_btn = on_arrangefn; + } + + void on_reset_btn(std::function on_resetfn) + { + m_on_reset_btn = on_resetfn; + } + + // ArrangeSettingsView iface: + + float get_distance_from_objects() const override { return m_db->get_distance_from_objects(); } + float get_distance_from_bed() const override { return m_db->get_distance_from_bed(); } + bool is_rotation_enabled() const override { return m_db->is_rotation_enabled(); } + + XLPivots get_xl_alignment() const override { return m_db->get_xl_alignment(); } + GeometryHandling get_geometry_handling() const override { return m_db->get_geometry_handling(); } + ArrangeStrategy get_arrange_strategy() const override { return m_db->get_arrange_strategy(); } +}; + +}} // namespace Slic3r::GUI + +#endif // ARRANGESETTINGSDIALOGIMGUI_HPP diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 8bdc3bfa3a..fb2be672df 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -35,6 +35,7 @@ #include "slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp" #include "slic3r/Utils/UndoRedo.hpp" +#include "libslic3r/Arrange/ArrangeSettingsDb_AppCfg.hpp" #if ENABLE_RETINA_GL #include "slic3r/Utils/RetinaHelper.hpp" @@ -1045,94 +1046,6 @@ wxDEFINE_EVENT(EVT_GLCANVAS_GIZMO_HIGHLIGHTER_TIMER, wxTimerEvent); const double GLCanvas3D::DefaultCameraZoomToBoxMarginFactor = 1.25; -void GLCanvas3D::load_arrange_settings() -{ - std::string dist_fff_str = - wxGetApp().app_config->get("arrange", "min_object_distance_fff"); - - std::string dist_bed_fff_str = - wxGetApp().app_config->get("arrange", "min_bed_distance_fff"); - - std::string dist_fff_seq_print_str = - wxGetApp().app_config->get("arrange", "min_object_distance_fff_seq_print"); - - std::string dist_bed_fff_seq_print_str = - wxGetApp().app_config->get("arrange", "min_bed_distance_fff_seq_print"); - - std::string dist_sla_str = - wxGetApp().app_config->get("arrange", "min_object_distance_sla"); - - std::string dist_bed_sla_str = - wxGetApp().app_config->get("arrange", "min_bed_distance_sla"); - - std::string en_rot_fff_str = - wxGetApp().app_config->get("arrange", "enable_rotation_fff"); - - std::string en_rot_fff_seqp_str = - wxGetApp().app_config->get("arrange", "enable_rotation_fff_seq_print"); - - std::string en_rot_sla_str = - wxGetApp().app_config->get("arrange", "enable_rotation_sla"); - -// std::string alignment_fff_str = -// wxGetApp().app_config->get("arrange", "alignment_fff"); - -// std::string alignment_fff_seqp_str = -// wxGetApp().app_config->get("arrange", "alignment_fff_seq_pring"); - -// std::string alignment_sla_str = -// wxGetApp().app_config->get("arrange", "alignment_sla"); - - // Override default alignment and save save/load it to a temporary slot "alignment_xl" - std::string alignment_xl_str = - wxGetApp().app_config->get("arrange", "alignment_xl"); - - if (!dist_fff_str.empty()) - m_arrange_settings_fff.distance = string_to_float_decimal_point(dist_fff_str); - - if (!dist_bed_fff_str.empty()) - m_arrange_settings_fff.distance_from_bed = string_to_float_decimal_point(dist_bed_fff_str); - - if (!dist_fff_seq_print_str.empty()) - m_arrange_settings_fff_seq_print.distance = string_to_float_decimal_point(dist_fff_seq_print_str); - - if (!dist_bed_fff_seq_print_str.empty()) - m_arrange_settings_fff_seq_print.distance_from_bed = string_to_float_decimal_point(dist_bed_fff_seq_print_str); - - if (!dist_sla_str.empty()) - m_arrange_settings_sla.distance = string_to_float_decimal_point(dist_sla_str); - - if (!dist_bed_sla_str.empty()) - m_arrange_settings_sla.distance_from_bed = string_to_float_decimal_point(dist_bed_sla_str); - - if (!en_rot_fff_str.empty()) - m_arrange_settings_fff.enable_rotation = (en_rot_fff_str == "1" || en_rot_fff_str == "yes"); - - if (!en_rot_fff_seqp_str.empty()) - m_arrange_settings_fff_seq_print.enable_rotation = (en_rot_fff_seqp_str == "1" || en_rot_fff_seqp_str == "yes"); - - if (!en_rot_sla_str.empty()) - m_arrange_settings_sla.enable_rotation = (en_rot_sla_str == "1" || en_rot_sla_str == "yes"); - -// if (!alignment_sla_str.empty()) -// m_arrange_settings_sla.alignment = std::stoi(alignment_sla_str); - -// if (!alignment_fff_str.empty()) -// m_arrange_settings_fff.alignment = std::stoi(alignment_fff_str); - -// if (!alignment_fff_seqp_str.empty()) -// m_arrange_settings_fff_seq_print.alignment = std::stoi(alignment_fff_seqp_str); - - // Override default alignment and save save/load it to a temporary slot "alignment_xl" - int arr_alignment = static_cast(arrangement::Pivots::BottomLeft); - if (!alignment_xl_str.empty()) - arr_alignment = std::stoi(alignment_xl_str); - - m_arrange_settings_sla.alignment = arr_alignment ; - m_arrange_settings_fff.alignment = arr_alignment ; - m_arrange_settings_fff_seq_print.alignment = arr_alignment ; -} - static std::vector processed_objects_idxs(const Model& model, const SLAPrint& sla_print, const GLVolumePtrs& volumes) { std::vector ret; @@ -1392,38 +1305,47 @@ bool GLCanvas3D::is_arrange_alignment_enabled() const return m_config ? is_XL_printer(*m_config) : false; } -GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D &bed) - : m_canvas(canvas) - , m_context(nullptr) - , m_bed(bed) +GLCanvas3D::GLCanvas3D(wxGLCanvas *canvas, Bed3D &bed) + : m_canvas(canvas), + m_context(nullptr), + m_bed(bed) #if ENABLE_RETINA_GL - , m_retina_helper(nullptr) + , + m_retina_helper(nullptr) #endif - , m_in_render(false) - , m_main_toolbar(GLToolbar::Normal, "Main") - , m_undoredo_toolbar(GLToolbar::Normal, "Undo_Redo") - , m_gizmos(*this) - , m_use_clipping_planes(false) - , m_sidebar_field("") - , m_extra_frame_requested(false) - , m_config(nullptr) - , m_process(nullptr) - , m_model(nullptr) - , m_dirty(true) - , m_initialized(false) - , m_apply_zoom_to_volumes_filter(false) - , m_picking_enabled(false) - , m_moving_enabled(false) - , m_dynamic_background_enabled(false) - , m_multisample_allowed(false) - , m_moving(false) - , m_tab_down(false) - , m_cursor_type(Standard) - , m_reload_delayed(false) - , m_render_sla_auxiliaries(true) - , m_labels(*this) - , m_slope(m_volumes) - , m_sla_view(*this) + , + m_in_render(false), + m_main_toolbar(GLToolbar::Normal, "Main"), + m_undoredo_toolbar(GLToolbar::Normal, "Undo_Redo"), + m_gizmos(*this), + m_use_clipping_planes(false), + m_sidebar_field(""), + m_extra_frame_requested(false), + m_config(nullptr), + m_process(nullptr), + m_model(nullptr), + m_dirty(true), + m_initialized(false), + m_apply_zoom_to_volumes_filter(false), + m_picking_enabled(false), + m_moving_enabled(false), + m_dynamic_background_enabled(false), + m_multisample_allowed(false), + m_moving(false), + m_tab_down(false), + m_cursor_type(Standard), + m_reload_delayed(false), + m_render_sla_auxiliaries(true), + m_labels(*this), + m_slope(m_volumes), + m_sla_view(*this), + m_arrange_settings_dialog{wxGetApp().imgui(), + std::make_unique( + wxGetApp().app_config, + [this]() { return m_config; }, + [this] { + return current_printer_technology(); + })} { if (m_canvas != nullptr) { m_timer.SetOwner(m_canvas); @@ -1433,9 +1355,13 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D &bed) #endif // ENABLE_RETINA_GL } - load_arrange_settings(); - m_selection.set_volumes(&m_volumes.volumes); + m_arrange_settings_dialog.show_xl_align_combo([this](){ + return this->is_arrange_alignment_enabled(); + }); + m_arrange_settings_dialog.on_arrange_btn([]{ + wxGetApp().plater()->arrange(); + }); } GLCanvas3D::~GLCanvas3D() @@ -4831,104 +4757,9 @@ bool GLCanvas3D::_render_search_list(float pos_x) bool GLCanvas3D::_render_arrange_menu(float pos_x) { - ImGuiWrapper *imgui = wxGetApp().imgui(); + m_arrange_settings_dialog.render(pos_x, m_main_toolbar.get_height()); - imgui->set_next_window_pos(pos_x, m_main_toolbar.get_height(), ImGuiCond_Always, 0.5f, 0.0f); - - imgui->begin(_L("Arrange options"), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); - - ArrangeSettings settings = get_arrange_settings(); - ArrangeSettings &settings_out = get_arrange_settings_ref(this); - - auto &appcfg = wxGetApp().app_config; - PrinterTechnology ptech = current_printer_technology(); - - bool settings_changed = false; - float dist_min = 0.f; - float dist_bed_min = 0.f; - std::string dist_key = "min_object_distance"; - std::string dist_bed_key = "min_bed_distance"; - std::string rot_key = "enable_rotation"; - std::string align_key = "alignment"; - std::string postfix; - - if (ptech == ptSLA) { - postfix = "_sla"; - } else if (ptech == ptFFF) { - auto co_opt = m_config->option("complete_objects"); - if (co_opt && co_opt->value) { - dist_min = float(min_object_distance(*m_config)); - postfix = "_fff_seq_print"; - } else { - dist_min = 0.f; - postfix = "_fff"; - } - } - - dist_key += postfix; - dist_bed_key += postfix; - rot_key += postfix; - align_key += postfix; - - imgui->text(GUI::format_wxstr(_L("Press %1%left mouse button to enter the exact value"), shortkey_ctrl_prefix())); - - if (imgui->slider_float(_L("Spacing"), &settings.distance, dist_min, 100.0f, "%5.2f") || dist_min > settings.distance) { - settings.distance = std::max(dist_min, settings.distance); - settings_out.distance = settings.distance; - appcfg->set("arrange", dist_key.c_str(), float_to_string_decimal_point(settings_out.distance)); - settings_changed = true; - } - - if (imgui->slider_float(_L("Spacing from bed"), &settings.distance_from_bed, dist_bed_min, 100.0f, "%5.2f") || dist_bed_min > settings.distance_from_bed) { - settings.distance_from_bed = std::max(dist_bed_min, settings.distance_from_bed); - settings_out.distance_from_bed = settings.distance_from_bed; - appcfg->set("arrange", dist_bed_key.c_str(), float_to_string_decimal_point(settings_out.distance_from_bed)); - settings_changed = true; - } - - if (imgui->checkbox(_L("Enable rotations (slow)"), settings.enable_rotation)) { - settings_out.enable_rotation = settings.enable_rotation; - appcfg->set("arrange", rot_key.c_str(), settings_out.enable_rotation? "1" : "0"); - settings_changed = true; - } - - Points bed = m_config ? get_bed_shape(*m_config) : Points{}; - - if (arrangement::is_box(bed) && settings.alignment >= 0 && - imgui->combo(_L("Alignment"), {_u8L("Center"), _u8L("Rear left"), _u8L("Front left"), _u8L("Front right"), _u8L("Rear right"), _u8L("Random") }, settings.alignment)) { - settings_out.alignment = settings.alignment; - appcfg->set("arrange", align_key.c_str(), std::to_string(settings_out.alignment)); - settings_changed = true; - } - - ImGui::Separator(); - - if (imgui->button(_L("Reset"))) { - auto alignment = settings_out.alignment; - settings_out = ArrangeSettings{}; - settings_out.distance = std::max(dist_min, settings_out.distance); - - // Default alignment for XL printers set explicitly: - if (is_arrange_alignment_enabled()) - settings_out.alignment = static_cast(arrangement::Pivots::BottomLeft); - else - settings_out.alignment = alignment; - - appcfg->set("arrange", dist_key.c_str(), float_to_string_decimal_point(settings_out.distance)); - appcfg->set("arrange", dist_bed_key.c_str(), float_to_string_decimal_point(settings_out.distance_from_bed)); - appcfg->set("arrange", rot_key.c_str(), settings_out.enable_rotation? "1" : "0"); - settings_changed = true; - } - - ImGui::SameLine(); - - if (imgui->button(_L("Arrange"))) { - wxGetApp().plater()->arrange(); - } - - imgui->end(); - - return settings_changed; + return true; } #define ENABLE_THUMBNAIL_GENERATOR_DEBUG_OUTPUT 0 @@ -7773,15 +7604,20 @@ const SLAPrint* GLCanvas3D::sla_print() const return (m_process == nullptr) ? nullptr : m_process->sla_print(); } -void GLCanvas3D::WipeTowerInfo::apply_wipe_tower() const +void GLCanvas3D::WipeTowerInfo::apply_wipe_tower(Vec2d pos, double rot) { DynamicPrintConfig cfg; - cfg.opt("wipe_tower_x", true)->value = m_pos(X); - cfg.opt("wipe_tower_y", true)->value = m_pos(Y); - cfg.opt("wipe_tower_rotation_angle", true)->value = (180./M_PI) * m_rotation; + cfg.opt("wipe_tower_x", true)->value = pos.x(); + cfg.opt("wipe_tower_y", true)->value = pos.y(); + cfg.opt("wipe_tower_rotation_angle", true)->value = (180./M_PI) * rot; wxGetApp().get_tab(Preset::TYPE_PRINT)->load_config(cfg); } +void GLCanvas3D::WipeTowerInfo::apply_wipe_tower() const +{ + apply_wipe_tower(m_pos, m_rotation); +} + void GLCanvas3D::RenderTimer::Notify() { wxPostEvent((wxEvtHandler*)GetOwner(), RenderTimerEvent( EVT_GLCANVAS_RENDER_TIMER, *this)); diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 4d7368ed26..a25fddaa67 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -18,6 +18,7 @@ #include "Camera.hpp" #include "SceneRaycaster.hpp" #include "GUI_Utils.hpp" +#include "ArrangeSettingsDialogImgui.hpp" #include "libslic3r/Slicing.hpp" @@ -466,6 +467,8 @@ public: float accuracy = 0.65f; // Unused currently bool enable_rotation = false; int alignment = 0; + int geometry_handling = 0; + int strategy = 0; }; enum class ESLAViewType @@ -581,44 +584,11 @@ private: SLAView m_sla_view; bool m_sla_view_type_detection_active{ false }; - ArrangeSettings m_arrange_settings_fff, m_arrange_settings_sla, - m_arrange_settings_fff_seq_print; - bool is_arrange_alignment_enabled() const; - template - static auto & get_arrange_settings_ref(Self *self) { - PrinterTechnology ptech = self->current_printer_technology(); - - auto *ptr = &self->m_arrange_settings_fff; - - if (ptech == ptSLA) { - ptr = &self->m_arrange_settings_sla; - } else if (ptech == ptFFF) { - auto co_opt = self->m_config->template option("complete_objects"); - if (co_opt && co_opt->value) - ptr = &self->m_arrange_settings_fff_seq_print; - else - ptr = &self->m_arrange_settings_fff; - } - - return *ptr; - } + ArrangeSettingsDialogImgui m_arrange_settings_dialog; public: - ArrangeSettings get_arrange_settings() const { - const ArrangeSettings &settings = get_arrange_settings_ref(this); - ArrangeSettings ret = settings; - if (&settings == &m_arrange_settings_fff_seq_print) { - ret.distance = std::max(ret.distance, - float(min_object_distance(*m_config))); - } - - if (!is_arrange_alignment_enabled()) - ret.alignment = -1; - - return ret; - } struct ContoursList { @@ -631,7 +601,6 @@ public: }; private: - void load_arrange_settings(); class SequentialPrintClearance { @@ -754,10 +723,13 @@ public: void update_instance_printable_state_for_objects(const std::vector& object_idxs); void set_config(const DynamicPrintConfig* config); + const DynamicPrintConfig *config() const { return m_config; } void set_process(BackgroundSlicingProcess* process); void set_model(Model* model); const Model* get_model() const { return m_model; } + const arr2::ArrangeSettingsView * get_arrange_settings_view() const { return &m_arrange_settings_dialog; } + const Selection& get_selection() const { return m_selection; } Selection& get_selection() { return m_selection; } @@ -919,8 +891,11 @@ public: inline const Vec2d& pos() const { return m_pos; } inline double rotation() const { return m_rotation; } inline const Vec2d bb_size() const { return m_bb.size(); } + inline const BoundingBoxf& bounding_box() const { return m_bb; } void apply_wipe_tower() const; + + static void apply_wipe_tower(Vec2d pos, double rot); }; WipeTowerInfo get_wipe_tower_info() const; diff --git a/src/slic3r/GUI/Jobs/ArrangeJob.cpp b/src/slic3r/GUI/Jobs/ArrangeJob.cpp index 2d6333f8f7..7609a949f3 100644 --- a/src/slic3r/GUI/Jobs/ArrangeJob.cpp +++ b/src/slic3r/GUI/Jobs/ArrangeJob.cpp @@ -403,27 +403,27 @@ arrangement::ArrangePolygon get_arrange_poly(ModelInstance *inst, arrangement::ArrangeParams get_arrange_params(Plater *p) { - const GLCanvas3D::ArrangeSettings &settings = - p->canvas3D()->get_arrange_settings(); + const arr2::ArrangeSettingsView *settings = + p->canvas3D()->get_arrange_settings_view(); arrangement::ArrangeParams params; - params.allow_rotations = settings.enable_rotation; - params.min_obj_distance = scaled(settings.distance); - params.min_bed_distance = scaled(settings.distance_from_bed); + params.allow_rotations = settings->is_rotation_enabled(); + params.min_obj_distance = scaled(settings->get_distance_from_objects()); + params.min_bed_distance = scaled(settings->get_distance_from_bed()); arrangement::Pivots pivot = arrangement::Pivots::Center; int pivot_max = static_cast(arrangement::Pivots::TopRight); - if (settings.alignment < 0) { + if (settings->get_xl_alignment() < 0) { pivot = arrangement::Pivots::Center; - } else if (settings.alignment > pivot_max) { + } else if (settings->get_xl_alignment() == arr2::ArrangeSettingsView::xlpRandom) { // means it should be random std::random_device rd{}; std::mt19937 rng(rd()); std::uniform_int_distribution dist(0, pivot_max); pivot = static_cast(dist(rng)); } else { - pivot = static_cast(settings.alignment); + pivot = static_cast(settings->get_xl_alignment()); } params.alignment = pivot; diff --git a/src/slic3r/GUI/Jobs/ArrangeJob2.cpp b/src/slic3r/GUI/Jobs/ArrangeJob2.cpp new file mode 100644 index 0000000000..91830c3fb3 --- /dev/null +++ b/src/slic3r/GUI/Jobs/ArrangeJob2.cpp @@ -0,0 +1,207 @@ +#include "ArrangeJob2.hpp" + +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + +namespace Slic3r { namespace GUI { + +class GUISelectionMask: public arr2::SelectionMask { + const Selection *m_sel; + +public: + explicit GUISelectionMask(const Selection *sel) : m_sel{sel} {} + + bool is_wipe_tower() const override + { + return m_sel->is_wipe_tower(); + } + + std::vector selected_objects() const override + { + auto selmap = m_sel->get_object_idxs(); + + std::vector ret(m_sel->get_model()->objects.size(), false); + + for (auto sel : selmap) { + ret[sel] = true; + } + + return ret; + } + + std::vector selected_instances(int obj_id) const override + { + auto objcnt = static_cast(m_sel->get_model()->objects.size()); + auto icnt = obj_id < objcnt ? + m_sel->get_model()->objects[obj_id]->instances.size() : + 0; + + std::vector ret(icnt, false); + + auto selmap = m_sel->get_content(); + auto objit = selmap.find(obj_id); + + if (objit != selmap.end() && obj_id < objcnt) { + ret = std::vector(icnt, false); + for (auto sel : objit->second) { + ret[sel] = true; + } + } + + return ret; + } +}; + +struct WipeTowerGeometry +{ + Polygon poly; + Point pos = Point::Zero(); + double rot = 0.; +}; + +static WipeTowerGeometry get_wtg(const GLCanvas3D::WipeTowerInfo &wti) +{ + WipeTowerGeometry ret; + + auto bb = scaled(wti.bounding_box()); + ret.poly = Polygon({ + {bb.min}, + {bb.max.x(), bb.min.y()}, + {bb.max}, + {bb.min.x(), bb.max.y()} + }); + + ret.pos = scaled(wti.pos()); + ret.rot = wti.rotation(); + + return ret; +} + +// Wipe tower logic based on GLCanvas3D::WipeTowerInfo implements the Arrangeable +// interface with this class: +class ArrangeableWT: public arr2::ArrangeableWipeTowerBase +{ + BoundingBox m_xl_bb; +public: + explicit ArrangeableWT(const ObjectID &oid, + const WipeTowerGeometry &wtg, + std::function sel_pred, + const BoundingBox xl_bb = {}) + : arr2::ArrangeableWipeTowerBase{oid, wtg.poly, wtg.pos, wtg.rot, + std::move(sel_pred)}, m_xl_bb{xl_bb} + {} + + void transform(const Vec2d &transl, double rot) override + { + GLCanvas3D::WipeTowerInfo::apply_wipe_tower(unscaled(pos) + transl, rot); + } + + void imbue_data(arr2::AnyWritable &datastore) const override + { + // For XL printers, there is a requirement that the wipe tower + // needs to be placed right beside the extruders which reside at the + // top edge of the bed. + if (m_xl_bb.defined) { + Vec2crd xl_center = m_xl_bb.center(); + datastore.write("sink", Vec2crd{xl_center.x(), 2 * m_xl_bb.max.y()}); + } + + arr2::ArrangeableWipeTowerBase::imbue_data(datastore); + } +}; + +// Now the wipe tower handler implementation for GLCanvas3D::WipeTowerInfo +// This is what creates the ArrangeableWT when the arrangement requests it. +// An object of this class is installed into the arrangement Scene. +struct WTH : public arr2::WipeTowerHandler +{ + GLCanvas3D::WipeTowerInfo wti; + ObjectID oid; + std::function sel_pred; + BoundingBox xl_bb; + + WTH(const ObjectID &objid, + const GLCanvas3D::WipeTowerInfo &w, + std::function sel_predicate = [] { return false; }) + : wti(w), oid{objid}, sel_pred{std::move(sel_predicate)} + {} + + template + static void visit_(Self &&self, Fn &&fn) + { + auto wtg = get_wtg(self.wti); + ArrangeableWT wta{self.oid, wtg, self.sel_pred, self.xl_bb}; + fn(wta); + } + + void visit(std::function fn) override + { + visit_(*this, fn); + } + + void visit(std::function fn) const override + { + visit_(*this, fn); + } + + void set_selection_predicate(std::function pred) override + { + sel_pred = std::move(pred); + } +}; + +arr2::SceneBuilder build_scene(Plater &plater, ArrangeSelectionMode mode) +{ + arr2::SceneBuilder builder; + + if (mode == ArrangeSelectionMode::SelectionOnly) { + auto sel = std::make_unique(&plater.get_selection()); + builder.set_selection(std::move(sel)); + } + + builder.set_arrange_settings(plater.canvas3D()->get_arrange_settings_view()); + + auto wti = plater.canvas3D()->get_wipe_tower_info(); + + AnyPtr wth; + + if (wti) { + wth = std::make_unique(plater.model().wipe_tower.id(), wti); + } + + if (plater.config()) { + builder.set_bed(*plater.config()); + if (wth && is_XL_printer(*plater.config())) { + wth->xl_bb = bounding_box(get_bed_shape(*plater.config())); + } + } + + builder.set_wipe_tower_handler(std::move(wth)); + builder.set_model(plater.model()); + + if (plater.printer_technology() == ptSLA) + builder.set_sla_print(&plater.sla_print()); + else + builder.set_fff_print(&plater.fff_print()); + + return builder; +} + +FillBedJob2::FillBedJob2(arr2::Scene &&scene, const Callbacks &cbs) : Base(std::move(scene), _u8L("Filling bed"), cbs) {} + +ArrangeJob2::ArrangeJob2(arr2::Scene &&scene, const Callbacks &cbs) : Base(std::move(scene), _u8L("Arranging"), cbs) {} + +}} // namespace Slic3r diff --git a/src/slic3r/GUI/Jobs/ArrangeJob2.hpp b/src/slic3r/GUI/Jobs/ArrangeJob2.hpp new file mode 100644 index 0000000000..31d51e9124 --- /dev/null +++ b/src/slic3r/GUI/Jobs/ArrangeJob2.hpp @@ -0,0 +1,138 @@ +#ifndef ARRANGEJOB2_HPP +#define ARRANGEJOB2_HPP + +#include + +#include "Job.hpp" + +#include "libslic3r/Arrange/Tasks/ArrangeTask.hpp" +#include "libslic3r/Arrange/Tasks/FillBedTask.hpp" +#include "libslic3r/Arrange/Items/ArrangeItem.hpp" +#include "libslic3r/Arrange/SceneBuilder.hpp" + +namespace Slic3r { + +class Model; +class DynamicPrintConfig; +class ModelInstance; + +class Print; +class SLAPrint; + +namespace GUI { + +class Plater; + +enum class ArrangeSelectionMode { SelectionOnly, Full }; + +arr2::SceneBuilder build_scene( + Plater &plater, ArrangeSelectionMode mode = ArrangeSelectionMode::Full); + +struct ArrCtl : public arr2::ArrangeTaskBase::Ctl +{ + Job::Ctl &parent_ctl; + int total; + const std::string &msg; + + ArrCtl(Job::Ctl &ctl, int cnt, const std::string &m) + : parent_ctl{ctl}, total{cnt}, msg{m} + {} + + bool was_canceled() const override + { + return parent_ctl.was_canceled(); + } + + void update_status(int remaining) override + { + if (remaining > 0) + parent_ctl.update_status((total - remaining) * 100 / total, msg); + } +}; + +template +class ArrangeJob_ : public Job +{ +public: + using ResultType = + typename decltype(std::declval().process_native( + std::declval()))::element_type; + + struct Callbacks { + std::function on_prepared; + std::function on_processed; + std::function on_finished; + }; + +private: + arr2::Scene m_scene; + std::unique_ptr m_task; + std::unique_ptr m_result; + Callbacks m_cbs; + std::string m_task_msg; + +public: + void process(Ctl &ctl) override + { + ctl.call_on_main_thread([this]{ + m_task = ArrangeTaskT::create(m_scene); + m_result.reset(); + if (m_task && m_cbs.on_prepared) + m_cbs.on_prepared(*m_task); + }).wait(); + + if (!m_task) + return; + + auto count = m_task->item_count_to_process(); + + if (count == 0) // Should be taken care of by plater, but doesn't hurt + return; + + ctl.update_status(0, m_task_msg); + + auto taskctl = ArrCtl{ctl, count, m_task_msg}; + m_result = m_task->process_native(taskctl); + + ctl.update_status(100, m_task_msg); + } + + void finalize(bool canceled, std::exception_ptr &eptr) override + { + if (canceled || eptr || !m_result) + return; + + if (m_task && m_cbs.on_processed) + m_cbs.on_processed(*m_task); + + m_result->apply_on(m_scene.model()); + + if (m_task && m_cbs.on_finished) + m_cbs.on_finished(*m_result); + } + + explicit ArrangeJob_(arr2::Scene &&scene, + std::string task_msg, + const Callbacks &cbs = {}) + : m_scene{std::move(scene)}, m_cbs{cbs}, m_task_msg{std::move(task_msg)} + {} +}; + +class ArrangeJob2: public ArrangeJob_> +{ + using Base = ArrangeJob_>; +public: + ArrangeJob2(arr2::Scene &&scene, const Callbacks &cbs = {}); +}; + +class FillBedJob2: public ArrangeJob_> +{ + using Base = ArrangeJob_>; +public: + FillBedJob2(arr2::Scene &&scene, const Callbacks &cbs = {}); +}; + +} // namespace GUI +} // namespace Slic3r + +#endif // ARRANGEJOB2_HPP diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 36f284f3a8..6799b9c062 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -79,8 +79,10 @@ #include "Camera.hpp" #include "Mouse3DController.hpp" #include "Tab.hpp" -#include "Jobs/ArrangeJob.hpp" -#include "Jobs/FillBedJob.hpp" +//#include "Jobs/ArrangeJob.hpp" +#include "Jobs/ArrangeJob2.hpp" + +//#include "Jobs/FillBedJob.hpp" #include "Jobs/RotoptimizeJob.hpp" #include "Jobs/SLAImportJob.hpp" #include "Jobs/SLAImportDialog.hpp" @@ -6259,8 +6261,52 @@ void Plater::fill_bed_with_instances() { auto &w = get_ui_job_worker(); if (w.is_idle()) { - p->take_snapshot(_L("Fill bed")); - replace_job(w, std::make_unique()); + + FillBedJob2::Callbacks cbs; + cbs.on_processed = [this](arr2::ArrangeTaskBase &t) { + p->take_snapshot(_L("Fill bed")); + }; + + auto scene = arr2::Scene{ + build_scene(*this, ArrangeSelectionMode::SelectionOnly)}; + + cbs.on_finished = [this](arr2::FillBedTaskResult &result) { + auto [prototype_mi, pos] = arr2::find_instance_by_id(model(), result.prototype_id); + + if (!prototype_mi) + return; + + ModelObject *model_object = prototype_mi->get_object(); + assert(model_object); + +// model_object->ensure_on_bed(); + + size_t inst_cnt = model_object->instances.size(); + if (inst_cnt == 0) + return; + + int object_idx = pos.obj_idx; + + if (object_idx < 0 || object_idx >= int(model().objects.size())) + return; + + int added_cnt = result.to_add.size(); + + if (added_cnt > 0) { + update(static_cast(UpdateParams::FORCE_FULL_SCREEN_REFRESH)); + + // FIXME: somebody explain why this is needed for + // increase_object_instances + if (inst_cnt == 1) + added_cnt++; + + sidebar() + .obj_list() + ->increase_object_instances(object_idx, size_t(added_cnt)); + } + }; + + replace_job(w, std::make_unique(std::move(scene), cbs)); } } @@ -6338,8 +6384,8 @@ void Plater::apply_cut_object_to_model(size_t obj_idx, const ModelObjectPtrs& ne selection.add_object((unsigned int)(last_id - i), i == 0); UIThreadWorker w; - replace_job(w, std::make_unique(ArrangeJob::SelectionOnly)); - w.process_events(); + arrange(w, true); + w.wait_for_idle(); } void Plater::export_gcode(bool prefer_removable) @@ -7229,19 +7275,70 @@ GLCanvas3D* Plater::get_current_canvas3D() return p->get_current_canvas3D(); } +static std::string concat_strings(const std::set &strings, + const std::string &delim = "\n") +{ + return std::accumulate( + strings.begin(), strings.end(), std::string(""), + [delim](const std::string &s, const std::string &name) { + return s + name + delim; + }); +} + void Plater::arrange() { if (p->can_arrange()) { auto &w = get_ui_job_worker(); - p->take_snapshot(_L("Arrange")); - - auto mode = wxGetKeyState(WXK_SHIFT) ? ArrangeJob::SelectionOnly : - ArrangeJob::Full; - - replace_job(w, std::make_unique(mode)); + arrange(w, wxGetKeyState(WXK_SHIFT)); } } +void Plater::arrange(Worker &w, bool selected) +{ + ArrangeSelectionMode mode = selected ? + ArrangeSelectionMode::SelectionOnly : + ArrangeSelectionMode::Full; + + arr2::Scene arrscene{build_scene(*this, mode)}; + + ArrangeJob2::Callbacks cbs; + + cbs.on_processed = [this](arr2::ArrangeTaskBase &t) { + p->take_snapshot(_L("Arrange")); + }; + + cbs.on_finished = [this](arr2::ArrangeTaskResult &t) { + std::set names; + + auto collect_unarranged = [this, &names](const arr2::TrafoOnlyArrangeItem &itm) { + if (!arr2::is_arranged(itm)) { + std::optional id = arr2::retrieve_id(itm); + if (id) { + auto [mi, pos] = arr2::find_instance_by_id(p->model, *id); + if (mi && mi->get_object()) { + names.insert(mi->get_object()->name); + } + } + } + }; + + for (const arr2::TrafoOnlyArrangeItem &itm : t.items) + collect_unarranged(itm); + + if (!names.empty()) { + get_notification_manager()->push_notification( + GUI::format(_L("Arrangement ignored the following objects which " + "can't fit into a single bed:\n%s"), + concat_strings(names, "\n"))); + } + + update(static_cast(UpdateParams::FORCE_FULL_SCREEN_REFRESH)); + wxGetApp().obj_manipul()->set_dirty(); + }; + + replace_job(w, std::make_unique(std::move(arrscene), cbs)); +} + void Plater::set_current_canvas_as_dirty() { p->set_current_canvas_as_dirty(); diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index bed65d7d13..cc51a78628 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -337,6 +337,7 @@ public: GLCanvas3D* get_current_canvas3D(); void arrange(); + void arrange(Worker &w, bool selected); void set_current_canvas_as_dirty(); void unbind_canvas_event_handlers(); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index b91f75be41..d6d16db1d2 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -27,7 +27,7 @@ endif() set_property(GLOBAL PROPERTY USE_FOLDERS ON) -add_subdirectory(libnest2d) +add_subdirectory(arrange) add_subdirectory(libslic3r) add_subdirectory(slic3rutils) add_subdirectory(fff_print) diff --git a/tests/arrange/CMakeLists.txt b/tests/arrange/CMakeLists.txt new file mode 100644 index 0000000000..26204e82b2 --- /dev/null +++ b/tests/arrange/CMakeLists.txt @@ -0,0 +1,17 @@ +get_filename_component(_TEST_NAME ${CMAKE_CURRENT_LIST_DIR} NAME) +add_executable(${_TEST_NAME}_tests ${_TEST_NAME}_tests_main.cpp + test_arrange.cpp + test_arrange_integration.cpp + ../data/prusaparts.cpp +) + +target_link_libraries(${_TEST_NAME}_tests test_common libslic3r) +set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER "tests") + +if (WIN32) + prusaslicer_copy_dlls(${_TEST_NAME}_tests) +endif() + +set(_catch_args "exclude:[NotWorking]") +list(APPEND _catch_args "${CATCH_EXTRA_ARGS}") +add_test(${_TEST_NAME}_tests ${_TEST_NAME}_tests ${_catch_args}) diff --git a/tests/arrange/arrange_tests_main.cpp b/tests/arrange/arrange_tests_main.cpp new file mode 100644 index 0000000000..b2aa80259d --- /dev/null +++ b/tests/arrange/arrange_tests_main.cpp @@ -0,0 +1 @@ +#include diff --git a/tests/arrange/test_arrange.cpp b/tests/arrange/test_arrange.cpp new file mode 100644 index 0000000000..578fc573b6 --- /dev/null +++ b/tests/arrange/test_arrange.cpp @@ -0,0 +1,1122 @@ +#include +#include "test_utils.hpp" + +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include + +#include +#include + +#include "../data/prusaparts.hpp" + +#include +#include + +#include + +#include +#include +#include +#include + +#include + +template +static std::vector prusa_parts(double infl = 0.) { + using namespace Slic3r; + + std::vector ret; + + if(ret.empty()) { + ret.reserve(PRUSA_PART_POLYGONS.size()); + for(auto& inp : PRUSA_PART_POLYGONS) { + ExPolygons inp_cpy{ExPolygon(inp)}; + inp_cpy.back().contour.points.pop_back(); + + std::reverse(inp_cpy.back().contour.begin(), + inp_cpy.back().contour.end()); + + REQUIRE(inp_cpy.back().contour.is_counter_clockwise()); + + if (infl > 0.) + inp_cpy = offset_ex(inp_cpy, scaled(std::ceil(infl / 2.))); + + ArrItem item{Geometry::convex_hull(inp_cpy)}; + + ret.emplace_back(std::move(item)); + } + } + + return ret; +} + +static std::vector prusa_parts_ex(double infl = 0.) +{ + using namespace Slic3r; + + std::vector ret; + + if (ret.empty()) { + ret.reserve(PRUSA_PART_POLYGONS_EX.size()); + for (auto &inp : PRUSA_PART_POLYGONS_EX) { + ExPolygons inp_cpy{inp}; + + REQUIRE(std::all_of(inp_cpy.begin(), inp_cpy.end(), + [](const ExPolygon &p) { + return p.contour.is_counter_clockwise(); + })); + + if (infl > 0.) + inp_cpy = offset_ex(inp_cpy, scaled(std::ceil(infl / 2.))); + + Point c = get_extents(inp_cpy).center(); + for (auto &p : inp_cpy) + p.translate(-c); + + arr2::ArrangeItem item{inp_cpy}; + + ret.emplace_back(std::move(item)); + } + } + + return ret; +} + +using Slic3r::arr2::ArrangeItem; +using Slic3r::arr2::DecomposedShape; + +struct ItemPair { + ArrangeItem orbiter; + ArrangeItem stationary; +}; + +using Slic3r::scaled; +using Slic3r::Vec2f; + +std::vector nfp_testdata = { + { + ArrangeItem { DecomposedShape { + scaled(Vec2f{80, 50}) , + scaled(Vec2f{120, 50}), + scaled(Vec2f{100, 70}), + }}, + ArrangeItem { DecomposedShape { + scaled(Vec2f{40, 10}), + scaled(Vec2f{40, 40}), + scaled(Vec2f{10, 40}), + scaled(Vec2f{10, 10}), + }} + }, + { + ArrangeItem { + scaled(Vec2f{120, 50}), + scaled(Vec2f{140, 70}), + scaled(Vec2f{120, 90}), + scaled(Vec2f{80, 90}) , + scaled(Vec2f{60, 70}) , + scaled(Vec2f{80, 50}) , + }, + ArrangeItem { + scaled(Vec2f{40, 10}), + scaled(Vec2f{40, 40}), + scaled(Vec2f{10, 40}), + scaled(Vec2f{10, 10}), + } + }, +}; + +struct PolyPair { Slic3r::ExPolygon orbiter; Slic3r::ExPolygon stationary; }; + +std::vector nfp_concave_testdata = { + { // ItemPair + { + { + scaled(Vec2f{53.3726f, 14.2141f}), + scaled(Vec2f{53.2359f, 14.3386f}), + scaled(Vec2f{53.0141f, 14.2155f}), + scaled(Vec2f{52.8649f, 16.0091f}), + scaled(Vec2f{53.3659f, 15.7607f}), + scaled(Vec2f{53.8669f, 16.0091f}), + scaled(Vec2f{53.7178f, 14.2155f}), + scaled(Vec2f{53.4959f, 14.3386f}) + } + }, + { + { + scaled(Vec2f{11.8305f, 1.1603f}), + scaled(Vec2f{11.8311f, 2.6616f}), + scaled(Vec2f{11.3311f, 2.6611f}), + scaled(Vec2f{10.9311f, 2.9604f}), + scaled(Vec2f{10.9300f, 4.4608f}), + scaled(Vec2f{10.9311f, 4.9631f}), + scaled(Vec2f{11.3300f, 5.2636f}), + scaled(Vec2f{11.8311f, 5.2636f}), + scaled(Vec2f{11.8308f, 10.3636f}), + scaled(Vec2f{22.3830f, 10.3636f}), + scaled(Vec2f{23.6845f, 9.0642f}), + scaled(Vec2f{23.6832f, 1.1630f}), + scaled(Vec2f{23.2825f, 1.1616f}), + scaled(Vec2f{21.0149f, 1.1616f}), + scaled(Vec2f{21.1308f, 1.3625f}), + scaled(Vec2f{20.9315f, 1.7080f}), + scaled(Vec2f{20.5326f, 1.7080f}), + scaled(Vec2f{20.3334f, 1.3629f}), + scaled(Vec2f{20.4493f, 1.1616f}) + } + }, + } +}; + +static void check_nfp(const std::string & outfile_prefix, + const Slic3r::Polygons &stationary, + const Slic3r::Polygons &orbiter, + const Slic3r::ExPolygons &bedpoly, + const Slic3r::ExPolygons &nfp) +{ + using namespace Slic3r; + + auto stationary_ex = to_expolygons(stationary); + auto bedbb = get_extents(bedpoly); + bedbb.offset(scaled(1.)); + auto bedrect = arr2::to_rectangle(bedbb); + + ExPolygons bed_negative = diff_ex(bedrect, bedpoly); + ExPolygons orb_ex_r = to_expolygons(orbiter); + ExPolygons orb_ex_r_ch = {ExPolygon(Geometry::convex_hull(orb_ex_r))}; + auto orb_ex_offs_pos_r = offset_ex(orb_ex_r, SCALED_EPSILON); + auto orb_ex_offs_neg_r = offset_ex(orb_ex_r, -SCALED_EPSILON); + auto orb_ex_offs_pos_r_ch = offset_ex(orb_ex_r_ch, SCALED_EPSILON); + auto orb_ex_offs_neg_r_ch = offset_ex(orb_ex_r_ch, -SCALED_EPSILON); + + auto bedpoly_offs = offset_ex(bedpoly, SCALED_EPSILON); + + auto check_at_nfppos = [&](const Point &pos) { + ExPolygons orb_ex = orb_ex_r; + Point d = pos - reference_vertex(orbiter); + for (ExPolygon &poly : orb_ex) + poly.translate(d); + + bool touching = false; + bool check_failed = false; + + bool within_bed = false; + bool touches_fixed = false; + bool touches_bedwall = false; + + try { + auto beddiff = diff_ex(orb_ex, bedpoly_offs); + if (beddiff.empty()) + within_bed = true; + + auto orb_ex_offs_pos = orb_ex_offs_pos_r; + for (ExPolygon &poly: orb_ex_offs_pos) + poly.translate(d); + + auto orb_ex_offs_neg = orb_ex_offs_neg_r; + for (ExPolygon &poly: orb_ex_offs_neg) + poly.translate(d); + + auto orb_ex_offs_pos_ch = orb_ex_offs_pos_r_ch; + for (ExPolygon &poly: orb_ex_offs_pos_ch) + poly.translate(d); + + auto orb_ex_offs_neg_ch = orb_ex_offs_neg_r_ch; + for (ExPolygon &poly: orb_ex_offs_neg_ch) + poly.translate(d); + + if (!touches_bedwall) { + auto inters_pos = intersection_ex(bed_negative, orb_ex_offs_pos_ch); + auto inters_neg = intersection_ex(bed_negative, orb_ex_offs_neg_ch); + if (!inters_pos.empty() && inters_neg.empty()) + touches_bedwall = true; + } + + if (!touches_fixed) { + auto inters_pos = intersection_ex(stationary_ex, orb_ex_offs_pos); + auto inters_neg = intersection_ex(stationary_ex, orb_ex_offs_neg); + if (!inters_pos.empty() && inters_neg.empty()) + touches_fixed = true; + } + + touching = within_bed && (touches_fixed || touches_bedwall); + } catch (...) { + check_failed = true; + touching = false; + } + +#ifndef NDEBUG + if (!touching || check_failed) { + + auto bb = get_extents(bedpoly); + SVG svg(outfile_prefix + ".svg", bb, 0, true); + svg.draw(orbiter, "orange"); + svg.draw(stationary, "yellow"); + svg.draw(bed_negative, "blue", 0.5f); + svg.draw(nfp, "green", 0.5f); + svg.draw(orb_ex, "red"); + + svg.Close(); + } +#endif + REQUIRE(!check_failed); + REQUIRE(touching); + }; + + if (nfp.empty()) { + auto bb = get_extents(bedpoly); + SVG svg(outfile_prefix + ".svg", bb, 0, true); + svg.draw(orbiter, "orange"); + svg.draw(stationary, "yellow"); + svg.draw(bedpoly, "blue", 0.5f); + + svg.Close(); + } + + REQUIRE(!nfp.empty()); + + for (const ExPolygon &nfp_part : nfp) { + for (const Point &nfp_pos : nfp_part.contour) { + check_at_nfppos(nfp_pos); + } + for (const Polygon &h : nfp_part.holes) + for (const Point &nfp_pos : h) { + check_at_nfppos(nfp_pos); + } + } +} + +template +void test_itempairs(const std::vector &testdata, + const Bed &bed, + const std::string &outfile_prefix = "") +{ + using namespace Slic3r; + + size_t testnum = 0; + + ExPolygons bedshape = arr2::to_expolygons(bed); + + for(auto td : testdata) { + Polygons orbiter = td.orbiter.envelope().transformed_outline(); + Polygons stationary = td.stationary.shape().transformed_outline(); + Point center = bounding_box(bed).center(); + Point stat_c = get_extents(stationary).center(); + Point d = center - stat_c; + arr2::translate(td.stationary, d); + stationary = td.stationary.shape().transformed_outline(); + + std::array, 1> fixed = {{td.stationary}}; + auto nfp = arr2::calculate_nfp(td.orbiter, arr2::default_context(fixed), bed); + + check_nfp(outfile_prefix + "nfp_test_" + std::to_string(testnum), + stationary, + orbiter, + bedshape, + nfp); + + testnum++; + } +} + +template +static void foreach_combo(const Slic3r::Range &range, const Fn &fn) +{ + std::vector pairs(range.size(), false); + + assert(range.size() >= 2); + pairs[range.size() - 1] = true; + pairs[range.size() - 2] = true; + + do { + std::vector::value_type> items; + for (size_t i = 0; i < pairs.size(); i++) { + if (pairs[i]) { + auto it = range.begin(); + std::advance(it, i); + items.emplace_back(*it); + } + } + fn (items[0], items[1]); + } while (std::next_permutation(pairs.begin(), pairs.end())); +} + +TEST_CASE("Static type tests for arrange items", "[arrange2]") +{ + using namespace Slic3r; + + REQUIRE(arr2::IsDataStore); + REQUIRE(arr2::IsWritableItem); + + REQUIRE(! arr2::IsDataStore); + REQUIRE(arr2::IsWritableItem); + + REQUIRE(arr2::IsDataStore); + REQUIRE(arr2::IsWritableItem); +} + +template Bed init_bed() { return {}; } +template<> inline Slic3r::arr2::InfiniteBed init_bed() +{ + return Slic3r::arr2::InfiniteBed{{scaled(250.) / 2., scaled(210.) / 2.}}; +} + +template<> inline Slic3r::arr2::RectangleBed init_bed() +{ + return Slic3r::arr2::RectangleBed{scaled(500.), scaled(500.)}; +} + +template<> inline Slic3r::arr2::CircleBed init_bed() +{ + return Slic3r::arr2::CircleBed{Slic3r::Point::Zero(), scaled(300.)}; +} + +template<> inline Slic3r::arr2::IrregularBed init_bed() +{ + using namespace Slic3r; + BoundingBox bb_outer{Point::Zero(), {scaled(500.), scaled(500.)}}; + BoundingBox corner{Point::Zero(), {scaled(50.), scaled(50.)}}; + + auto transl = [](BoundingBox bb, Point t) { bb.translate(t); return bb; }; + + Polygons rect_outer = {arr2::to_rectangle(bb_outer)}; + Polygons corners = {arr2::to_rectangle(transl(corner, {scaled(10.), scaled(10.)})), + arr2::to_rectangle(transl(corner, {scaled(440.), scaled(10.)})), + arr2::to_rectangle(transl(corner, {scaled(440.), scaled(440.)})), + arr2::to_rectangle(transl(corner, {scaled(10.), scaled(440.)})), + arr2::to_rectangle(BoundingBox({scaled(80.), scaled(450.)}, {scaled(420.), scaled(510.)})), + arr2::to_rectangle(BoundingBox({scaled(80.), scaled(-10.)}, {scaled(420.), scaled(50.)}))}; + + ExPolygons bedshape = diff_ex(rect_outer, corners); + + return arr2::IrregularBed{bedshape}; +} + +template std::string bedtype_str(const Bed &bed) +{ + return ""; +} + +inline std::string bedtype_str(const Slic3r::arr2::RectangleBed &bed) +{ + return "RectangleBed"; +} + +inline std::string bedtype_str(const Slic3r::arr2::CircleBed &bed) +{ + return "CircleBed"; +} + +inline std::string bedtype_str(const Slic3r::arr2::InfiniteBed &bed) +{ + return "InfiniteBed"; +} + +inline std::string bedtype_str(const Slic3r::arr2::IrregularBed &bed) +{ + return "IrregularBed"; +} + +TEST_CASE("NFP should be empty if item cannot fit into bed", "[arrange2]") { + using namespace Slic3r; + + arr2::RectangleBed bed{scaled(10.), scaled(10.)}; + ExPolygons bedshape = arr2::to_expolygons(bed); + + for(auto& td : nfp_testdata) { + REQUIRE(&(td.orbiter.envelope()) == &(td.orbiter.shape())); + REQUIRE(&(td.stationary.envelope()) == &(td.stationary.shape())); + REQUIRE(td.orbiter.envelope().reference_vertex() == + td.orbiter.shape().reference_vertex()); + REQUIRE(td.stationary.envelope().reference_vertex() == + td.stationary.shape().reference_vertex()); + + ArrangeItem cpy = td.stationary; + REQUIRE(&(cpy.envelope()) == &(cpy.shape())); + REQUIRE(cpy.envelope().reference_vertex() == + cpy.shape().reference_vertex()); + + std::array, 1> fixed = + {{td.stationary}}; + + auto nfp = arr2::calculate_nfp(td.orbiter, default_context(fixed), bed); + + REQUIRE(nfp.empty()); + } +} + +#include +#include + +TEMPLATE_TEST_CASE("NFP algorithm test", + "[arrange2]", + Slic3r::arr2::InfiniteBed, + Slic3r::arr2::RectangleBed, + Slic3r::arr2::CircleBed, + Slic3r::arr2::IrregularBed) +{ + using namespace Slic3r; + + auto bed = init_bed(); + std::string bedtypestr = bedtype_str(bed); + + SECTION("Predefined simple polygons for debugging") { + test_itempairs(nfp_testdata, bed, bedtypestr + "_"); + } + + SECTION("All combinations of convex prusa parts without inflation") { + auto parts = prusa_parts(); + + std::vector testdata; + foreach_combo(range(parts), [&testdata](auto &i1, auto &i2){ + testdata.emplace_back(ItemPair{i1, i2}); + }); + + test_itempairs(testdata, bed, bedtypestr + "_prusacombos"); + } + + SECTION("All combinations of prusa parts with random inflation") { + std::random_device rd; + auto seed = rd(); + std::mt19937 rng{seed}; + std::uniform_real_distribution distr{0., 50.}; + + INFO ("Seed = " << seed); + + auto parts = prusa_parts(distr(rng)); + + std::vector testdata; + foreach_combo(range(parts), [&testdata](auto &i1, auto &i2){ + testdata.emplace_back(ItemPair{i1, i2}); + }); + + test_itempairs(testdata, bed, bedtypestr + "_prusacombos_infl"); + } + + SECTION("All combinations of concave-holed prusa parts without inflation") + { + auto parts = prusa_parts_ex(); + for (ArrangeItem &itm : parts) { + itm.set_envelope(arr2::DecomposedShape{itm.shape().convex_hull()}); + } + + std::vector testdata; + foreach_combo(range(parts), [&testdata](auto &i1, auto &i2){ + testdata.emplace_back(ItemPair{i1, i2}); + }); + + test_itempairs(testdata, bed, bedtypestr + "_prusacombos_ex"); + } + + SECTION("All combinations of concave-holed prusa parts with inflation") { + std::random_device rd; + auto seed = rd(); + std::mt19937 rng{seed}; + std::uniform_real_distribution distr{0., 50.}; + + INFO ("Seed = " << seed); + + auto parts = prusa_parts_ex(distr(rng)); + for (ArrangeItem &itm : parts) { + itm.set_envelope(arr2::DecomposedShape{itm.shape().convex_hull()}); + } + + std::vector testdata; + foreach_combo(range(parts), [&testdata](auto &i1, auto &i2){ + testdata.emplace_back(ItemPair{i1, i2}); + }); + + test_itempairs(testdata, bed, bedtypestr + "_prusacombos_ex_infl"); + } +} + +TEST_CASE("EdgeCache tests", "[arrange2]") { + using namespace Slic3r; + + SECTION ("Empty polygon should produce empty edge-cache") { + ExPolygon emptypoly; + + arr2::EdgeCache ep {&emptypoly}; + + std::vector samples; + ep.sample_contour(1., samples); + REQUIRE(samples.empty()); + } + + SECTION ("Single edge polygon should be considered as 2 lines") { + ExPolygon poly{scaled(Vec2f{0.f, 0.f}), scaled(Vec2f{10., 10.})}; + + arr2::EdgeCache ep{&poly}; + std::vector samples; + + double accuracy = 1.; + ep.sample_contour(accuracy, samples); + + REQUIRE(samples.size() == 2); + REQUIRE(ep.coords(samples[0]) == poly.contour[1]); + REQUIRE(ep.coords(samples[1]) == poly.contour[0]); + REQUIRE(ep.coords({0, 0.}) == ep.coords({0, 1.})); + } + + SECTION ("Test address range") { + // Single edge on the int range boundary + ExPolygon poly{scaled(Vec2f{-2000.f, 0.f}), scaled(Vec2f{2000.f, 0.f})}; + + arr2::EdgeCache ep{&poly}; + REQUIRE(ep.coords({0, 0.25}) == Vec2crd{0, 0}); + REQUIRE(ep.coords({0, 0.75}) == Vec2crd{0, 0}); + + // Multiple edges on the int range boundary + ExPolygon squ{arr2::to_rectangle(scaled(BoundingBoxf{{0., 0.}, {2000., 2000.}}))}; + + arr2::EdgeCache ep2{&squ}; + REQUIRE(ep2.coords({0, 0.}) == Vec2crd{0, 0}); + REQUIRE(ep2.coords({0, 0.25}) == Vec2crd{2000000000, 0}); + REQUIRE(ep2.coords({0, 0.5}) == Vec2crd{2000000000, 2000000000}); + REQUIRE(ep2.coords({0, 0.75}) == Vec2crd{0, 2000000000}); + REQUIRE(ep2.coords({0, 1.}) == Vec2crd{0, 0}); + } + + SECTION("Accuracy argument should skip corners correctly") { + ExPolygon poly{arr2::to_rectangle(scaled(BoundingBoxf{{0., 0.}, {10., 10.}}))}; + + double accuracy = 1.; + arr2::EdgeCache ep{&poly}; + std::vector samples; + ep.sample_contour(accuracy, samples); + REQUIRE(samples.size() == poly.contour.size()); + for (size_t i = 0; i < samples.size(); ++i) { + auto &cr = samples[i]; + REQUIRE(ep.coords(cr) == poly.contour.points[(i + 1) % poly.contour.size()]); + } + + accuracy = 0.; + arr2::EdgeCache ep0{&poly}; + samples.clear(); + ep0.sample_contour(accuracy, samples); + REQUIRE(samples.size() == 1); + REQUIRE(ep0.coords(samples[0]) == poly.contour.points[1]); + } +} + +// Mock packing strategy that places N items to the center of the +// bed bounding box, if the bed is larger than the item. +template +struct RectangleToCenterPackStrategy { static constexpr int Capacity = Cap; }; + +namespace Slic3r { namespace arr2 { +struct RectangleToCenterPackTag {}; + +template struct PackStrategyTag_> { + using Tag = RectangleToCenterPackTag; +}; + +// Dummy arrangeitem that is a rectangle +struct RectangleItem { + int bed_index = Unarranged; + BoundingBox shape = {{0, 0}, scaled(Vec2d{10., 10.})}; + Vec2crd translation = {0, 0}; + double rotation = 0; + + int priority = 0; + int packed_num = 0; + + void set_bed_index(int idx) { bed_index = idx; } + int get_bed_index() const noexcept { return bed_index; } + + void set_translation(const Vec2crd &tr) { translation = tr; } + const Vec2crd & get_translation() const noexcept { return translation; } + + void set_rotation(double r) { rotation = r; } + double get_rotation() const noexcept { return rotation; } + + int get_priority() const noexcept { return priority; } +}; + +template +bool pack(Strategy &&strategy, + const Bed &bed, + RectangleItem &item, + const PackStrategyContext &packing_context, + const Range &remaining_items, + const RectangleToCenterPackTag &) +{ + bool ret = false; + + auto bedbb = bounding_box(bed); + auto itmbb = item.shape; + + Vec2crd tr = bedbb.center() - itmbb.center(); + itmbb.translate(tr); + + auto fixed_items = all_items_range(packing_context); + + if (fixed_items.size() < Slic3r::StripCVRef::Capacity && + bedbb.contains(itmbb)) + { + translate(item, tr); + ret = true; + } + + return ret; +} + +}} // namespace Slic3r::arr2 + +using Slic3r::arr2::RectangleItem; + +TEST_CASE("First fit selection strategy", "[arrange2]") +{ + using ArrItem = RectangleItem; + using Cmp = Slic3r::arr2::firstfit::DefaultItemCompareFn; + + auto create_items_n = [](size_t count) { + INFO ("Item count = " << count); + + auto items = Slic3r::reserve_vector(count); + std::generate_n(std::back_inserter(items), count, [] { return ArrItem{}; }); + + return items; + }; + + auto bed = Slic3r::arr2::RectangleBed(scaled(100.), scaled(100.)); + + GIVEN("A packing strategy that does not accept any items") + { + using PackStrategy = RectangleToCenterPackStrategy<0>; + + int on_arrange_call_count = 0; + auto on_arranged_fn = [&on_arrange_call_count](ArrItem &itm, + auto &bed, auto &packed, + auto &rem) { + ++on_arrange_call_count; + + Slic3r::arr2::firstfit::DefaultOnArrangedFn{}(itm, bed, packed, rem); + }; + + int cancel_call_count = 0; + auto stop_cond = [&cancel_call_count] { + ++cancel_call_count; + return false; + }; + + WHEN ("attempting to pack a single item with a valid bed index") + { + auto items = create_items_n(1); + + Slic3r::arr2::set_bed_index(items.front(), random_value(0, 1000)); + + auto sel = Slic3r::arr2::firstfit::SelectionStrategy{Cmp{}, on_arranged_fn, stop_cond}; + + Slic3r::arr2::arrange(sel, + PackStrategy{}, + Slic3r::range(items), + bed); + + THEN ("the original bed index should be ignored and set to Unarranged") + { + REQUIRE(Slic3r::arr2::get_bed_index(items.front()) == + Slic3r::arr2::Unarranged); + } + + THEN("the arrange callback should not be called") + { + REQUIRE(on_arrange_call_count == 0); + } + + THEN("the stop condition should be called at least once") + { + REQUIRE(cancel_call_count > 0); + } + } + + WHEN("attempting to pack arbitrary number > 1 of items into the bed") + { + auto items = create_items_n(random_value(1, 100)); + + CHECK(cancel_call_count == 0); + CHECK(on_arrange_call_count == 0); + + Slic3r::arr2::arrange( + Slic3r::arr2::firstfit::SelectionStrategy{Cmp{}, on_arranged_fn, stop_cond}, + PackStrategy{}, Slic3r::range(items), bed); + + THEN("The item should be left unpacked") + { + REQUIRE(std::all_of(items.begin(), items.end(), [](auto &itm) { + return !Slic3r::arr2::is_arranged(itm); + })); + } + + THEN("the arrange callback should not be called") + { + REQUIRE(on_arrange_call_count == 0); + } + + THEN("the stop condition should be called at least once for each item") + { + INFO("items count = " << items.size()); + REQUIRE(cancel_call_count >= static_cast(items.size())); + } + } + } + + GIVEN("A pack strategy that accepts only a single item") + { + using PackStrategy = RectangleToCenterPackStrategy<1>; + + WHEN ("attempting to pack a single item with a valid bed index") + { + auto items = create_items_n(1); + + Slic3r::arr2::set_bed_index(items.front(), random_value(0, 1000)); + + Slic3r::arr2::arrange(Slic3r::arr2::firstfit::SelectionStrategy<>{}, + PackStrategy{}, + Slic3r::range(items), + bed); + + THEN ("the original bed index should be ignored and set to zero") + { + REQUIRE(Slic3r::arr2::get_bed_index(items.front()) == 0); + } + } + + WHEN("attempting to pack arbitrary number > 1 of items into bed") + { + auto items = create_items_n(random_value(1, 100)); + + Slic3r::arr2::arrange(Slic3r::arr2::firstfit::SelectionStrategy<>{}, + PackStrategy{}, + Slic3r::range(items), + bed); + + THEN ("The number of beds created should match the number of items") + { + auto bed_count = Slic3r::arr2::get_bed_count(Slic3r::range(items)); + + REQUIRE(bed_count == items.size()); + } + + THEN("All items should reside on their respective beds") + { + for (size_t i = 0; i < items.size(); ++i) + REQUIRE(Slic3r::arr2::get_bed_index(items[i]) == static_cast(i)); + } + } + } + + GIVEN ("Two packed beds with an unpacked bed between them") + { + using PackStrategy = RectangleToCenterPackStrategy<1>; + + auto bed = Slic3r::arr2::RectangleBed(scaled(100.), scaled(100.)); + auto fixed = create_items_n(2); + std::for_each(fixed.begin(), fixed.end(), [&bed](auto &itm){ + Slic3r::arr2::pack(PackStrategy{}, bed, itm); + }); + for (auto [i, idx] : {std::make_pair(0, 0), std::make_pair(1, 2)}) + Slic3r::arr2::set_bed_index(fixed[i], idx); + + WHEN("attempting to pack a single item") + { + auto items = create_items_n(1); + + Slic3r::arr2::arrange(Slic3r::arr2::firstfit::SelectionStrategy<>{}, + PackStrategy{}, + Slic3r::range(items), + Slic3r::crange(fixed), + bed); + + THEN("the item should end up on the first free bed") + { + REQUIRE(Slic3r::arr2::get_bed_index(items.front()) == 1); + } + } + } + + GIVEN ("A 100 items with increasing priorities and a packer that accepts 20 items") + { + static constexpr int Capacity = 20; + static constexpr int Count = 5 * Capacity; + + using PackStrategy = RectangleToCenterPackStrategy; + auto items = create_items_n(Count); + + for (size_t i = 0; i < items.size(); ++i) + items[i].priority = static_cast(i); + + WHEN("attempting to pack all items") + { + auto on_arranged_fn = [](ArrItem &itm, auto &bed, auto &packed, + auto &rem) { + itm.packed_num = packed.size(); + Slic3r::arr2::firstfit::DefaultOnArrangedFn{}(itm, bed, packed, rem); + }; + + Slic3r::arr2::arrange(Slic3r::arr2::firstfit::SelectionStrategy{Cmp{}, on_arranged_fn}, + PackStrategy{}, + Slic3r::range(items), + bed); + + THEN("all items should fit onto the beds from index 0 to 4") + { + REQUIRE(std::all_of(items.begin(), items.end(), [](auto &itm) { + auto bed_idx = Slic3r::arr2::get_bed_index(itm); + return bed_idx >= 0 && bed_idx < Count / Capacity; + })); + } + + // Highest priority goes first + THEN("all the items should be packed in reverse order of their priority value") + { + REQUIRE(std::all_of(items.begin(), items.end(), [](const ArrItem &itm) { + return itm.packed_num == (99 - itm.priority); + })); + } + } + } +} + +template<> +Slic3r::BoundingBox Slic3r::arr2::NFPArrangeItemTraits_< + RectangleItem>::envelope_bounding_box(const RectangleItem &itm) +{ + return itm.shape; +} + +template<> +Slic3r::Vec2crd Slic3r::arr2::NFPArrangeItemTraits_< + RectangleItem>::reference_vertex(const RectangleItem &itm) +{ + return itm.shape.center(); +} + +TEST_CASE("Optimal nfp position search with GravityKernel using RectangleItem and InfiniteBed", + "[arrange2]") +{ + auto bed = Slic3r::arr2::InfiniteBed{}; + auto strategy = Slic3r::arr2::PackStrategyNFP{Slic3r::arr2::GravityKernel{bed.center}}; + + GIVEN("An nfp made of a single point coincident with the bed center") + { + WHEN ("searching for optimal position") + { + THEN ("the optimum should be at the single nfp point") + { + Slic3r::ExPolygons nfp; + nfp.emplace_back(Slic3r::ExPolygon{{bed.center}}); + + auto item = RectangleItem{}; + + double score = pick_best_spot_on_nfp_verts_only(item, nfp, bed, strategy); + + Slic3r::Vec2crd D = bed.center - item.shape.center(); + REQUIRE(item.translation == D); + REQUIRE(score == Approx(0.)); + } + } + } +} + +TEST_CASE("RectangleOverfitPackingStrategy test", "[arrange2]") +{ + using Slic3r::arr2::RectangleOverfitPackingStrategy; + using Slic3r::arr2::PackStrategyNFP; + using Slic3r::arr2::GravityKernel; + using Slic3r::arr2::get_bed_index; + + namespace firstfit = Slic3r::arr2::firstfit; + + using ArrItem = Slic3r::arr2::SimpleArrangeItem; + + auto frontleft_align_fn = [](const Slic3r::BoundingBox &bedbb, + const Slic3r::BoundingBox &pilebb) { + return bedbb.min - pilebb.min; + }; + + RectangleOverfitPackingStrategy pstrategy{PackStrategyNFP{GravityKernel{}}, + frontleft_align_fn}; + + auto bed = Slic3r::arr2::RectangleBed{scaled(100.), scaled(100.)}; + auto item_blueprint = Slic3r::arr2::to_rectangle( + Slic3r::BoundingBox{{0, 0}, {scaled(20.), scaled(20.)}}); + + auto item_gen_fn = [&item_blueprint] { return ArrItem{item_blueprint}; }; + + GIVEN("One empty logical rectangular 100x100 mm bed ") { + + WHEN("attempting to pack one rectangle") { + constexpr auto count = size_t{1}; + auto items = Slic3r::reserve_vector(count); + + std::generate_n(std::back_inserter(items), count, item_gen_fn); + + Slic3r::arr2::arrange(firstfit::SelectionStrategy<>{}, pstrategy, + Slic3r::range(items), bed); + + THEN ("Overfit kernel should take over and align the single item") { + auto pilebb = bounding_box(Slic3r::range(items)); + + Slic3r::Vec2crd D = frontleft_align_fn(bounding_box(bed), pilebb); + REQUIRE(D.squaredNorm() == 0); + } + } + + WHEN("attempting to pack two rectangles") { + + constexpr auto count = size_t{2}; + auto items = Slic3r::reserve_vector(count); + + std::generate_n(std::back_inserter(items), count, item_gen_fn); + + Slic3r::arr2::arrange(firstfit::SelectionStrategy<>{}, pstrategy, + Slic3r::range(items), bed); + + THEN("Overfit kernel should take over and align the single item") + { + auto pilebb = bounding_box(Slic3r::range(items)); + + Slic3r::Vec2crd D = frontleft_align_fn(bounding_box(bed), pilebb); + REQUIRE(D.squaredNorm() == 0); + } + } + } + + GIVEN("Two logical rectangular beds, the second having fixed items") { + + auto fixed_item_bb = Slic3r::BoundingBox{{0, 0}, {scaled(20.), scaled(20.)}}; + std::vector fixed = { + ArrItem{Slic3r::arr2::to_rectangle(fixed_item_bb)}}; + + Slic3r::arr2::set_bed_index(fixed.front(), 1); + + WHEN("attempting to pack 3 rectangles, 1 filling the first bed") { + + auto items = Slic3r::reserve_vector(3); + + // Add a big rectangle this will fill the first bed so that + // smaller rectangles will fit only into the next bed + items.emplace_back(ArrItem{Slic3r::arr2::to_rectangle( + Slic3r::BoundingBox{{0, 0}, {scaled(90.), scaled(90.)}})}); + + std::generate_n(std::back_inserter(items), 2, item_gen_fn); + + Slic3r::arr2::arrange(firstfit::SelectionStrategy<>{}, pstrategy, + Slic3r::range(items), Slic3r::crange(fixed), + bed); + + THEN("Overfit kernel should handle the 0th bed and gravity kernel handles the 1st bed") + { + REQUIRE(get_bed_index(items.front()) == 0); + + auto pilebb = bounding_box_on_bedidx(Slic3r::range(items), 0); + Slic3r::Vec2crd D = frontleft_align_fn(bounding_box(bed), pilebb); + REQUIRE(D.squaredNorm() == 0); + + REQUIRE((get_bed_index(items[1]) == get_bed_index(items[2]) == 1)); + + auto pilebb1 = bounding_box_on_bedidx(Slic3r::range(items), 1); + REQUIRE(pilebb1.overlap(fixed_item_bb)); + + Slic3r::Vec2crd D1 = frontleft_align_fn(bounding_box(bed), pilebb1); + REQUIRE(D1.squaredNorm() != 0); + } + } + } +} + +TEMPLATE_TEST_CASE("Test if allowed item rotations are considered", "[arrange2]", + Slic3r::arr2::ArrangeItem) +{ + using ArrItem = TestType; + + auto item_blueprint = Slic3r::arr2::to_rectangle( + Slic3r::BoundingBox{{0, 0}, {scaled(20.), scaled(20.)}}); + + ArrItem itm{item_blueprint}; + + auto bed = Slic3r::arr2::RectangleBed{scaled(100.), scaled(100.)}; + + set_allowed_rotations(itm, {PI}); + + Slic3r::arr2::PackStrategyNFP strategy{Slic3r::arr2::GravityKernel{}}; + + bool packed = pack(strategy, bed, itm); + + REQUIRE(packed); + REQUIRE(get_rotation(itm) == Approx(PI)); +} + +//TEST_CASE("NFP optimizing test", "[arrange2]") { +// using namespace Slic3r; + +// auto itemshape = arr2::to_rectangle(BoundingBox{{scaled(-25.), scaled(-25.)}, {scaled(25.), scaled(25.)}}); + +// arr2::ArrangeItem item{arr2::DecomposedShape{itemshape}}; + +// ExPolygons nfp = { ExPolygon {{scaled(-2000.), scaled(25.)}, {scaled(2000.), scaled(25.)}} }; + +// struct K : public arr2::GravityKernel { +// size_t &fncnt; +// K(size_t &counter, Vec2crd gpos): arr2::GravityKernel{gpos}, fncnt{counter} {} +// double placement_fitness(const arr2::ArrangeItem &itm, const Vec2crd &tr) const +// { +// ++fncnt; +// return arr2::GravityKernel::placement_fitness(itm, tr); +// } +// }; + +// size_t counter = 0; +// K k{counter, Vec2crd{0, 0}}; +// opt::Optimizer solver_ref({}, 1000); +// opt::Optimizer solver (opt::StopCriteria{} +// .max_iterations(1000) +// /*.rel_score_diff(1e-20)*/); + +// double accuracy = 1.; +// arr2::PackStrategyNFP strategy_ref(solver_ref, k, ex_seq, accuracy); +// arr2::PackStrategyNFP strategy(solver, k, ex_seq, accuracy); + +// SVG svg("nfp_optimizing.svg"); +// svg.flipY = true; +// svg.draw_outline(nfp, "green"); + +// svg.draw_outline(item.shape().transformed_outline(), "yellow"); + +// double score = pick_best_spot_on_nfp(item, nfp, arr2::InfiniteBed{}, strategy); +// svg.draw_outline(item.shape().transformed_outline(), "red"); + +// counter = 0; +// double score_ref = pick_best_spot_on_nfp(item, nfp, arr2::InfiniteBed{}, strategy_ref); +// svg.draw_outline(item.shape().transformed_outline(), "blue"); + +//#ifndef NDEBUG +// std::cout << "fitness called: " << k.fncnt << " times" << std::endl; +// std::cout << "score = " << score << " score_ref = " << score_ref << std::endl; +//#endif + +// REQUIRE(!std::isnan(score)); +// REQUIRE(!std::isnan(score_ref)); +// REQUIRE(score >= score_ref); +//} + + diff --git a/tests/arrange/test_arrange_integration.cpp b/tests/arrange/test_arrange_integration.cpp new file mode 100644 index 0000000000..a7c9275600 --- /dev/null +++ b/tests/arrange/test_arrange_integration.cpp @@ -0,0 +1,1036 @@ +#include +#include "test_utils.hpp" + +#include +#include +#include + +#include + +#include "libslic3r/Model.hpp" +#include "libslic3r/Geometry/ConvexHull.hpp" +#include "libslic3r/Format/3mf.hpp" +#include "libslic3r/ModelArrange.hpp" + +static Slic3r::Model get_example_model_with_20mm_cube() +{ + using namespace Slic3r; + + Model model; + + ModelObject* new_object = model.add_object(); + new_object->name = "20mm_cube"; + new_object->add_instance(); + TriangleMesh mesh = make_cube(20., 20., 20.); + mesh.translate(Vec3f{-10.f, -10.f, 0.}); + ModelVolume* new_volume = new_object->add_volume(mesh); + new_volume->name = new_object->name; + + return model; +} + +[[maybe_unused]] +static Slic3r::Model get_example_model_with_random_cube_objects(size_t N = 0) +{ + using namespace Slic3r; + + Model model; + + auto cube_count = N == 0 ? random_value(size_t(1), size_t(100)) : N; + + INFO("Cube count " << cube_count); + + ModelObject* new_object = model.add_object(); + new_object->name = "20mm_cube"; + TriangleMesh mesh = make_cube(20., 20., 20.); + ModelVolume* new_volume = new_object->add_volume(mesh); + new_volume->name = new_object->name; + + for (size_t i = 0; i < cube_count; ++i) { + ModelInstance *inst = new_object->add_instance(); + arr2::transform_instance(*inst, + Vec2d{random_value(-200., 200.), + random_value(-200., 200.)}, + random_value(0., 2 * PI)); + } + + return model; +} + +static Slic3r::Model get_example_model_with_arranged_primitives() +{ + using namespace Slic3r; + + Model model; + + ModelObject* new_object = model.add_object(); + new_object->name = "20mm_cube"; + ModelInstance *cube_inst = new_object->add_instance(); + TriangleMesh mesh = make_cube(20., 20., 20.); + mesh.translate(Vec3f{-10.f, -10.f, 0.}); + ModelVolume* new_volume = new_object->add_volume(mesh); + new_volume->name = new_object->name; + + ModelInstance *inst = new_object->add_instance(*cube_inst); + auto tr = inst->get_matrix(); + tr.translate(Vec3d{25., 0., 0.}); + inst->set_transformation(Geometry::Transformation{tr}); + + new_object = model.add_object(); + new_object->name = "20mm_cyl"; + new_object->add_instance(); + mesh = make_cylinder(10., 20.); + mesh.translate(Vec3f{0., -25.f, 0.}); + new_volume = new_object->add_volume(mesh); + new_volume->name = new_object->name; + + new_object = model.add_object(); + new_object->name = "20mm_sphere"; + new_object->add_instance(); + mesh = make_sphere(10.); + mesh.translate(Vec3f{25., -25.f, 0.}); + new_volume = new_object->add_volume(mesh); + new_volume->name = new_object->name; + + return model; +} + + +class RandomArrangeSettings: public Slic3r::arr2::ArrangeSettingsView { + Slic3r::arr2::ArrangeSettingsDb::Values m_v; + + std::mt19937 m_rng; +public: + explicit RandomArrangeSettings(int seed) : m_rng(seed) + { + std::uniform_real_distribution fdist(0., 100.f); + std::uniform_int_distribution<> bdist(0, 1); + std::uniform_int_distribution<> dist; + m_v.d_obj = fdist(m_rng); + m_v.d_bed = fdist(m_rng); + m_v.rotations = bdist(m_rng); + m_v.geom_handling = static_cast(dist(m_rng) % ghCount); + m_v.arr_strategy = static_cast(dist(m_rng) % asCount); + m_v.xl_align = static_cast(dist(m_rng) % xlpCount); + } + explicit RandomArrangeSettings() : m_rng(std::random_device{} ()) {} + + float get_distance_from_objects() const override { return m_v.d_obj; } + float get_distance_from_bed() const override { return m_v.d_bed; } + bool is_rotation_enabled() const override { return m_v.rotations; } + XLPivots get_xl_alignment() const override { return m_v.xl_align; } + GeometryHandling get_geometry_handling() const override { return m_v.geom_handling; } + ArrangeStrategy get_arrange_strategy() const override { return m_v.arr_strategy; } +}; + + +TEST_CASE("ModelInstance should be retrievable when imbued into ArrangeItem", + "[arrange2][integration]") +{ + using namespace Slic3r; + + Model model = get_example_model_with_20mm_cube(); + auto mi = model.objects.front()->instances.front(); + + arr2::ArrangeItem itm; + arr2::PhysicalOnlyVBedHandler vbedh; + auto vbedh_ptr = static_cast(&vbedh); + auto arrbl = arr2::ArrangeableModelInstance{mi, vbedh_ptr, nullptr, {0, 0}}; + arr2::imbue_id(itm, arrbl.id()); + + std::optional id_returned = arr2::retrieve_id(itm); + + REQUIRE((id_returned && *id_returned == mi->id())); +} + +struct PhysicalBed +{ + Slic3r::arr2::InfiniteBed bed; + Slic3r::arr2::PhysicalOnlyVBedHandler vbedh; + int bed_idx_min = 0, bed_idx_max = 0; +}; + +struct XStriderBed +{ + Slic3r::arr2::RectangleBed bed; + Slic3r::arr2::XStriderVBedHandler vbedh; + int bed_idx_min = 0, bed_idx_max = 100; + + XStriderBed() : + bed{Slic3r::scaled(250.), Slic3r::scaled(210.)}, + vbedh{bounding_box(bed), bounding_box(bed).size().x() / 10} {} +}; + +TEMPLATE_TEST_CASE("Writing arrange transformations into ModelInstance should be correct", + "[arrange2][integration]", + PhysicalBed, + XStriderBed) +{ + auto [tx, ty, rot] = GENERATE(map( + [](int i) { + return std::make_tuple(-500. + i * 20., -500. + i * 20., + -PI + i * (2 * PI / 100.)); + }, + range(0, 100))); + + using namespace Slic3r; + + Model model = get_example_model_with_20mm_cube(); + + auto transl = scaled(Vec2d(tx, ty)); + + INFO("Translation = : " << transl.transpose()); + INFO("Rotation is: " << rot * 180 / PI); + + auto mi = model.objects.front()->instances.front(); + + BoundingBox bb_before = scaled(to_2d(arr2::instance_bounding_box(*mi))); + + TestType bed_case; + auto bed_index = random_value(bed_case.bed_idx_min, bed_case.bed_idx_max); + + bed_case.vbedh.assign_bed(arr2::VBedPlaceableMI{*mi}, bed_index); + INFO("bed_index = " << bed_index); + + auto builder = arr2::SceneBuilder{} + .set_bed(bed_case.bed) + .set_model(model) + .set_arrange_settings(arr2::ArrangeSettings{}.set_distance_from_objects(0.)) + .set_virtual_bed_handler(&bed_case.vbedh); + + arr2::Scene scene{std::move(builder)}; + + using ArrItem = arr2::ArrangeItem; + + auto cvt = arr2::ArrangeableToItemConverter::create(scene); + + ArrItem itm; + scene.model().visit_arrangeable(model.objects.front()->instances.front()->id(), + [&cvt, &itm](const arr2::Arrangeable &arrbl){ + itm = cvt->convert(arrbl); + }); + + BoundingBox bb_itm_before = arr2::fixed_bounding_box(itm); + REQUIRE((bb_itm_before.min - bb_before.min).norm() < SCALED_EPSILON); + REQUIRE((bb_itm_before.max - bb_before.max).norm() < SCALED_EPSILON); + + arr2::rotate(itm, rot); + arr2::translate(itm, transl); + arr2::set_bed_index(itm, arr2::PhysicalBedId); + + if (auto id = retrieve_id(itm)) { + scene.model().visit_arrangeable(*id, [&itm](arr2::Arrangeable &arrbl) { + arrbl.transform(unscaled(get_translation(itm)), get_rotation(itm)); + }); + } + + auto phys_tr = bed_case.vbedh.get_physical_bed_trafo(bed_index); + auto outline = arr2::extract_convex_outline(*mi, phys_tr); + BoundingBox bb_after = get_extents(outline); + BoundingBox bb_itm_after = arr2::fixed_bounding_box(itm); + REQUIRE((bb_itm_after.min - bb_after.min).norm() < 2 * SCALED_EPSILON); + REQUIRE((bb_itm_after.max - bb_after.max).norm() < 2 * SCALED_EPSILON); +} + +struct OutlineExtractorConvex { + auto operator() (const Slic3r::ModelInstance *mi) + { + return Slic3r::arr2::extract_convex_outline(*mi); + } +}; + +struct OutlineExtractorFull { + auto operator() (const Slic3r::ModelInstance *mi) + { + return Slic3r::arr2::extract_full_outline(*mi); + } +}; + +TEMPLATE_TEST_CASE("Outline extraction from ModelInstance", + "[arrange2][integration]", + OutlineExtractorConvex, + OutlineExtractorFull) +{ + using namespace Slic3r; + using OutlineExtractor = TestType; + + Model model = get_example_model_with_20mm_cube(); + + ModelInstance *mi = model.objects.front()->instances.front(); + auto matrix = mi->get_matrix(); + matrix.scale(Vec3d{random_value(0.1, 5.), + random_value(0.1, 5.), + random_value(0.1, 5.)}); + + matrix.rotate(Eigen::AngleAxisd(random_value(-PI, PI), Vec3d::UnitZ())); + + matrix.translate(Vec3d{random_value(-100., 100.), + random_value(-100., 100.), + random_value(0., 100.)}); + + mi->set_transformation(Geometry::Transformation{matrix}); + + GIVEN("An empty ModelInstance without mesh") + { + const ModelInstance *mi = model.add_object()->add_instance(); + + WHEN("the outline is generated") { + auto outline = OutlineExtractor{}(mi); + + THEN ("the outline is empty") { + REQUIRE(outline.empty()); + } + } + } + + GIVEN("A simple cube as outline") { + const ModelInstance *mi = model.objects.front()->instances.front(); + + WHEN("the outline is generated") { + auto outline = OutlineExtractor{}(mi); + + THEN("the 2D ortho projection of the model bounding box is the " + "same as the outline's bb") + { + auto bb = unscaled(get_extents(outline)); + auto modelbb = to_2d(model.bounding_box_exact()); + + REQUIRE((bb.min - modelbb.min).norm() < EPSILON); + REQUIRE((bb.max - modelbb.max).norm() < EPSILON); + } + } + } +} + +template +auto create_vbed_handler(const Slic3r::BoundingBox &bedbb, coord_t gap) +{ + return VBH{}; +} + +template<> +auto create_vbed_handler(const Slic3r::BoundingBox &bedbb, coord_t gap) +{ + return Slic3r::arr2::PhysicalOnlyVBedHandler{}; +} + +template<> +auto create_vbed_handler(const Slic3r::BoundingBox &bedbb, coord_t gap) +{ + return Slic3r::arr2::XStriderVBedHandler{bedbb, gap}; +} + +template<> +auto create_vbed_handler(const Slic3r::BoundingBox &bedbb, coord_t gap) +{ + return Slic3r::arr2::YStriderVBedHandler{bedbb, gap}; +} + +TEMPLATE_TEST_CASE("Common virtual bed handlers", "[arrange2][integration][vbeds]", + Slic3r::arr2::PhysicalOnlyVBedHandler, + Slic3r::arr2::XStriderVBedHandler, + Slic3r::arr2::YStriderVBedHandler) +{ + using namespace Slic3r; + using VBP = arr2::VBedPlaceableMI; + + Model model = get_example_model_with_20mm_cube(); + + const auto bedsize = Vec2d{random_value(21., 500.), random_value(21., 500.)}; + + const Vec2crd bed_displace = {random_value(scaled(-100.), scaled(100.)), + random_value(scaled(-100.), scaled(100.))}; + + const BoundingBox bedbb{bed_displace, scaled(bedsize) + bed_displace}; + + INFO("Bed boundaries bedbb = { {" << unscaled(bedbb.min).transpose() << "}, {" + << unscaled(bedbb.max).transpose() << "} }" ); + + auto modelbb = model.bounding_box_exact(); + + // Center the single instance within the model + arr2::transform_instance(*model.objects.front()->instances.front(), + unscaled(bedbb.center()) - to_2d(modelbb.center()), + 0.); + + const auto vbed_gap = GENERATE(0, random_value(1, scaled(100.))); + + INFO("vbed_gap = " << unscaled(vbed_gap)); + + std::unique_ptr vbedh = std::make_unique( + create_vbed_handler(bedbb, vbed_gap)); + + GIVEN("A ModelInstance on the physical bed") + { + ModelInstance *mi = model.objects.front()->instances.front(); + + WHEN ("trying to move the item to an invalid bed index") + { + auto &mi_to_move = *model.objects.front()->add_instance(*mi); + Transform3d mi_trafo_before = mi_to_move.get_matrix(); + bool was_accepted = vbedh->assign_bed(VBP{mi_to_move}, arr2::Unarranged); + + Transform3d mi_trafo_after = mi_to_move.get_matrix(); + + THEN("the model instance should be unchanged") { + REQUIRE(!was_accepted); + REQUIRE(mi_trafo_before.isApprox(mi_trafo_after)); + } + } + } + + GIVEN("A ModelInstance being assigned to a virtual bed") + { + ModelInstance *mi = model.objects.front()->instances.front(); + + auto bedidx_to = GENERATE(random_value(-1000, -1), 0, random_value(1, 1000)); + INFO("bed index = " << bedidx_to); + + auto &mi_to_move = *model.objects.front()->add_instance(*mi); + + // Move model instance to the given virtual bed + bool was_accepted = vbedh->assign_bed(VBP{mi_to_move}, bedidx_to); + + WHEN ("querying the virtual bed index of this item") + { + int bedidx_on = vbedh->get_bed_index(VBP{mi_to_move}); + + THEN("should actually be on that bed, or the assign should be discarded") { + REQUIRE(((!was_accepted) || (bedidx_to == bedidx_on))); + } + + THEN("assigning the same bed index again should produce the same result") + { + auto &mi_to_move_cpy = *model.objects.front()->add_instance(mi_to_move); + bool was_accepted_rep = vbedh->assign_bed(VBP{mi_to_move_cpy}, bedidx_to); + int bedidx_on_rep = vbedh->get_bed_index(VBP{mi_to_move_cpy}); + + REQUIRE(was_accepted_rep == was_accepted); + REQUIRE(((!was_accepted_rep) || (bedidx_to == bedidx_on_rep))); + } + } + + WHEN ("moving back to the physical bed") + { + auto &mi_back_to_phys = *model.objects.front()->add_instance(mi_to_move); + bool moved_back_to_physical = vbedh->assign_bed(VBP{mi_back_to_phys}, 0); + + THEN("model instance should actually move back to the physical bed") + { + REQUIRE(moved_back_to_physical); + int bedidx_mi2 = vbedh->get_bed_index(VBP{mi_back_to_phys}); + REQUIRE(bedidx_mi2 == 0); + } + + THEN("the bounding box should be inside bed") + { + auto bbf = arr2::instance_bounding_box(mi_back_to_phys); + auto bb = BoundingBox{scaled(to_2d(bbf))}; + INFO("bb = { {" << unscaled(bb.min).transpose() << "}, {" + << unscaled(bb.max).transpose() << "} }" ); + + REQUIRE(bedbb.contains(bb)); + } + } + + WHEN("extracting transformed model instance bounding box using the " + "physical bed trafo") + { + int from_bed_idx = vbedh->get_bed_index(VBP{mi_to_move}); + auto physical_bed_trafo = vbedh->get_physical_bed_trafo(from_bed_idx); + + auto &mi_back_to_phys = *model.objects.front()->add_instance(mi_to_move); + mi_back_to_phys.set_transformation(Geometry::Transformation{ + physical_bed_trafo * mi_back_to_phys.get_matrix()}); + + auto bbf = arr2::instance_bounding_box(mi_back_to_phys); + + auto bb = BoundingBox{scaled(to_2d(bbf))}; + + THEN("the bounding box should be inside bed") + { + INFO("bb = { {" << unscaled(bb.min).transpose() << "}, {" + << unscaled(bb.max).transpose() << "} }" ); + + REQUIRE(bedbb.contains(bb)); + } + + THEN("the outline should be inside the physical bed") + { + auto outline = arr2::extract_convex_outline(mi_to_move, + physical_bed_trafo); + auto bb = get_extents(outline); + INFO("bb = { {" << bb.min.transpose() << "}, {" + << bb.max.transpose() << "} }" ); + + REQUIRE(bedbb.contains(bb)); + } + } + } +} + +TEST_CASE("Virtual bed handlers - StriderVBedHandler", "[arrange2][integration][vbeds]") +{ + using namespace Slic3r; + using VBP = arr2::VBedPlaceableMI; + + Model model = get_example_model_with_20mm_cube(); + + static const Vec2d bedsize{250., 210.}; + static const BoundingBox bedbb{{0, 0}, scaled(bedsize)}; + static const auto modelbb = model.bounding_box_exact(); + + GIVEN("An instance of StriderVBedHandler with a stride of the bed width" + " and random non-negative gap") + { + auto [instance_pos, instance_displace] = GENERATE(table({ + {"start", unscaled(bedbb.min) - to_2d(modelbb.min) + Vec2d::Ones() * EPSILON}, // at the min edge of vbed + {"middle", unscaled(bedbb.center()) - to_2d(modelbb.center())}, // at the center + {"end", unscaled(bedbb.max) - to_2d(modelbb.max) - Vec2d::Ones() * EPSILON} // at the max edge of vbed + })); + + // Center the single instance within the model + arr2::transform_instance(*model.objects.front()->instances.front(), + instance_displace, + 0.); + + INFO("Instance pos at " << instance_pos << " of bed"); + + coord_t gap = GENERATE(0, random_value(1, scaled(100.))); + + INFO("Gap is " << unscaled(gap)); + + arr2::XStriderVBedHandler vbh{bedbb, gap}; + + WHEN("a model instance is on the Nth virtual bed (spatially)") + { + ModelInstance *mi = model.objects.front()->instances.front(); + auto &mi_to_move = *model.objects.front()->add_instance(*mi); + + auto bed_index = GENERATE(random_value(-1000, -1), 0, random_value(1, 1000)); + INFO("N is " << bed_index); + + double bed_disp = bed_index * unscaled(vbh.stride_scaled()); + arr2::transform_instance(mi_to_move, Vec2d{bed_disp, 0.}, 0.); + + THEN("the bed index of this model instance should be max(0, N)") + { + REQUIRE(vbh.get_bed_index(VBP{mi_to_move}) == bed_index); + } + + THEN("the physical trafo should move the instance back to bed 0") + { + auto tr = vbh.get_physical_bed_trafo(bed_index); + mi_to_move.set_transformation(Geometry::Transformation{tr * mi_to_move.get_matrix()}); + REQUIRE(vbh.get_bed_index(VBP{mi_to_move}) == 0); + + auto instbb = BoundingBox{scaled(to_2d(arr2::instance_bounding_box(mi_to_move)))}; + INFO("bedbb = { {" << bedbb.min.transpose() << "}, {" << bedbb.max.transpose() << "} }" ); + INFO("instbb = { {" << instbb.min.transpose() << "}, {" << instbb.max.transpose() << "} }" ); + + REQUIRE(bedbb.contains(instbb)); + } + } + + WHEN("a model instance is on the physical bed") + { + ModelInstance *mi = model.objects.front()->instances.front(); + auto &mi_to_move = *model.objects.front()->add_instance(*mi); + + THEN("assigning the model instance to the Nth bed will move it N*stride in the X axis") + { + auto bed_index = GENERATE(random_value(-1000, -1), 0, random_value(1, 1000)); + INFO("N is " << bed_index); + + if (vbh.assign_bed(VBP{mi_to_move}, bed_index)) + REQUIRE(vbh.get_bed_index(VBP{mi_to_move}) == bed_index); + else + REQUIRE(bed_index < 0); + + auto tr = vbh.get_physical_bed_trafo(bed_index); + auto ref_pos = tr * Vec3d::Zero(); + + auto displace = bed_index * (unscaled(vbh.stride_scaled())); + REQUIRE(ref_pos.x() == Approx(-displace)); + + auto ref_pos_mi = mi_to_move.get_matrix() * Vec3d::Zero(); + REQUIRE(ref_pos_mi.x() == Approx(instance_displace.x() + (bed_index >= 0) * displace)); + } + } + } + + GIVEN("An instance of StriderVBedHandler with a stride of the bed width" + " and a 100mm gap") + { + coord_t gap = scaled(100.); + + arr2::XStriderVBedHandler vbh{bedbb, gap}; + + WHEN("a model instance is within the gap on the Nth virtual bed") + { + ModelInstance *mi = model.objects.front()->instances.front(); + auto &mi_to_move = *model.objects.front()->add_instance(*mi); + + auto bed_index = GENERATE(random_value(-1000, -1), 0, random_value(1, 1000)); + INFO("N is " << bed_index); + + auto bed_disp = Vec2d{bed_index * unscaled(vbh.stride_scaled()), 0.}; + auto instbb_before = to_2d(arr2::instance_bounding_box(mi_to_move)); + + auto transl_to_bed_end = Vec2d{bed_disp + unscaled(bedbb.max) + - instbb_before.min + Vec2d::Ones() * EPSILON}; + + arr2::transform_instance(mi_to_move, + transl_to_bed_end + Vec2d{unscaled(gap / 2), 0.}, + 0.); + + THEN("the model instance should reside on the Nth logical bed but " + "outside of the bed boundaries") + { + REQUIRE(vbh.get_bed_index(VBP{mi_to_move}) == bed_index); + + auto instbb = BoundingBox{scaled(to_2d(arr2::instance_bounding_box(mi_to_move)))}; + INFO("bedbb = { {" << bedbb.min.transpose() << "}, {" << bedbb.max.transpose() << "} }" ); + INFO("instbb = { {" << instbb.min.transpose() << "}, {" << instbb.max.transpose() << "} }" ); + + REQUIRE(! bedbb.contains(instbb)); + } + } + } +} + +TEMPLATE_TEST_CASE("Bed needs to be completely filled with 1cm cubes", + "[arrange2][integration][bedfilling]", + Slic3r::arr2::ArrangeItem) +{ + using namespace Slic3r; + using ArrItem = TestType; + + std::string basepath = TEST_DATA_DIR PATH_SEPARATOR; + + DynamicPrintConfig cfg; + cfg.load_from_ini(basepath + "default_fff.ini", + ForwardCompatibilitySubstitutionRule::Enable); + cfg.set_key_value("bed_shape", + new ConfigOptionPoints( + {{0., 0.}, {100., 0.}, {100., 100.}, {0, 100.}})); + + Model m; + + ModelObject* new_object = m.add_object(); + new_object->name = "10mm_box"; + new_object->add_instance(); + TriangleMesh mesh = make_cube(10., 10., 10.); + ModelVolume* new_volume = new_object->add_volume(mesh); + new_volume->name = new_object->name; + + store_3mf("fillbed_10mm.3mf", &m, &cfg, false); + + arr2::ArrangeSettings settings; + settings.values().d_obj = 0.; + settings.values().d_bed = 0.; + + arr2::FixedSelection sel({{true}}); + + arr2::Scene scene{arr2::SceneBuilder{} + .set_model(m) + .set_arrange_settings(settings) + .set_selection(&sel) + .set_bed(cfg)}; + + auto task = arr2::FillBedTask::create(scene); + auto result = task->process_native(arr2::DummyCtl{}); + result->apply_on(scene.model()); + + store_3mf("fillbed_10mm_result.3mf", &m, &cfg, false); + + Points bedpts = get_bed_shape(cfg); + arr2::ArrangeBed bed = arr2::to_arrange_bed(bedpts); + + REQUIRE(bed.which() == 1); // Rectangle bed + + auto bedbb = unscaled(bounding_box(bed)); + auto bedbbsz = bedbb.size(); + + REQUIRE(m.objects.size() == 1); + REQUIRE(m.objects.front()->instances.size() == + bedbbsz.x() * bedbbsz.y() / 100); + + REQUIRE(task->unselected.empty()); + REQUIRE(result->to_add.size() + result->arranged_items.size() == arr2::model_instance_count(m)); + + REQUIRE( + std::all_of(task->selected.begin(), task->selected.end(), [](auto &itm) { + return arr2::get_bed_index(itm) == 0; + })); + REQUIRE( + std::all_of(result->to_add.begin(), result->to_add.end(), [](auto &itm) { + return arr2::get_bed_index(itm) == 0; + })); +} + +template +static void foreach_combo(const Slic3r::Range &range, const Fn &fn) +{ + std::vector pairs(range.size(), false); + + assert(range.size() >= 2); + pairs[range.size() - 1] = true; + pairs[range.size() - 2] = true; + + do { + std::vector::value_type> items; + for (size_t i = 0; i < pairs.size(); i++) { + if (pairs[i]) { + auto it = range.begin(); + std::advance(it, i); + items.emplace_back(*it); + } + } + fn (items[0], items[1]); + } while (std::next_permutation(pairs.begin(), pairs.end())); +} + +TEST_CASE("Testing minimum area bounding box rotation on simple cubes", "[arrange2][integration]") +{ + using namespace Slic3r; + + BoundingBox bb{Point::Zero(), scaled(Vec2d(10., 10.))}; + Polygon sh = arr2::to_rectangle(bb); + + auto prot = random_value(0., 2 * PI); + sh.translate(Vec2crd{random_value(-scaled(10.), scaled(10.)), + random_value(-scaled(10.), scaled(10.))}); + sh.rotate(prot); + + INFO("box item is rotated by: " << prot << " rads"); + + arr2::ArrangeItem itm{sh}; + arr2::rotate(itm, random_value(0., 2 * PI)); + + double rot = arr2::get_min_area_bounding_box_rotation(itm); + + arr2::translate(itm, + Vec2crd{random_value(-scaled(10.), scaled(10.)), + random_value(-scaled(10.), scaled(10.))}); + arr2::rotate(itm, rot); + + auto itmbb = arr2::fixed_bounding_box(itm); + REQUIRE(std::abs(itmbb.size().norm() - bb.size().norm()) < + SCALED_EPSILON * SCALED_EPSILON); +} + +template +bool is_collision_free(const Slic3r::Range &item_range) +{ + using namespace Slic3r; + + bool collision_free = true; + foreach_combo(item_range, [&collision_free](auto &itm1, auto &itm2) { + auto outline1 = offset(arr2::fixed_outline(itm1), -SCALED_EPSILON); + auto outline2 = offset(arr2::fixed_outline(itm2), -SCALED_EPSILON); + + auto inters = intersection(outline1, outline2); + collision_free = collision_free && inters.empty(); + }); + + return collision_free; +} + +TEST_CASE("Testing a simple arrange on cubes", "[arrange2][integration]") +{ + using namespace Slic3r; + + Model model = get_example_model_with_random_cube_objects(size_t{10}); + + arr2::ArrangeSettings settings; + settings.set_rotation_enabled(true); + + auto bed = arr2::RectangleBed{scaled(250.), scaled(210.)}; + + arr2::Scene scene{arr2::SceneBuilder{} + .set_model(model) + .set_arrange_settings(settings) + .set_bed(bed)}; + + auto task = arr2::ArrangeTask::create(scene); + + REQUIRE(task->printable.selected.size() == arr2::model_instance_count(model)); + + auto result = task->process_native(arr2::DummyCtl{}); + + REQUIRE(result); + + REQUIRE(result->items.size() == task->printable.selected.size()); + + bool applied = result->apply_on(scene.model()); + + REQUIRE(applied); + + REQUIRE(std::all_of(result->items.begin(), + result->items.end(), + [](auto &item) { return arr2::is_arranged(item); })); + + REQUIRE(std::all_of(task->printable.selected.begin(), task->printable.selected.end(), + [&bed](auto &item) { return bounding_box(bed).contains(arr2::envelope_bounding_box(item)); })); + + REQUIRE(std::all_of(task->unprintable.selected.begin(), task->unprintable.selected.end(), + [&bed](auto &item) { return bounding_box(bed).contains(arr2::envelope_bounding_box(item)); })); + + REQUIRE(is_collision_free(range(task->printable.selected))); + +} + +bool settings_eq(const Slic3r::arr2::ArrangeSettingsView &v1, + const Slic3r::arr2::ArrangeSettingsView &v2) +{ + return v1.is_rotation_enabled() == v2.is_rotation_enabled() && + v1.get_arrange_strategy() == v2.get_arrange_strategy() && + v1.get_distance_from_bed() == Approx(v2.get_distance_from_bed()) && + v1.get_distance_from_objects() == Approx(v2.get_distance_from_objects()) && + v1.get_geometry_handling() == v2.get_geometry_handling() && + v1.get_xl_alignment() == v2.get_xl_alignment(); + ; +} + +namespace Slic3r { namespace arr2 { + +class MocWT: public ArrangeableWipeTowerBase { +public: + using ArrangeableWipeTowerBase::ArrangeableWipeTowerBase; +}; + +class MocWTH : public WipeTowerHandler { + std::function m_sel_pred; + ObjectID m_id; + +public: + MocWTH(const ObjectID &id) : m_id{id} {} + + void visit(std::function fn) override + { + MocWT wt{m_id, Polygon{}, Point::Zero(), 0., m_sel_pred}; + fn(wt); + } + void visit(std::function fn) const override + { + MocWT wt{m_id, Polygon{}, Point::Zero(), 0., m_sel_pred}; + fn(wt); + } + void set_selection_predicate(std::function pred) override + { + m_sel_pred = std::move(pred); + } +}; + +}} // namespace Slic3r::arr2 + +TEST_CASE("Test SceneBuilder", "[arrange2][integration]") +{ + using namespace Slic3r; + + GIVEN("An empty SceneBuilder") + { + arr2::SceneBuilder bld; + + WHEN("building an ArrangeScene from it") + { + arr2::Scene scene{std::move(bld)}; + + THEN("The scene should still be initialized consistently with empty model") + { + // This would segfault if model_wt isn't initialized properly + REQUIRE(scene.model().arrangeable_count() == 0); + REQUIRE(settings_eq(scene.settings(), arr2::ArrangeSettings{})); + REQUIRE(scene.selected_ids().empty()); + } + + THEN("The associated bed should be an instance of InfiniteBed") + { + scene.visit_bed([](auto &bed){ + REQUIRE(std::is_convertible_v); + }); + } + } + + WHEN("pushing random settings into the builder") + { + RandomArrangeSettings settings; + auto bld2 = arr2::SceneBuilder{}.set_arrange_settings(&settings); + arr2::Scene scene{std::move(bld)}; + + THEN("settings of the resulting scene should be equal") + { + REQUIRE(settings_eq(scene.settings(), settings)); + } + } + } + + GIVEN("An existing instance of the class Model") + { + auto N = random_value(1, 20); + Model model = get_example_model_with_random_cube_objects(N); + INFO("model object count " << N); + + WHEN("a scene is built from a builder that holds a reference to an existing model") + { + arr2::Scene scene{arr2::SceneBuilder{}.set_model(&model)}; + + THEN("the model of the constructed scene should have the same number of arrangeables") { + REQUIRE(scene.model().arrangeable_count() == arr2::model_instance_count(model)); + } + } + } + + GIVEN("An instance of DynamicPrintConfig with rectangular bed") + { + std::string basepath = TEST_DATA_DIR PATH_SEPARATOR; + + DynamicPrintConfig cfg; + cfg.load_from_ini(basepath + "default_fff.ini", + ForwardCompatibilitySubstitutionRule::Enable); + + WHEN("a scene is built with a bed initialized from this DynamicPrintConfig") + { + arr2::Scene scene(arr2::SceneBuilder{}.set_bed(cfg)); + + auto bedbb = bounding_box(get_bed_shape(cfg)); + + THEN("the bed should be a rectangular bed with the same dimensions as the bed points") + { + scene.visit_bed([&bedbb, &scene](auto &bed) { + constexpr bool is_rect = std::is_convertible_v< + decltype(bed), arr2::RectangleBed>; + + REQUIRE(is_rect); + + if constexpr (is_rect) { + bedbb.offset(scaled(scene.settings().get_distance_from_objects() / 2.)); + REQUIRE(bedbb.size().x() == bed.width()); + REQUIRE(bedbb.size().y() == bed.height()); + } + }); + } + } + } + + GIVEN("A wipe tower handler that uses the builder's selection mask") + { + arr2::SceneBuilder bld; + Model mdl; + bld.set_model(mdl); + bld.set_wipe_tower_handler(std::make_unique(mdl.wipe_tower.id())); + + WHEN("the selection mask is initialized as a fallback default in the created scene") + { + arr2::Scene scene{std::move(bld)}; + + THEN("the wipe tower should use the fallback selmask (created after set_wipe_tower)") + { + // Should be the wipe tower + REQUIRE(scene.model().arrangeable_count() == 1); + + bool wt_selected = false; + scene.model() + .visit_arrangeable(mdl.wipe_tower.id(), + [&wt_selected]( + const arr2::Arrangeable &arrbl) { + wt_selected = arrbl.is_selected(); + }); + + REQUIRE(wt_selected); + } + } + } +} + +TEST_CASE("Testing duplicate function to really duplicate the whole Model", + "[arrange2][integration]") +{ + using namespace Slic3r; + + Model model = get_example_model_with_arranged_primitives(); + + store_3mf("dupl_example.3mf", &model, nullptr, false); + + size_t instcnt = arr2::model_instance_count(model); + + size_t copies_num = random_value(1, 10); + + INFO("Copies: " << copies_num); + + auto bed = arr2::InfiniteBed{}; + arr2::ArrangeSettings settings; + settings.set_arrange_strategy(arr2::ArrangeSettings::asPullToCenter); + arr2::DuplicableModel dup_model{&model, arr2::VirtualBedHandler::create(bed), bounding_box(bed)}; + + arr2::Scene scene{arr2::BasicSceneBuilder{} + .set_arrangeable_model(&dup_model) + .set_arrange_settings(&settings) + .set_bed(bed)}; + + auto task = arr2::MultiplySelectionTask::create(scene, copies_num); + auto result = task->process_native(arr2::DummyCtl{}); + bool applied = result->apply_on(scene.model()); + if (applied) { + dup_model.apply_duplicates(); + store_3mf("dupl_example_result.3mf", &model, nullptr, false); + REQUIRE(applied); + } + + size_t new_instcnt = arr2::model_instance_count(model); + + REQUIRE(new_instcnt == (copies_num + 1) * instcnt); + + REQUIRE(std::all_of(result->arranged_items.begin(), + result->arranged_items.end(), + [](auto &item) { return arr2::is_arranged(item); })); + + REQUIRE(std::all_of(result->to_add.begin(), + result->to_add.end(), + [](auto &item) { return arr2::is_arranged(item); })); + + REQUIRE(std::all_of(task->selected.begin(), task->selected.end(), + [&bed](auto &item) { return bounding_box(bed).contains(arr2::envelope_bounding_box(item)); })); + + REQUIRE(is_collision_free(range(task->selected))); +} + +// TODO: +//TEST_CASE("Testing fit-into-bed rotation search", "[arrange2][integration]") +//{ +// using namespace Slic3r; + +// Model model; + +// ModelObject* new_object = model.add_object(); +// new_object->name = "big_cube"; +// new_object->add_instance(); +// TriangleMesh mesh = make_cube(205., 220., 10.); +// mesh.rotate_z(15 * PI / 180); + +// ModelVolume* new_volume = new_object->add_volume(mesh); +// new_volume->name = new_object->name; + +// store_3mf("rotfail.3mf", &model, nullptr, false); + +// arr2::RectangleBed bed{scaled(250.), scaled(210.)}; + +// arr2::Scene scene{ +// arr2::SceneBuilder{} +// .set_bed(bed) +// .set_model(model) +// .set_arrange_settings(arr2::ArrangeSettings{} +// .set_distance_from_objects(0.) +// .set_rotation_enabled(true) +// ) +// }; + +// auto task = arr2::ArrangeTask::create(scene); +// auto result = task->process_native(arr2::DummyCtl{}); + +// REQUIRE(result->items.size() == 1); +// REQUIRE(arr2::get_rotation(result->items.front()) > 0.); +// REQUIRE(arr2::is_arranged(result->items.front())); +//} + diff --git a/tests/data/default_fff.ini b/tests/data/default_fff.ini new file mode 100644 index 0000000000..1b7cfa6a2d --- /dev/null +++ b/tests/data/default_fff.ini @@ -0,0 +1,313 @@ +; generated by PrusaSlicer 2.6.0-beta2 on 2023-05-30 at 07:06:06 UTC + +autoemit_temperature_commands = 1 +avoid_crossing_curled_overhangs = 0 +avoid_crossing_perimeters = 0 +avoid_crossing_perimeters_max_detour = 0 +bed_custom_model = +bed_custom_texture = +bed_shape = 0x0,250x0,250x210,0x210 +bed_temperature = 110 +before_layer_gcode = ;BEFORE_LAYER_CHANGE\nG92 E0.0\n;[layer_z]\n\n +between_objects_gcode = +bottom_fill_pattern = monotonic +bottom_solid_layers = 4 +bottom_solid_min_thickness = 0.5 +bridge_acceleration = 1000 +bridge_angle = 0 +bridge_fan_speed = 25 +bridge_flow_ratio = 0.95 +bridge_speed = 25 +brim_separation = 0.1 +brim_type = outer_only +brim_width = 0 +color_change_gcode = M600\nG1 E0.4 F1500 ; prime after color change +colorprint_heights = +compatible_printers_condition_cummulative = "printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4";"nozzle_diameter[0]!=0.8 and printer_model!=\"MINI\" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)" +complete_objects = 0 +cooling = 1 +cooling_tube_length = 5 +cooling_tube_retraction = 91.5 +default_acceleration = 1000 +default_filament_profile = "Prusament PLA" +default_print_profile = 0.15mm QUALITY @MK3 +deretract_speed = 0 +disable_fan_first_layers = 4 +dont_support_bridges = 0 +draft_shield = disabled +duplicate_distance = 6 +elefant_foot_compensation = 0.2 +enable_dynamic_fan_speeds = 0 +enable_dynamic_overhang_speeds = 1 +end_filament_gcode = "; Filament-specific end gcode" +end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+1, max_print_height)} F720 ; Move print head up{endif}\nG1 X0 Y200 F3600 ; park\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+49, max_print_height)} F720 ; Move print head further up{endif}\nG4 ; wait\nM221 S100 ; reset flow\nM900 K0 ; reset LA\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3|@0.25 nozzle MK3).*/}M907 E538 ; reset extruder motor current{endif}\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nM84 ; disable motors\n; max_layer_z = [max_layer_z] +external_perimeter_acceleration = 0 +external_perimeter_extrusion_width = 0.45 +external_perimeter_speed = 25 +external_perimeters_first = 0 +extra_loading_move = -2 +extra_perimeters = 0 +extra_perimeters_on_overhangs = 0 +extruder_clearance_height = 20 +extruder_clearance_radius = 45 +extruder_colour = "" +extruder_offset = 0x0 +extrusion_axis = E +extrusion_multiplier = 1 +extrusion_width = 0.45 +fan_always_on = 0 +fan_below_layer_time = 30 +filament_colour = #FFF2EC +filament_cooling_final_speed = 3.4 +filament_cooling_initial_speed = 2.2 +filament_cooling_moves = 4 +filament_cost = 27.82 +filament_density = 1.04 +filament_deretract_speed = nil +filament_diameter = 1.75 +filament_load_time = 0 +filament_loading_speed = 28 +filament_loading_speed_start = 3 +filament_max_volumetric_speed = 11 +filament_minimal_purge_on_wipe_tower = 15 +filament_notes = "" +filament_ramming_parameters = "120 100 5.70968 6.03226 7 8.25806 9 9.19355 9.3871 9.77419 10.129 10.3226 10.4516 10.5161| 0.05 5.69677 0.45 6.15484 0.95 8.76774 1.45 9.20323 1.95 9.95806 2.45 10.3871 2.95 10.5677 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" +filament_retract_before_travel = nil +filament_retract_before_wipe = nil +filament_retract_layer_change = nil +filament_retract_length = nil +filament_retract_lift = nil +filament_retract_lift_above = nil +filament_retract_lift_below = nil +filament_retract_restart_extra = nil +filament_retract_speed = nil +filament_settings_id = "Generic ABS" +filament_soluble = 0 +filament_spool_weight = 0 +filament_toolchange_delay = 0 +filament_type = ABS +filament_unload_time = 0 +filament_unloading_speed = 90 +filament_unloading_speed_start = 100 +filament_vendor = Generic +filament_wipe = nil +fill_angle = 45 +fill_density = 15% +fill_pattern = gyroid +first_layer_acceleration = 800 +first_layer_acceleration_over_raft = 0 +first_layer_bed_temperature = 100 +first_layer_extrusion_width = 0.42 +first_layer_height = 0.2 +first_layer_speed = 20 +first_layer_speed_over_raft = 30 +first_layer_temperature = 255 +full_fan_speed_layer = 0 +fuzzy_skin = none +fuzzy_skin_point_dist = 0.8 +fuzzy_skin_thickness = 0.3 +gap_fill_enabled = 1 +gap_fill_speed = 40 +gcode_comments = 0 +gcode_flavor = marlin +gcode_label_objects = 1 +gcode_resolution = 0.0125 +gcode_substitutions = +high_current_on_filament_swap = 0 +host_type = prusalink +idle_temperature = nil +infill_acceleration = 1000 +infill_anchor = 2.5 +infill_anchor_max = 12 +infill_every_layers = 1 +infill_extruder = 1 +infill_extrusion_width = 0.45 +infill_first = 0 +infill_overlap = 10% +infill_speed = 80 +interface_shells = 0 +ironing = 0 +ironing_flowrate = 15% +ironing_spacing = 0.1 +ironing_speed = 15 +ironing_type = top +layer_gcode = ;AFTER_LAYER_CHANGE\n;[layer_z] +layer_height = 0.2 +machine_limits_usage = emit_to_gcode +machine_max_acceleration_e = 5000,5000 +machine_max_acceleration_extruding = 1250,1250 +machine_max_acceleration_retracting = 1250,1250 +machine_max_acceleration_travel = 1500,1250 +machine_max_acceleration_x = 1000,960 +machine_max_acceleration_y = 1000,960 +machine_max_acceleration_z = 200,200 +machine_max_feedrate_e = 120,120 +machine_max_feedrate_x = 200,100 +machine_max_feedrate_y = 200,100 +machine_max_feedrate_z = 12,12 +machine_max_jerk_e = 4.5,4.5 +machine_max_jerk_x = 8,8 +machine_max_jerk_y = 8,8 +machine_max_jerk_z = 0.4,0.4 +machine_min_extruding_rate = 0,0 +machine_min_travel_rate = 0,0 +max_fan_speed = 15 +max_layer_height = 0.25 +max_print_height = 210 +max_print_speed = 200 +max_volumetric_extrusion_rate_slope_negative = 0 +max_volumetric_extrusion_rate_slope_positive = 0 +max_volumetric_speed = 0 +min_bead_width = 85% +min_fan_speed = 15 +min_feature_size = 25% +min_layer_height = 0.07 +min_print_speed = 15 +min_skirt_length = 4 +mmu_segmented_region_max_width = 0 +notes = +nozzle_diameter = 0.4 +only_retract_when_crossing_perimeters = 0 +ooze_prevention = 0 +output_filename_format = {input_filename_base}_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.gcode +overhang_fan_speed_0 = 0 +overhang_fan_speed_1 = 0 +overhang_fan_speed_2 = 0 +overhang_fan_speed_3 = 0 +overhang_speed_0 = 15 +overhang_speed_1 = 15 +overhang_speed_2 = 20 +overhang_speed_3 = 25 +overhangs = 1 +parking_pos_retraction = 92 +pause_print_gcode = M601 +perimeter_acceleration = 800 +perimeter_extruder = 1 +perimeter_extrusion_width = 0.45 +perimeter_generator = arachne +perimeter_speed = 45 +perimeters = 2 +physical_printer_settings_id = +post_process = +print_settings_id = 0.20mm QUALITY @MK3 +printer_model = MK3 +printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK3\n +printer_settings_id = Original Prusa i3 MK3 +printer_technology = FFF +printer_variant = 0.4 +printer_vendor = +raft_contact_distance = 0.2 +raft_expansion = 1.5 +raft_first_layer_density = 90% +raft_first_layer_expansion = 3 +raft_layers = 0 +remaining_times = 1 +resolution = 0 +retract_before_travel = 1 +retract_before_wipe = 0% +retract_layer_change = 1 +retract_length = 0.8 +retract_length_toolchange = 4 +retract_lift = 0.4 +retract_lift_above = 0 +retract_lift_below = 209 +retract_restart_extra = 0 +retract_restart_extra_toolchange = 0 +retract_speed = 35 +seam_position = aligned +silent_mode = 1 +single_extruder_multi_material = 0 +single_extruder_multi_material_priming = 0 +skirt_distance = 2 +skirt_height = 3 +skirts = 1 +slice_closing_radius = 0.049 +slicing_mode = regular +slowdown_below_layer_time = 20 +small_perimeter_speed = 25 +solid_infill_acceleration = 0 +solid_infill_below_area = 0 +solid_infill_every_layers = 0 +solid_infill_extruder = 1 +solid_infill_extrusion_width = 0.45 +solid_infill_speed = 80 +spiral_vase = 0 +staggered_inner_seams = 0 +standby_temperature_delta = -5 +start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.01{elsif nozzle_diameter[0]==0.6}0.02{else}0.04{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K12{elsif nozzle_diameter[0]==0.8};{else}M900 K20{endif} ; Filament gcode LA 1.0" +start_gcode = M862.3 P "[printer_model]" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.2 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n{if filament_settings_id[initial_tool]=~/.*Prusament PA11.*/}\nG1 Z0.3 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E9 F1000 ; intro line\n{else}\nG1 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E12.5 F1000 ; intro line\n{endif}\nG92 E0\nM221 S{if layer_height<0.075}100{else}95{endif}\n\n; Don't change E values below. Excessive value can damage the printer.\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3).*/}M907 E430 ; set extruder motor current{endif}\n{if print_settings_id=~/.*(SPEED @MK3|DRAFT @MK3).*/}M907 E538 ; set extruder motor current{endif} +support_material = 0 +support_material_angle = 0 +support_material_auto = 1 +support_material_bottom_contact_distance = 0 +support_material_bottom_interface_layers = 0 +support_material_buildplate_only = 0 +support_material_closing_radius = 2 +support_material_contact_distance = 0.2 +support_material_enforce_layers = 0 +support_material_extruder = 0 +support_material_extrusion_width = 0.35 +support_material_interface_contact_loops = 0 +support_material_interface_extruder = 0 +support_material_interface_layers = 2 +support_material_interface_pattern = rectilinear +support_material_interface_spacing = 0.2 +support_material_interface_speed = 80% +support_material_pattern = rectilinear +support_material_spacing = 2 +support_material_speed = 50 +support_material_style = grid +support_material_synchronize_layers = 0 +support_material_threshold = 50 +support_material_with_sheath = 0 +support_material_xy_spacing = 60% +support_tree_angle = 40 +support_tree_angle_slow = 25 +support_tree_branch_diameter = 2 +support_tree_branch_diameter_angle = 5 +support_tree_branch_diameter_double_wall = 3 +support_tree_branch_distance = 1 +support_tree_tip_diameter = 0.8 +support_tree_top_rate = 15% +temperature = 255 +template_custom_gcode = +thick_bridges = 0 +thin_walls = 0 +threads = 24 +thumbnails = 160x120 +thumbnails_format = PNG +toolchange_gcode = +top_fill_pattern = monotoniclines +top_infill_extrusion_width = 0.4 +top_solid_infill_acceleration = 0 +top_solid_infill_speed = 40 +top_solid_layers = 5 +top_solid_min_thickness = 0.7 +travel_acceleration = 0 +travel_speed = 180 +travel_speed_z = 12 +use_firmware_retraction = 0 +use_relative_e_distances = 1 +use_volumetric_e = 0 +variable_layer_height = 1 +wall_distribution_count = 1 +wall_transition_angle = 10 +wall_transition_filter_deviation = 25% +wall_transition_length = 100% +wipe = 1 +wipe_into_infill = 0 +wipe_into_objects = 0 +wipe_tower = 1 +wipe_tower_bridging = 10 +wipe_tower_brim_width = 2 +wipe_tower_cone_angle = 0 +wipe_tower_extra_spacing = 100% +wipe_tower_no_sparse_layers = 0 +wipe_tower_rotation_angle = 0 +wipe_tower_width = 60 +wipe_tower_x = 170 +wipe_tower_y = 125 +wiping_volumes_extruders = 70,70 +wiping_volumes_matrix = 0 +xy_size_compensation = 0 +z_offset = 0 diff --git a/tests/data/prusaparts.cpp b/tests/data/prusaparts.cpp new file mode 100644 index 0000000000..72d5c7d9bc --- /dev/null +++ b/tests/data/prusaparts.cpp @@ -0,0 +1,5981 @@ +#include "prusaparts.hpp" + +const TestData PRUSA_PART_POLYGONS = +{ + { + {-5000000, 8954050}, + {5000000, 8954050}, + {5000000, -45949}, + {4972609, -568550}, + {3500000, -8954050}, + {-3500000, -8954050}, + {-4972609, -568550}, + {-5000000, -45949}, + {-5000000, 8954050}, + }, + { + {-63750000, -8000000}, + {-54750000, 46000000}, + {50750000, 46000000}, + {63750000, 33000000}, + {63750000, -46000000}, + {-54750000, -46000000}, + {-63750000, -28000000}, + {-63750000, -8000000}, + }, + { + {-52750000, 41512348}, + {-31250000, 45987651}, + {52750000, 45987651}, + {52750000, -45987651}, + {-52750000, -45987651}, + {-52750000, 41512348}, + }, + { + {-3900000, 14000000}, + {-2167950, 14000000}, + {1721454, 7263400}, + {3828529, 3613790}, + {3838809, 3582149}, + {3871560, 3270569}, + {3900000, 3000000}, + {3500000, -3000000}, + {3471560, -3270565}, + {3447549, -3498986}, + {3292510, -3976167}, + {3099999, -4512949}, + {2530129, -5500000}, + {807565, -8483570}, + {-2377349, -14000000}, + {-3900000, -14000000}, + {-3900000, 14000000}, + }, + { + {-31750000, -1000000}, + {-25250000, 40500000}, + {-18250000, 47500000}, + {10750000, 47500000}, + {16750000, 41500000}, + {31750000, -37000000}, + {31750000, -43857898}, + {28107900, -47500000}, + {18392099, -47500000}, + {-20750000, -46500000}, + {-31750000, -4000000}, + {-31750000, -1000000}, + }, + { + {-34625000, -14265399}, + {-10924999, 24875000}, + {33325000, 24875000}, + {37575000, 20625000}, + {37575000, 17625000}, + {26575000, -24875000}, + {-8924999, -24875000}, + {-34625000, -24484600}, + {-37575000, -19375000}, + {-34625000, -14265399}, + }, + { + {-14000000, 9000000}, + {-11000000, 17000000}, + {14000000, 17000000}, + {14000000, -17000000}, + {-11000000, -17000000}, + {-14000000, -8000000}, + {-14000000, 9000000}, + }, + { + {-5300000, 2227401}, + {-237800, 5150001}, + {5299999, 5150001}, + {5299999, 650001}, + {4699999, -5149997}, + {-5300000, -5149997}, + {-5300000, 2227401}, + }, + { + {-12000000, 18000000}, + {12000000, 18000000}, + {12000000, -18000000}, + {-12000000, -18000000}, + {-12000000, 18000000}, + }, + { + {-18000000, -1000000}, + {-15000000, 22000000}, + {-11000000, 26000000}, + {11000000, 26000000}, + {15000000, 22000000}, + {18000000, -1000000}, + {18000000, -26000000}, + {-18000000, -26000000}, + {-18000000, -1000000}, + }, + { + {-77500000, 30000000}, + {-72500000, 35000000}, + {72500000, 35000000}, + {77500000, 30000000}, + {77500000, -32928901}, + {75428901, -35000000}, + {-75428901, -35000000}, + {-77500000, -32928901}, + {-77500000, 30000000}, + }, + { + {-9945219, -3065619}, + {-9781479, -2031780}, + {-9510560, -1020730}, + {-9135450, -43529}, + {-2099999, 14110899}, + {2099999, 14110899}, + {9135450, -43529}, + {9510560, -1020730}, + {9781479, -2031780}, + {9945219, -3065619}, + {10000000, -4110899}, + {9945219, -5156179}, + {9781479, -6190019}, + {9510560, -7201069}, + {9135450, -8178270}, + {8660249, -9110899}, + {8090169, -9988750}, + {7431449, -10802209}, + {6691309, -11542349}, + {5877850, -12201069}, + {5000000, -12771149}, + {4067369, -13246350}, + {3090169, -13621459}, + {2079119, -13892379}, + {1045279, -14056119}, + {0, -14110899}, + {-1045279, -14056119}, + {-2079119, -13892379}, + {-3090169, -13621459}, + {-4067369, -13246350}, + {-5000000, -12771149}, + {-5877850, -12201069}, + {-6691309, -11542349}, + {-7431449, -10802209}, + {-8090169, -9988750}, + {-8660249, -9110899}, + {-9135450, -8178270}, + {-9510560, -7201069}, + {-9781479, -6190019}, + {-9945219, -5156179}, + {-10000000, -4110899}, + {-9945219, -3065619}, + }, + { + {-34192394, -5192389}, + {-31499996, 39000000}, + {-8183795, 47668998}, + {-6769596, 47668998}, + {-4648197, 45547698}, + {34192394, 6707109}, + {34192394, 5192389}, + {31500003, -39000000}, + {8183803, -47668998}, + {6769603, -47668998}, + {4648202, -45547698}, + {-32474895, -8424619}, + {-34192394, -6707109}, + {-34192394, -5192389}, + }, + { + {-23475500, -11910099}, + {-18000000, 8217699}, + {-11139699, 20100000}, + {-10271400, 20899999}, + {9532010, 20899999}, + {11199999, 20100000}, + {18500000, 8600000}, + {23475500, -11910099}, + {23799999, -14899999}, + {23706600, -15788900}, + {23668899, -16147499}, + {23281299, -17340400}, + {22654100, -18426700}, + {21814800, -19358900}, + {20799999, -20096199}, + {19654100, -20606300}, + {18427200, -20867099}, + {17799999, -20899999}, + {-17799999, -20899999}, + {-18427200, -20867099}, + {-19654100, -20606300}, + {-20799999, -20096199}, + {-21814800, -19358900}, + {-22654100, -18426700}, + {-23281299, -17340400}, + {-23668899, -16147499}, + {-23799999, -14899999}, + {-23475500, -11910099}, + }, + { + {-32000000, 10000000}, + {-31934440, 10623733}, + {-31740640, 11220210}, + {-31427049, 11763360}, + {-31007389, 12229430}, + {-30500000, 12598079}, + {-29927051, 12853170}, + {-29313585, 12983570}, + {16000000, 16000000}, + {26000000, 16000000}, + {31007400, 12229430}, + {31427101, 11763360}, + {31740600, 11220210}, + {31934398, 10623733}, + {32000000, 10000000}, + {32000000, -13000000}, + {31934398, -13623699}, + {31740600, -14220199}, + {31427101, -14763399}, + {31007400, -15229400}, + {30500000, -15598100}, + {29927101, -15853200}, + {29313598, -15983600}, + {29000000, -16000000}, + {-28000000, -16000000}, + {-29313585, -15983600}, + {-29927051, -15853200}, + {-30500000, -15598100}, + {-31007389, -15229400}, + {-31427049, -14763399}, + {-31740640, -14220199}, + {-31934440, -13623699}, + {-32000000, -13000000}, + {-32000000, 10000000}, + }, + { + {-36133789, -46431022}, + {-36040100, -46171817}, + {-35852722, -45653411}, + {2200073, 59616485}, + {12112792, 87039184}, + {14274505, 93019332}, + {14382049, 93291641}, + {14508483, 93563430}, + {14573425, 93688369}, + {14654052, 93832443}, + {14818634, 94096328}, + {14982757, 94327621}, + {15001708, 94352630}, + {15202392, 94598999}, + {15419342, 94833160}, + {15497497, 94910552}, + {15650848, 95053039}, + {15894866, 95256866}, + {16104309, 95412185}, + {16149047, 95443206}, + {16410888, 95611038}, + {16677795, 95759750}, + {16782348, 95812332}, + {16947143, 95889144}, + {17216400, 95999465}, + {17483123, 96091293}, + {17505554, 96098251}, + {17745178, 96165542}, + {18000671, 96223373}, + {18245880, 96265884}, + {18484039, 96295257}, + {18976715, 96319580}, + {31135131, 96319580}, + {31697082, 96287902}, + {31746368, 96282104}, + {32263000, 96190719}, + {32338623, 96172576}, + {32821411, 96026641}, + {32906188, 95995391}, + {33360565, 95797012}, + {33443420, 95754882}, + {33869171, 95505874}, + {33900756, 95485122}, + {34136413, 95318618}, + {34337127, 95159790}, + {34377288, 95125930}, + {34619628, 94905410}, + {34756286, 94767364}, + {34859008, 94656143}, + {35090606, 94378067}, + {35120849, 94338546}, + {35309295, 94072113}, + {35434875, 93871475}, + {35510070, 93740310}, + {35688232, 93385772}, + {35699096, 93361679}, + {35839782, 93012557}, + {35905487, 92817459}, + {35961578, 92625488}, + {36048004, 92249023}, + {36051574, 92229934}, + {36108856, 91831405}, + {36122985, 91667816}, + {36133789, 91435317}, + {36129669, 91085830}, + {36127685, 91046661}, + {36092742, 90669830}, + {36069946, 90514739}, + {36031829, 90308425}, + {35948211, 89965225}, + {34482635, 84756820}, + {27911407, 61403976}, + {-5872558, -58657440}, + {-14243621, -88406509}, + {-14576812, -89590599}, + {-15421997, -92594200}, + {-15657684, -93431732}, + {-16038940, -93720520}, + {-16420196, -94009307}, + {-17182708, -94586875}, + {-18834838, -95838272}, + {-19470275, -96319580}, + {-21368133, -96319580}, + {-22763854, -96319534}, + {-29742462, -96319274}, + {-32533935, -96319168}, + {-36133789, -54619018}, + {-36133789, -46431022}, + }, + { + {-26000000, 25500000}, + {-6500000, 45000000}, + {17499998, 45000000}, + {23966310, 38533699}, + {26000000, 36500000}, + {26000000, -19000000}, + {25950000, -24500000}, + {17000000, -42214698}, + {14300000, -45000000}, + {-14299999, -45000000}, + {-17500000, -41714698}, + {-23400001, -24500000}, + {-26000000, -10464000}, + {-26000000, 25500000}, + }, + { + {-26000000, 16636100}, + {-25072200, 18777799}, + {-16500000, 35299999}, + {-15050000, 36750000}, + {13550000, 36750000}, + {15000000, 35299999}, + {26000000, 16045200}, + {26000000, -2750000}, + {16500000, -34507900}, + {14840600, -36167301}, + {14257900, -36750000}, + {-14257900, -36750000}, + {-16500000, -34507900}, + {-26000000, -2750000}, + {-26000000, 16636100}, + }, + { + {-18062349, 18950099}, + {4644938, 20049900}, + {6230361, 20049900}, + {7803279, 19851200}, + {9338899, 19456899}, + {10812990, 18873300}, + {12202310, 18109500}, + {13484951, 17177600}, + {14640670, 16092300}, + {15651250, 14870700}, + {16500749, 13532100}, + {17175849, 12097599}, + {17665750, 10589700}, + {17962850, 9032400}, + {18062349, 7450099}, + {17962850, 5867799}, + {15810750, -11007740}, + {15683750, -11727769}, + {15506849, -12437200}, + {15280929, -13132559}, + {15007040, -13810470}, + {14686531, -14467609}, + {14320949, -15100799}, + {13912099, -15706950}, + {13461959, -16283100}, + {12972730, -16826450}, + {12446790, -17334339}, + {11886699, -17804309}, + {11295190, -18234069}, + {10675149, -18621520}, + {10029590, -18964771}, + {9361650, -19262149}, + {8674600, -19512220}, + {7971780, -19713699}, + {7256609, -19865798}, + {6532589, -19967498}, + {5803222, -20018501}, + {5437650, -20024900}, + {-1062349, -20049900}, + {-16562349, -20049900}, + {-18062349, -18549900}, + {-18062349, 18950099}, + }, + { + {-18062349, 41299900}, + {-1062349, 41299900}, + {15280929, -8117440}, + {15506849, -8812799}, + {15683750, -9522230}, + {15810750, -10242259}, + {17962850, -27117799}, + {18062349, -28700099}, + {17962850, -30282400}, + {17665750, -31839700}, + {17175849, -33347599}, + {16500749, -34782100}, + {15651250, -36120700}, + {14640670, -37342300}, + {13484951, -38427600}, + {12202310, -39359500}, + {10812990, -40123298}, + {9338899, -40706901}, + {7803279, -41101200}, + {6230361, -41299900}, + {4644938, -41299900}, + {-18062349, -40200099}, + {-18062349, 41299900}, + }, + { + {-11750000, 13057900}, + {-9807860, 15000000}, + {4392139, 24000000}, + {11750000, 24000000}, + {11750000, -24000000}, + {4392139, -24000000}, + {-9807860, -15000000}, + {-11750000, -13057900}, + {-11750000, 13057900}, + }, + { + {-12500000, 17500000}, + {12500000, 17500000}, + {12500000, -17500000}, + {-12500000, -17500000}, + {-12500000, 17500000}, + }, + { + {-23500000, 11500000}, + {-13857859, 21000000}, + {-11000000, 21000000}, + {18500000, 500000}, + {23500000, -4500000}, + {23500000, -19500000}, + {22000000, -21000000}, + {-23500000, -21000000}, + {-23500000, 11500000}, + }, + { + {-13000000, 5250000}, + {-4000000, 6750000}, + {4000000, 6750000}, + {13000000, 5250000}, + {13000000, 838459}, + {11376299, -1973939}, + {10350899, -3750000}, + {8618800, -6750000}, + {-8498290, -6750000}, + {-13000000, 1047180}, + {-13000000, 5250000}, + }, + { + {-25000000, 50500000}, + {-21500000, 54000000}, + {18286800, 54000000}, + {25000000, 47286800}, + {25000000, -47286800}, + {18286800, -54000000}, + {-21500000, -54000000}, + {-25000000, -50500000}, + {-25000000, 50500000}, + }, + { + {-19000000, 46000000}, + {-16799999, 46000000}, + {14000000, 34000000}, + {19000000, 29000000}, + {19000000, -29000000}, + {14000000, -34000000}, + {-16799999, -46000000}, + {-19000000, -46000000}, + {-19000000, 46000000}, + }, + { + {-7956170, 836226}, + {-7825180, 1663290}, + {-7767529, 1914530}, + {-7608449, 2472140}, + {-7308360, 3253890}, + {-7083650, 3717780}, + {-6928199, 4000000}, + {-6472139, 4702280}, + {-5988090, 5304979}, + {-5945159, 5353040}, + {-5353040, 5945159}, + {-4702280, 6472139}, + {-4544519, 6583869}, + {-4000000, 6928199}, + {-3253890, 7308360}, + {-2836839, 7480130}, + {-2472140, 7608449}, + {-1663290, 7825180}, + {-964293, 7941669}, + {-836226, 7956170}, + {0, 8000000}, + {836226, 7956170}, + {964293, 7941669}, + {1663290, 7825180}, + {2472140, 7608449}, + {2836839, 7480130}, + {3253890, 7308360}, + {4000000, 6928199}, + {4544519, 6583869}, + {4702280, 6472139}, + {5353040, 5945159}, + {5945159, 5353040}, + {5988090, 5304979}, + {6472139, 4702280}, + {6928199, 4000000}, + {7083650, 3717780}, + {7308360, 3253890}, + {7608449, 2472140}, + {7767529, 1914530}, + {7825180, 1663290}, + {7956170, 836226}, + {8000000, 0}, + {7956170, -836226}, + {7825180, -1663290}, + {7767529, -1914530}, + {7608449, -2472140}, + {7308360, -3253890}, + {7083650, -3717780}, + {6928199, -4000000}, + {6472139, -4702280}, + {5988090, -5304979}, + {5945159, -5353040}, + {5353040, -5945159}, + {4702280, -6472139}, + {4544519, -6583869}, + {4000000, -6928199}, + {3253890, -7308360}, + {2836839, -7480130}, + {2472140, -7608449}, + {1663290, -7825180}, + {964293, -7941669}, + {836226, -7956170}, + {0, -8000000}, + {-836226, -7956170}, + {-964293, -7941669}, + {-1663290, -7825180}, + {-2472140, -7608449}, + {-2836839, -7480130}, + {-3253890, -7308360}, + {-4000000, -6928199}, + {-4544519, -6583869}, + {-4702280, -6472139}, + {-5353040, -5945159}, + {-5945159, -5353040}, + {-5988090, -5304979}, + {-6472139, -4702280}, + {-6928199, -4000000}, + {-7083650, -3717780}, + {-7308360, -3253890}, + {-7608449, -2472140}, + {-7767529, -1914530}, + {-7825180, -1663290}, + {-7956170, -836226}, + {-8000000, 0}, + {-7956170, 836226}, + }, +}; + +const TestData PRUSA_STEGOSAUR_POLYGONS = +{ + { + {113210205, 107034095}, + {113561798, 109153793}, + {113750099, 109914001}, + {114396499, 111040199}, + {114599197, 111321998}, + {115570404, 112657096}, + {116920097, 114166595}, + {117630599, 114609390}, + {119703704, 115583900}, + {120559494, 115811996}, + {121045410, 115754493}, + {122698097, 115526496}, + {123373001, 115370193}, + {123482406, 115315689}, + {125664199, 114129798}, + {125920303, 113968193}, + {128551208, 111866195}, + {129075592, 111443199}, + {135044692, 106572608}, + {135254898, 106347694}, + {135415100, 106102897}, + {136121704, 103779891}, + {136325103, 103086303}, + {136690093, 101284896}, + {136798309, 97568496}, + {136798309, 97470397}, + {136787399, 97375297}, + {136753295, 97272102}, + {136687988, 97158699}, + {136539794, 96946899}, + {135526702, 95550994}, + {135388488, 95382293}, + {135272491, 95279098}, + {135214904, 95250595}, + {135122894, 95218002}, + {134966705, 95165191}, + {131753997, 94380798}, + {131226806, 94331001}, + {129603393, 94193893}, + {129224197, 94188003}, + {127874107, 94215103}, + {126812797, 94690200}, + {126558197, 94813896}, + {118361801, 99824195}, + {116550796, 101078796}, + {116189704, 101380493}, + {114634002, 103027999}, + {114118103, 103820297}, + {113399200, 105568000}, + {113201705, 106093597}, + {113210205, 107034095}, + }, + { + {77917999, 130563003}, + {77926300, 131300903}, + {77990196, 132392700}, + {78144195, 133328002}, + {78170593, 133427093}, + {78235900, 133657592}, + {78799598, 135466705}, + {78933296, 135832397}, + {79112899, 136247604}, + {79336303, 136670898}, + {79585197, 137080596}, + {79726303, 137309005}, + {79820297, 137431900}, + {79942199, 137549407}, + {90329193, 145990203}, + {90460197, 146094390}, + {90606399, 146184509}, + {90715194, 146230010}, + {90919601, 146267211}, + {142335296, 153077697}, + {143460296, 153153594}, + {143976593, 153182189}, + {145403991, 153148605}, + {145562301, 153131195}, + {145705993, 153102905}, + {145938796, 153053192}, + {146134094, 153010101}, + {146483184, 152920196}, + {146904693, 152806396}, + {147180099, 152670196}, + {147357788, 152581695}, + {147615295, 152423095}, + {147782287, 152294708}, + {149281799, 150908386}, + {149405303, 150784912}, + {166569305, 126952499}, + {166784301, 126638099}, + {166938491, 126393699}, + {167030899, 126245101}, + {167173004, 126015899}, + {167415298, 125607200}, + {167468292, 125504699}, + {167553100, 125320899}, + {167584594, 125250694}, + {167684997, 125004394}, + {167807098, 124672401}, + {167938995, 124255203}, + {168052307, 123694000}, + {170094100, 112846900}, + {170118408, 112684204}, + {172079101, 88437797}, + {172082000, 88294403}, + {171916290, 82827606}, + {171911590, 82705703}, + {171874893, 82641906}, + {169867004, 79529907}, + {155996795, 58147998}, + {155904998, 58066299}, + {155864791, 58054199}, + {134315704, 56830902}, + {134086486, 56817901}, + {98200096, 56817798}, + {97838195, 56818599}, + {79401695, 56865097}, + {79291297, 56865501}, + {79180694, 56869499}, + {79058799, 56885097}, + {78937301, 56965301}, + {78324691, 57374599}, + {77932998, 57638401}, + {77917999, 57764297}, + {77917999, 130563003}, + }, + { + {75566848, 109289947}, + {75592651, 109421951}, + {75644248, 109534446}, + {95210548, 141223846}, + {95262649, 141307449}, + {95487854, 141401443}, + {95910850, 141511642}, + {96105651, 141550338}, + {106015045, 142803451}, + {106142852, 142815155}, + {166897460, 139500244}, + {167019348, 139484741}, + {168008239, 138823043}, + {168137542, 138735153}, + {168156250, 138616851}, + {173160751, 98882049}, + {174381546, 87916046}, + {174412246, 87579048}, + {174429443, 86988746}, + {174436141, 86297348}, + {174438949, 84912048}, + {174262939, 80999145}, + {174172546, 80477546}, + {173847549, 79140846}, + {173623840, 78294349}, + {173120239, 76485046}, + {173067138, 76300544}, + {173017852, 76137542}, + {172941543, 75903045}, + {172892547, 75753143}, + {172813537, 75533348}, + {172758453, 75387046}, + {172307556, 74196746}, + {171926544, 73192848}, + {171891448, 73100448}, + {171672546, 72524147}, + {171502441, 72085144}, + {171414459, 71859146}, + {171294250, 71552352}, + {171080139, 71019744}, + {171039245, 70928146}, + {170970550, 70813346}, + {170904235, 70704040}, + {170786254, 70524353}, + {168063247, 67259048}, + {167989547, 67184844}, + {83427947, 67184844}, + {78360847, 67201248}, + {78238845, 67220550}, + {78151550, 67350547}, + {77574554, 68220550}, + {77494949, 68342651}, + {77479949, 68464546}, + {75648345, 106513351}, + {75561050, 109165740}, + {75566848, 109289947}, + }, + { + {75619415, 108041595}, + {83609863, 134885772}, + {83806945, 135450820}, + {83943908, 135727371}, + {84799934, 137289794}, + {86547897, 140033782}, + {86674118, 140192962}, + {86810661, 140364715}, + {87045211, 140619918}, + {88187042, 141853240}, + {93924575, 147393783}, + {94058013, 147454803}, + {111640083, 153754562}, + {111762550, 153787933}, + {111975250, 153835311}, + {112127426, 153842803}, + {116797996, 154005157}, + {116969688, 154010681}, + {117141731, 154005935}, + {117333145, 153988037}, + {118007507, 153919952}, + {118159675, 153902130}, + {118931480, 153771942}, + {120878150, 153379089}, + {121172164, 153319259}, + {122074508, 153034362}, + {122260681, 152970367}, + {122313438, 152949584}, + {130755096, 149423736}, + {130996063, 149316818}, + {138893524, 144469665}, + {138896423, 144466918}, + {169883666, 97686134}, + {170115036, 96518981}, + {170144317, 96365257}, + {174395645, 67672065}, + {174396560, 67664222}, + {174288452, 66839241}, + {174170364, 66096923}, + {174112731, 65952033}, + {174021377, 65823486}, + {173948608, 65743225}, + {173863830, 65654769}, + {170408340, 63627494}, + {170004867, 63394714}, + {169585632, 63194389}, + {169441162, 63137046}, + {168944274, 62952133}, + {160605072, 60214218}, + {160331573, 60126396}, + {159674743, 59916877}, + {150337249, 56943778}, + {150267730, 56922073}, + {150080139, 56864868}, + {149435333, 56676422}, + {149310241, 56640579}, + {148055419, 56285041}, + {147828796, 56230949}, + {147598205, 56181800}, + {147149963, 56093917}, + {146834457, 56044700}, + {146727966, 56028717}, + {146519729, 56004882}, + {146328521, 55989326}, + {146170684, 55990036}, + {146151321, 55990745}, + {145800170, 56003616}, + {145639526, 56017753}, + {145599426, 56022491}, + {145481338, 56039184}, + {145389556, 56052757}, + {145325134, 56062591}, + {145176574, 56086135}, + {145017272, 56113922}, + {107163085, 63504539}, + {101013870, 65454101}, + {100921798, 65535285}, + {95362182, 74174079}, + {75652366, 107803443}, + {75635391, 107834983}, + {75628814, 107853294}, + {75603431, 107933692}, + {75619415, 108041595}, + }, + { + {83617141, 120264900}, + {84617370, 126416427}, + {84648635, 126601341}, + {84693695, 126816085}, + {84762496, 127082641}, + {84772140, 127117034}, + {84860748, 127391693}, + {84927398, 127550239}, + {85072967, 127789642}, + {85155151, 127908851}, + {86745422, 130042907}, + {86982666, 130317489}, + {89975143, 133230743}, + {90091384, 133338500}, + {96260833, 138719818}, + {96713928, 139103668}, + {98139297, 140307388}, + {102104766, 143511505}, + {102142089, 143536468}, + {102457626, 143735107}, + {103386764, 144312988}, + {103845001, 144579177}, + {104139175, 144737136}, + {104551254, 144932250}, + {104690155, 144985778}, + {104844238, 145010009}, + {105020034, 145010375}, + {128999633, 144082305}, + {129096542, 144076141}, + {133932327, 143370178}, + {134130615, 143326751}, + {134281250, 143289520}, + {135247116, 142993438}, + {150774948, 137828704}, + {150893478, 137786178}, + {151350921, 137608901}, + {159797760, 134318115}, + {159979827, 134244384}, + {159988128, 134240997}, + {160035186, 134221633}, + {160054962, 134211486}, + {160168762, 134132736}, + {160181228, 134121047}, + {160336425, 133961502}, + {160689147, 133564331}, + {161446258, 132710739}, + {163306427, 130611648}, + {164845474, 128873855}, + {165270233, 128393600}, + {165281478, 128380706}, + {165300598, 128358673}, + {165303497, 128355194}, + {166411590, 122772674}, + {166423767, 122708648}, + {164745605, 66237312}, + {164740341, 66193061}, + {164721755, 66082092}, + {164721160, 66078750}, + {164688476, 65914146}, + {164668426, 65859436}, + {164563110, 65765937}, + {164431152, 65715034}, + {163997619, 65550788}, + {163946426, 65531440}, + {162998107, 65173629}, + {162664978, 65049140}, + {162482696, 64991668}, + {162464660, 64989639}, + {148029083, 66896141}, + {147862396, 66932853}, + {130087829, 73341102}, + {129791564, 73469726}, + {100590927, 90307685}, + {100483535, 90373847}, + {100364990, 90458930}, + {96447448, 93276664}, + {95179656, 94189010}, + {93692718, 95260208}, + {87904327, 99430885}, + {87663711, 99606147}, + {87576202, 99683990}, + {87498199, 99801719}, + {85740264, 104173728}, + {85538925, 104710494}, + {84786132, 107265830}, + {84635955, 107801383}, + {84619506, 107868064}, + {84518463, 108287200}, + {84456848, 108613471}, + {84419158, 108826194}, + {84375244, 109093818}, + {84329818, 109435180}, + {84249862, 110179664}, + {84218429, 110572166}, + {83630020, 117995208}, + {83595535, 118787673}, + {83576217, 119290679}, + {83617141, 120264900}, + }, + { + {91735549, 117640846}, + {91748252, 117958145}, + {91823547, 118515449}, + {92088752, 119477249}, + {97995346, 140538452}, + {98031051, 140660446}, + {98154449, 141060241}, + {98179855, 141133758}, + {98217056, 141232849}, + {98217147, 141233047}, + {98269256, 141337051}, + {98298950, 141387954}, + {98337753, 141445755}, + {99455047, 142984451}, + {99656250, 143247344}, + {102567855, 146783752}, + {102685150, 146906845}, + {102828948, 147031250}, + {102972457, 147120452}, + {103676147, 147539642}, + {103758956, 147586151}, + {103956756, 147682144}, + {104479949, 147931457}, + {104744453, 148044143}, + {104994750, 148123443}, + {105375648, 148158645}, + {109266250, 148178253}, + {109447753, 148169052}, + {109693649, 148129150}, + {113729949, 147337448}, + {113884552, 147303054}, + {115155349, 146956146}, + {117637145, 146174346}, + {154694046, 134048049}, + {156979949, 133128555}, + {157076843, 133059356}, + {157125045, 133001449}, + {157561340, 132300750}, + {157865753, 131795959}, + {157923156, 131667358}, + {158007049, 131297653}, + {158112747, 130777053}, + {158116653, 130640853}, + {158268951, 119981643}, + {158260040, 119824752}, + {158229949, 119563751}, + {149914047, 73458648}, + {149877548, 73331748}, + {144460754, 66413558}, + {144230545, 66153152}, + {144128051, 66075057}, + {143974853, 65973152}, + {142812744, 65353149}, + {141810943, 64837249}, + {141683349, 64805152}, + {141505157, 64784652}, + {108214355, 61896251}, + {107826354, 61866352}, + {107072151, 61821750}, + {106938850, 61873550}, + {106584251, 62055152}, + {106419952, 62147548}, + {100459152, 65546951}, + {100343849, 65615150}, + {100198852, 65716949}, + {99825149, 65979751}, + {94619247, 70330352}, + {94492355, 70480850}, + {94445846, 70547355}, + {94425354, 70588752}, + {94379753, 70687652}, + {94110252, 71443450}, + {94095252, 71569053}, + {91737251, 117308746}, + {91731048, 117430946}, + {91735549, 117640846}, + }, + { + {108231399, 111763748}, + {108335403, 111927955}, + {108865203, 112754745}, + {109206703, 113283851}, + {127117500, 125545951}, + {127212097, 125560951}, + {127358497, 125563652}, + {131348007, 125551147}, + {131412002, 125550849}, + {131509506, 125535446}, + {131579391, 125431343}, + {132041000, 124735656}, + {132104690, 124637847}, + {144108505, 100950546}, + {144120605, 100853042}, + {144123291, 100764648}, + {144122695, 100475143}, + {144086898, 85637748}, + {144083602, 85549346}, + {144071105, 85451843}, + {144007003, 85354545}, + {143679595, 84864547}, + {143468597, 84551048}, + {143367889, 84539146}, + {109847702, 84436347}, + {109684700, 84458953}, + {105946502, 89406143}, + {105915901, 91160446}, + {105880905, 93187744}, + {105876701, 93441345}, + {108231399, 111763748}, + }, + { + {102614700, 117684249}, + {102675102, 118074157}, + {102888999, 118743148}, + {103199707, 119517555}, + {103446800, 120099655}, + {103488204, 120193450}, + {104063903, 121373947}, + {104535499, 122192245}, + {104595802, 122295249}, + {104663002, 122402854}, + {104945701, 122854858}, + {105740501, 124038848}, + {106809700, 125479354}, + {107564399, 126380050}, + {108116203, 126975646}, + {123724700, 142516540}, + {124938400, 143705444}, + {127919601, 146599243}, + {128150894, 146821456}, + {128251602, 146917251}, + {128383605, 147041839}, + {128527709, 147176147}, + {128685699, 147321456}, + {128861007, 147481246}, + {132825103, 151046661}, + {133005493, 151205657}, + {133389007, 151488143}, + {133896499, 151858062}, + {134172302, 151991546}, + {134375000, 152063140}, + {135316101, 152300949}, + {136056304, 152220947}, + {136242706, 152186843}, + {136622207, 152016448}, + {136805404, 151908355}, + {147099594, 145766845}, + {147246704, 144900756}, + {147387603, 144048461}, + {144353698, 99345855}, + {144333801, 99232254}, + {144244598, 98812850}, + {144228698, 98757858}, + {144174606, 98616455}, + {133010101, 72396743}, + {132018905, 70280853}, + {130667404, 67536949}, + {129167297, 64854446}, + {128569198, 64098350}, + {124458503, 59135948}, + {124260597, 58946949}, + {123908706, 58658851}, + {123460098, 58327850}, + {122674499, 57840648}, + {122041801, 57712150}, + {121613403, 57699047}, + {121359901, 57749351}, + {121123199, 57826450}, + {120953498, 57882247}, + {120431701, 58198547}, + {120099205, 58599349}, + {119892303, 58903049}, + {102835296, 115179351}, + {102686599, 115817245}, + {102612396, 116540557}, + {102614700, 117684249}, + }, + { + {98163757, 71203430}, + {98212463, 73314544}, + {98326538, 74432693}, + {98402908, 75169799}, + {98524154, 76328353}, + {99088806, 79911361}, + {99304885, 80947769}, + {100106689, 84244186}, + {100358123, 85080337}, + {101715545, 89252807}, + {101969528, 89987213}, + {107989440, 106391418}, + {126299575, 140277343}, + {127061813, 141486663}, + {127405746, 141872253}, + {127846908, 142318450}, + {130818496, 145301574}, + {134366424, 148100921}, + {135308380, 148798828}, + {135745666, 149117523}, + {136033020, 149251800}, + {136500579, 149387725}, + {136662719, 149418395}, + {136973922, 149474822}, + {137184890, 149484375}, + {137623748, 149434356}, + {137830810, 149355072}, + {138681732, 148971343}, + {139374465, 148463409}, + {139589187, 148264312}, + {139809707, 148010711}, + {139985610, 147685028}, + {140196029, 147284973}, + {140355834, 146978668}, + {142079666, 142575622}, + {146702194, 129469726}, + {151285888, 113275238}, + {151543731, 112046264}, + {151701629, 110884704}, + {151837020, 108986206}, + {151837097, 107724029}, + {151760101, 106529205}, + {151581970, 105441925}, + {151577301, 105413757}, + {151495269, 105014709}, + {151393142, 104551513}, + {151058502, 103296112}, + {150705520, 102477264}, + {150137725, 101686370}, + {149427032, 100938537}, + {102979965, 60772064}, + {101930953, 60515609}, + {101276748, 60634414}, + {100717803, 60918136}, + {100125732, 61584625}, + {99618148, 62413436}, + {99457214, 62709442}, + {99368347, 62914794}, + {99166992, 63728332}, + {98313827, 69634780}, + {98176910, 70615707}, + {98162902, 70798233}, + {98163757, 71203430}, + }, + { + {79090698, 116426399}, + {80959800, 137087692}, + {81030303, 137762298}, + {81190704, 138903503}, + {81253700, 139084197}, + {81479301, 139544998}, + {81952003, 140118896}, + {82319900, 140523895}, + {82967803, 140993896}, + {83022903, 141032104}, + {83777900, 141493606}, + {84722099, 141849899}, + {84944396, 141887207}, + {86144699, 141915893}, + {87643997, 141938095}, + {88277503, 141887695}, + {88582099, 141840606}, + {89395401, 141712203}, + {90531204, 141528396}, + {91014801, 141438400}, + {92097595, 141190093}, + {123348297, 132876998}, + {123399505, 132860000}, + {123452804, 132841506}, + {123515502, 132818908}, + {123543800, 132806198}, + {124299598, 132437393}, + {124975502, 132042098}, + {125047500, 131992202}, + {125119506, 131930603}, + {166848800, 86317703}, + {168976409, 83524902}, + {169359603, 82932701}, + {169852600, 81917800}, + {170686904, 79771202}, + {170829406, 79245597}, + {170885498, 78796295}, + {170909301, 78531898}, + {170899703, 78238700}, + {170842803, 77553199}, + {170701293, 76723495}, + {170302307, 75753898}, + {169924301, 75067398}, + {169359802, 74578796}, + {168148605, 73757499}, + {163261596, 71124702}, + {162986007, 70977798}, + {162248703, 70599098}, + {158193405, 68923995}, + {157514297, 68667495}, + {156892700, 68495201}, + {156607299, 68432998}, + {154301895, 68061904}, + {93440299, 68061904}, + {88732002, 68255996}, + {88627304, 68298500}, + {88111396, 68541900}, + {86393898, 69555404}, + {86138298, 69706695}, + {85871704, 69913200}, + {85387199, 70393402}, + {79854499, 76783203}, + {79209701, 77649398}, + {79108505, 78072502}, + {79090698, 78472198}, + {79090698, 116426399}, + }, + { + {90956314, 84639938}, + {91073814, 85141891}, + {91185752, 85505371}, + {109815368, 137196487}, + {110342590, 138349899}, + {110388549, 138447540}, + {110652862, 138971343}, + {110918045, 139341140}, + {114380859, 143159042}, + {114446723, 143220352}, + {114652198, 143392166}, + {114712196, 143437301}, + {114782165, 143476028}, + {114873054, 143514923}, + {115217086, 143660934}, + {115306060, 143695526}, + {115344009, 143707580}, + {115444541, 143737747}, + {115589378, 143779937}, + {115751358, 143823989}, + {115802780, 143825820}, + {116872810, 143753616}, + {116927055, 143744644}, + {154690734, 133504180}, + {155009704, 133371856}, + {155029907, 133360061}, + {155089141, 133323181}, + {155342315, 133163360}, + {155602294, 132941406}, + {155669158, 132880294}, + {155821624, 132737884}, + {155898986, 132656890}, + {155934936, 132608932}, + {155968627, 132562713}, + {156062896, 132431808}, + {156111694, 132363174}, + {156148147, 132297180}, + {158738342, 127281066}, + {159026672, 126378631}, + {159073699, 125806335}, + {159048522, 125299743}, + {159040313, 125192901}, + {158898300, 123934677}, + {149829376, 70241508}, + {149763031, 69910629}, + {149684692, 69628723}, + {149557800, 69206214}, + {149366485, 68864326}, + {149137390, 68578514}, + {148637466, 68048767}, + {147027725, 66632934}, + {146228607, 66257507}, + {146061309, 66184646}, + {146017929, 66174186}, + {145236465, 66269500}, + {144802490, 66345039}, + {144673995, 66376220}, + {93732284, 79649864}, + {93345336, 79785865}, + {93208084, 79840286}, + {92814521, 79997779}, + {92591087, 80098968}, + {92567016, 80110511}, + {92032684, 80860725}, + {91988853, 80930152}, + {91471725, 82210029}, + {91142349, 83076683}, + {90969284, 83653182}, + {90929664, 84043212}, + {90926315, 84325256}, + {90956314, 84639938}, + }, + { + {114758499, 88719909}, + {114771591, 88860549}, + {115515533, 94195907}, + {115559539, 94383651}, + {119882980, 109502059}, + {120660522, 111909683}, + {126147735, 124949630}, + {127127212, 127107215}, + {129976379, 132117279}, + {130754470, 133257080}, + {130820968, 133340835}, + {130889312, 133423858}, + {131094787, 133652832}, + {131257629, 133828247}, + {131678619, 134164276}, + {131791107, 134248901}, + {131969482, 134335189}, + {132054107, 134373718}, + {132927368, 134701141}, + {133077072, 134749313}, + {133196075, 134785705}, + {133345230, 134804351}, + {133498809, 134809051}, + {133611541, 134797607}, + {134621170, 134565322}, + {134741165, 134527511}, + {134892089, 134465240}, + {135071212, 134353820}, + {135252029, 134185821}, + {135384979, 134003631}, + {135615585, 133576675}, + {135793029, 132859008}, + {135890228, 131382904}, + {135880828, 131261657}, + {135837570, 130787963}, + {135380661, 127428909}, + {132830596, 109495368}, + {132815826, 109411666}, + {132765869, 109199302}, + {132724380, 109068161}, + {127490066, 93353515}, + {125330810, 87852828}, + {125248336, 87647026}, + {125002182, 87088424}, + {124894592, 86872482}, + {121007278, 80019584}, + {120962829, 79941261}, + {120886489, 79833923}, + {120154983, 78949615}, + {119366561, 78111709}, + {119014755, 77776794}, + {116728790, 75636238}, + {116660522, 75593933}, + {116428192, 75458541}, + {116355255, 75416870}, + {116264663, 75372528}, + {115952728, 75233367}, + {115865554, 75205482}, + {115756835, 75190956}, + {115564163, 75197830}, + {115481170, 75202087}, + {115417144, 75230400}, + {115226959, 75337806}, + {115203842, 75351448}, + {114722015, 75746932}, + {114672103, 75795661}, + {114594619, 75891891}, + {114565811, 75973831}, + {114478256, 76240814}, + {114178039, 77252197}, + {114137664, 77769668}, + {114109771, 78154464}, + {114758499, 88719909}, + }, + { + {108135070, 109828002}, + {108200347, 110091529}, + {108319419, 110298500}, + {108439025, 110488388}, + {108663574, 110766731}, + {108812957, 110935768}, + {109321914, 111398925}, + {109368087, 111430320}, + {109421295, 111466331}, + {110058998, 111849746}, + {127160308, 120588981}, + {127350692, 120683456}, + {128052749, 120997207}, + {128326919, 121113449}, + {131669586, 122213058}, + {131754745, 122240592}, + {131854583, 122264770}, + {132662048, 122449813}, + {132782669, 122449897}, + {132909118, 122443687}, + {133013442, 122436058}, + {140561035, 121609939}, + {140786346, 121583320}, + {140876144, 121570228}, + {140962356, 121547996}, + {141052612, 121517837}, + {141231292, 121442184}, + {141309371, 121390007}, + {141370132, 121327003}, + {141456008, 121219932}, + {141591598, 121045005}, + {141905761, 120634796}, + {141894607, 120305725}, + {141881881, 120110855}, + {141840881, 119885009}, + {141685043, 119238922}, + {141617416, 118962882}, + {141570434, 118858856}, + {131617462, 100598548}, + {131542846, 100487213}, + {131229385, 100089019}, + {131091476, 99928108}, + {119824127, 90297180}, + {119636337, 90142387}, + {119507492, 90037765}, + {119436744, 89983657}, + {119423942, 89974159}, + {119207366, 89822471}, + {119117149, 89767097}, + {119039489, 89726867}, + {116322929, 88522857}, + {114817031, 87882110}, + {114683975, 87826751}, + {114306411, 87728507}, + {113876434, 87646003}, + {113792106, 87629974}, + {113658988, 87615974}, + {113574333, 87609275}, + {112813575, 87550102}, + {112578567, 87560157}, + {112439880, 87571647}, + {112306922, 87599395}, + {112225082, 87622535}, + {112132568, 87667175}, + {112103477, 87682830}, + {110795242, 88511634}, + {110373565, 88847793}, + {110286537, 88934989}, + {109730873, 89531501}, + {109648735, 89628883}, + {109552581, 89768859}, + {109514228, 89838470}, + {109501640, 89877586}, + {109480964, 89941864}, + {109461761, 90032417}, + {109457778, 90055458}, + {108105194, 109452575}, + {108094238, 109620979}, + {108135070, 109828002}, + }, + { + {108764694, 108910400}, + {108965499, 112306495}, + {109598602, 120388298}, + {110573898, 128289596}, + {110597801, 128427795}, + {113786201, 137983795}, + {113840301, 138134704}, + {113937202, 138326904}, + {114046005, 138520401}, + {114150802, 138696792}, + {114164703, 138717895}, + {114381896, 139021194}, + {114701004, 139425292}, + {114997398, 139747497}, + {115065597, 139805191}, + {115134498, 139850891}, + {115167098, 139871704}, + {115473396, 139992797}, + {115537498, 139995101}, + {116762596, 139832000}, + {116897499, 139808593}, + {118401802, 139225585}, + {118437500, 139209594}, + {118488204, 139182189}, + {118740097, 139033996}, + {118815795, 138967285}, + {134401000, 116395492}, + {134451507, 116309997}, + {135488098, 113593597}, + {137738006, 106775695}, + {140936492, 97033889}, + {140960006, 96948997}, + {141026504, 96660995}, + {141067291, 96467094}, + {141124893, 95771896}, + {141511795, 90171600}, + {141499801, 90026000}, + {141479598, 89907798}, + {141276794, 88844596}, + {141243804, 88707397}, + {140778305, 87031593}, + {140733306, 86871696}, + {140697204, 86789993}, + {140619796, 86708190}, + {140398391, 86487396}, + {125798797, 72806198}, + {125415802, 72454498}, + {123150398, 70566093}, + {123038803, 70503997}, + {122681198, 70305397}, + {121919204, 70104797}, + {121533699, 70008094}, + {121273696, 70004898}, + {121130599, 70020797}, + {121045097, 70033294}, + {120847099, 70082298}, + {120481895, 70278999}, + {120367004, 70379692}, + {120272796, 70475097}, + {119862098, 71004791}, + {119745101, 71167297}, + {119447799, 71726997}, + {119396499, 71825798}, + {119348701, 71944496}, + {109508796, 98298797}, + {109368598, 98700897}, + {109298400, 98926391}, + {108506301, 102750991}, + {108488197, 102879898}, + {108764694, 108910400}, + }, + { + {106666252, 87231246}, + {106673248, 87358055}, + {107734146, 101975646}, + {107762649, 102357955}, + {108702445, 111208351}, + {108749450, 111345153}, + {108848350, 111542648}, + {110270645, 114264358}, + {110389648, 114445144}, + {138794845, 143461151}, + {139048355, 143648956}, + {139376144, 143885345}, + {139594451, 144022644}, + {139754043, 144110046}, + {139923950, 144185852}, + {140058242, 144234451}, + {140185653, 144259552}, + {140427551, 144292648}, + {141130950, 144281448}, + {141157653, 144278152}, + {141214355, 144266555}, + {141347457, 144223449}, + {141625350, 144098953}, + {141755142, 144040145}, + {141878143, 143971557}, + {142011444, 143858154}, + {142076843, 143796356}, + {142160644, 143691055}, + {142224456, 143560852}, + {142925842, 142090850}, + {142935653, 142065353}, + {142995956, 141899154}, + {143042556, 141719757}, + {143102951, 141436157}, + {143129257, 141230453}, + {143316055, 139447250}, + {143342544, 133704650}, + {143307556, 130890960}, + {142461257, 124025558}, + {141916046, 120671051}, + {141890457, 120526153}, + {140002349, 113455749}, + {139909149, 113144149}, + {139853454, 112974456}, + {137303756, 105228057}, + {134700546, 98161254}, + {134617950, 97961547}, + {133823547, 96118057}, + {133688751, 95837356}, + {133481353, 95448059}, + {133205444, 94948150}, + {131178955, 91529853}, + {131144744, 91482055}, + {113942047, 67481246}, + {113837051, 67360549}, + {113048950, 66601745}, + {112305549, 66002746}, + {112030853, 65790351}, + {111970649, 65767547}, + {111912445, 65755249}, + {111854248, 65743453}, + {111657447, 65716354}, + {111576950, 65707351}, + {111509750, 65708549}, + {111443550, 65718551}, + {111397247, 65737449}, + {111338546, 65764648}, + {111129547, 65863349}, + {111112449, 65871551}, + {110995254, 65927856}, + {110968849, 65946151}, + {110941444, 65966751}, + {110836448, 66057853}, + {110490447, 66445449}, + {110404144, 66576751}, + {106802055, 73202148}, + {106741950, 73384948}, + {106715454, 73469650}, + {106678054, 73627151}, + {106657455, 75433448}, + {106666252, 87231246}, + }, + { + {101852752, 106261352}, + {101868949, 106406051}, + {102347549, 108974250}, + {112286750, 152027954}, + {112305648, 152106536}, + {112325752, 152175857}, + {112391448, 152290863}, + {113558250, 154187454}, + {113592048, 154226745}, + {113694351, 154313156}, + {113736549, 154335647}, + {113818145, 154367462}, + {114284454, 154490951}, + {114415847, 154504547}, + {114520751, 154489151}, + {114571350, 154478057}, + {114594551, 154472854}, + {114630546, 154463958}, + {114715148, 154429443}, + {146873657, 136143051}, + {146941741, 136074249}, + {147190155, 135763549}, + {147262649, 135654937}, + {147309951, 135557159}, + {147702255, 133903945}, + {147934143, 131616348}, + {147967041, 131273864}, + {148185852, 127892250}, + {148195648, 127669754}, + {148179656, 126409851}, + {148119552, 126182151}, + {147874053, 125334152}, + {147818954, 125150352}, + {146958557, 122656646}, + {139070251, 101025955}, + {139002655, 100879051}, + {119028450, 63067649}, + {118846649, 62740753}, + {115676048, 57814651}, + {115550453, 57629852}, + {115330352, 57319751}, + {115094749, 56998352}, + {114978347, 56847454}, + {114853050, 56740550}, + {114695053, 56609550}, + {114582252, 56528148}, + {114210449, 56375953}, + {113636245, 56214950}, + {113470352, 56171649}, + {109580749, 55503551}, + {109491645, 55495452}, + {109238754, 55511550}, + {109080352, 55534049}, + {108027748, 55687351}, + {107839950, 55732349}, + {107614456, 55834953}, + {107488143, 55925952}, + {107302551, 56062553}, + {107218353, 56145751}, + {107199447, 56167251}, + {107052749, 56354850}, + {106978652, 56476348}, + {106869644, 56710754}, + {104541351, 62448753}, + {104454551, 62672554}, + {104441253, 62707351}, + {104231750, 63366348}, + {104222648, 63419952}, + {104155746, 63922649}, + {104127349, 64147552}, + {104110847, 64299957}, + {102235450, 92366752}, + {101804351, 102877655}, + {101852752, 106261352}, + }, + { + {106808700, 120885696}, + {106818695, 120923103}, + {106873901, 121057098}, + {115123603, 133614700}, + {115128799, 133619598}, + {115182197, 133661804}, + {115330101, 133740707}, + {115455398, 133799407}, + {115595001, 133836807}, + {115651000, 133851806}, + {116413604, 134055206}, + {116654495, 134097900}, + {116887603, 134075210}, + {117071098, 134040405}, + {117458801, 133904891}, + {118057998, 133572601}, + {118546997, 133261001}, + {118578498, 133239395}, + {118818603, 133011596}, + {121109695, 130501495}, + {122661598, 128760101}, + {142458190, 102765197}, + {142789001, 102099601}, + {143105010, 101386505}, + {143154800, 101239700}, + {143193908, 100825500}, + {143160507, 100282501}, + {143133499, 100083602}, + {143092697, 99880500}, + {143050689, 99766700}, + {142657501, 98974502}, + {142580307, 98855201}, + {122267196, 76269897}, + {122036399, 76105003}, + {121832000, 76028305}, + {121688796, 75983108}, + {121591598, 75955001}, + {121119697, 75902099}, + {120789596, 75953498}, + {120487495, 76041900}, + {120042701, 76365798}, + {119886695, 76507301}, + {119774200, 76635299}, + {119739097, 76686904}, + {119685195, 76798202}, + {119456199, 77320098}, + {106877601, 119561401}, + {106854797, 119645103}, + {106849098, 119668807}, + {106847099, 119699005}, + {106840400, 119801406}, + {106807800, 120719299}, + {106806098, 120862808}, + {106808700, 120885696}, + }, + { + {99663352, 105328948}, + {99690048, 105797050}, + {99714050, 105921447}, + {99867248, 106439949}, + {100111557, 107256546}, + {104924850, 120873649}, + {105106155, 121284049}, + {105519149, 122184753}, + {105586051, 122292655}, + {105665054, 122400154}, + {106064147, 122838455}, + {106755355, 123453453}, + {106929054, 123577651}, + {107230346, 123771949}, + {107760650, 123930648}, + {108875854, 124205154}, + {108978752, 124228050}, + {131962051, 123738754}, + {135636047, 123513954}, + {135837249, 123500747}, + {136357345, 123442749}, + {136577346, 123394454}, + {136686645, 123367752}, + {137399353, 123185050}, + {137733947, 123063156}, + {137895355, 122997154}, + {138275650, 122829154}, + {138394256, 122767753}, + {138516845, 122670150}, + {139987045, 121111251}, + {149171646, 108517349}, + {149274353, 108372848}, + {149314758, 108314247}, + {149428848, 108140846}, + {149648651, 107650550}, + {149779541, 107290252}, + {149833343, 107115249}, + {149891357, 106920051}, + {150246353, 105630249}, + {150285842, 105423454}, + {150320953, 105233749}, + {150336639, 104981552}, + {150298049, 104374053}, + {150287948, 104271850}, + {150026153, 103481147}, + {149945449, 103301651}, + {149888946, 103213455}, + {149800949, 103103851}, + {149781143, 103079650}, + {149714141, 103005447}, + {149589950, 102914146}, + {149206054, 102698951}, + {128843856, 91378150}, + {128641754, 91283050}, + {119699851, 87248046}, + {117503555, 86311950}, + {117145851, 86178054}, + {116323654, 85925048}, + {115982551, 85834045}, + {115853050, 85819252}, + {115222549, 85771949}, + {107169357, 85771949}, + {107122650, 85776451}, + {106637145, 85831550}, + {105095046, 86423950}, + {104507850, 86703750}, + {104384155, 86763153}, + {104332351, 86790145}, + {104198257, 86882644}, + {103913757, 87109451}, + {103592346, 87388450}, + {103272651, 87666748}, + {103198051, 87779052}, + {101698654, 90600952}, + {101523551, 90958450}, + {101360054, 91347450}, + {101295349, 91542144}, + {99774551, 98278152}, + {99746749, 98417755}, + {99704055, 98675453}, + {99663352, 99022949}, + {99663352, 105328948}, + }, + { + {95036499, 101778106}, + {95479103, 102521301}, + {95587295, 102700103}, + {98306503, 106984901}, + {98573303, 107377700}, + {100622406, 110221702}, + {101252304, 111089599}, + {104669502, 115750198}, + {121838500, 131804107}, + {122000503, 131943695}, + {122176803, 132023406}, + {122474105, 132025390}, + {122703804, 132023101}, + {123278808, 131878112}, + {124072998, 131509109}, + {124466506, 131102508}, + {152779296, 101350906}, + {153016510, 101090606}, + {153269699, 100809097}, + {153731994, 100214096}, + {153927902, 99939796}, + {154641098, 98858100}, + {154864303, 98517601}, + {155056594, 97816604}, + {155083511, 97645599}, + {155084899, 97462097}, + {154682601, 94386100}, + {154376007, 92992599}, + {154198593, 92432403}, + {153830505, 91861701}, + {153686904, 91678695}, + {151907104, 90314605}, + {151368896, 89957603}, + {146983306, 87632202}, + {139082397, 84273605}, + {128947692, 80411399}, + {121179000, 78631301}, + {120264701, 78458198}, + {119279510, 78304603}, + {116913101, 77994102}, + {116151504, 77974601}, + {115435104, 78171401}, + {113544105, 78709106}, + {113231002, 78879898}, + {112726303, 79163604}, + {112310501, 79411102}, + {96169998, 97040802}, + {95196304, 98364402}, + {95167800, 98409599}, + {95083503, 98570701}, + {94986999, 99022201}, + {94915100, 100413299}, + {95036499, 101778106}, + }, + { + {82601348, 96004745}, + {83443847, 128861953}, + {84173248, 136147354}, + {104268249, 141388839}, + {104373649, 141395355}, + {105686950, 141389541}, + {149002243, 140435653}, + {159095748, 133388244}, + {159488143, 133112655}, + {159661849, 132894653}, + {163034149, 128290847}, + {164801849, 124684249}, + {167405746, 72553245}, + {167330444, 71960746}, + {167255050, 71791847}, + {167147155, 71572044}, + {166999557, 71341545}, + {166723937, 70961448}, + {166238250, 70611541}, + {165782348, 70359649}, + {165649444, 70286849}, + {165332946, 70122344}, + {165164154, 70062248}, + {164879150, 69967544}, + {164744949, 69928947}, + {164691452, 69915245}, + {164669448, 69910247}, + {159249938, 68738952}, + {158528259, 68704742}, + {147564254, 68604644}, + {116196655, 68982742}, + {115364944, 69005050}, + {115193145, 69013549}, + {101701248, 70984146}, + {93918449, 72233047}, + {93789749, 72285247}, + {93777046, 72292648}, + {93586044, 72444046}, + {93366348, 72662345}, + {93301147, 72745452}, + {93260345, 72816345}, + {83523948, 92593849}, + {83430145, 92810241}, + {82815048, 94665542}, + {82755554, 94858551}, + {82722953, 95014350}, + {82594253, 95682350}, + {82601348, 96004745}, + }, + { + {110371345, 125796493}, + {110411544, 126159599}, + {110445251, 126362899}, + {111201950, 127863800}, + {112030052, 129270492}, + {112367050, 129799301}, + {113088348, 130525604}, + {113418144, 130853698}, + {117363449, 134705505}, + {118131149, 135444793}, + {118307449, 135607299}, + {119102546, 136297195}, + {119385047, 136531906}, + {120080848, 137094390}, + {120794845, 137645401}, + {121150344, 137896392}, + {121528945, 138162506}, + {121644546, 138242095}, + {122142349, 138506408}, + {127540847, 141363006}, + {127933448, 141516204}, + {128728256, 141766799}, + {129877151, 141989898}, + {130626052, 142113891}, + {130912246, 142135192}, + {131246841, 142109100}, + {131496047, 142027404}, + {131596252, 141957794}, + {131696350, 141873504}, + {131741043, 141803405}, + {138788452, 128037704}, + {139628646, 125946197}, + {138319351, 112395401}, + {130035354, 78066703}, + {124174049, 69908798}, + {123970649, 69676895}, + {123874252, 69571899}, + {123246643, 68961303}, + {123193954, 68924400}, + {121952049, 68110000}, + {121787345, 68021896}, + {121661544, 67970306}, + {121313446, 67877502}, + {121010650, 67864799}, + {120995346, 67869705}, + {120583747, 68122207}, + {120509750, 68170600}, + {120485847, 68189102}, + {112160148, 77252403}, + {111128646, 78690704}, + {110969650, 78939407}, + {110512550, 79663406}, + {110397247, 79958206}, + {110371345, 80038299}, + {110371345, 125796493}, + }, + { + {112163948, 137752700}, + {112171150, 137837997}, + {112203048, 137955993}, + {112240150, 138008209}, + {112343246, 138111099}, + {112556243, 138223205}, + {112937149, 138307998}, + {113318748, 138331909}, + {126076446, 138428298}, + {126165245, 138428695}, + {126312446, 138417907}, + {134075546, 136054504}, + {134322753, 135949401}, + {134649948, 135791198}, + {135234954, 135493408}, + {135290145, 135464691}, + {135326248, 135443695}, + {135920043, 135032592}, + {135993850, 134975799}, + {136244247, 134761199}, + {136649444, 134378692}, + {137067153, 133964294}, + {137188156, 133839096}, + {137298049, 133704498}, + {137318954, 133677795}, + {137413543, 133522201}, + {137687347, 133043792}, + {137816055, 132660705}, + {137836044, 131747695}, + {137807144, 131318603}, + {136279342, 119078704}, + {136249053, 118945800}, + {127306152, 81348602}, + {127114852, 81065505}, + {127034248, 80951400}, + {126971649, 80893707}, + {125093551, 79178001}, + {124935745, 79036003}, + {115573745, 71767601}, + {115411148, 71701805}, + {115191947, 71621002}, + {115017051, 71571304}, + {114870147, 71572898}, + {113869552, 71653900}, + {112863349, 72976104}, + {112756347, 73223899}, + {112498947, 73832206}, + {112429351, 73998504}, + {112366050, 74168098}, + {112273246, 74487098}, + {112239250, 74605400}, + {112195549, 74899902}, + {112163948, 75280700}, + {112163948, 137752700}, + }, + { + {78562347, 141451843}, + {79335624, 142828186}, + {79610343, 143188140}, + {79845077, 143445724}, + {81379173, 145126678}, + {81826751, 145577178}, + {82519126, 146209472}, + {83964973, 147280502}, + {85471343, 148377868}, + {86115539, 148760803}, + {88839988, 150281188}, + {89021247, 150382217}, + {90775917, 151320526}, + {91711380, 151767288}, + {92757591, 152134277}, + {93241058, 152201766}, + {113402145, 153091995}, + {122065994, 146802825}, + {164111053, 91685104}, + {164812759, 90470565}, + {165640182, 89037384}, + {171027435, 66211853}, + {171450805, 64406951}, + {171463150, 64349624}, + {171469787, 64317184}, + {171475585, 64282028}, + {171479812, 64253036}, + {171483596, 64210433}, + {171484405, 64153488}, + {171483001, 64140785}, + {171481719, 64132751}, + {171478668, 64115478}, + {171472702, 64092437}, + {171462768, 64075408}, + {171448089, 64061347}, + {171060333, 63854789}, + {169640502, 63197738}, + {169342147, 63086711}, + {166413101, 62215766}, + {151881774, 58826736}, + {146010574, 57613151}, + {141776962, 56908004}, + {140982940, 57030628}, + {139246154, 57540817}, + {139209609, 57566974}, + {127545310, 66015594}, + {127476654, 66104812}, + {105799087, 98784980}, + {85531921, 129338897}, + {79319717, 138704513}, + {78548156, 140188079}, + {78530448, 140530456}, + {78515594, 141299987}, + {78562347, 141451843}, + }, + { + {77755004, 128712387}, + {78073547, 130552612}, + {78433593, 132017822}, + {79752693, 136839645}, + {80479461, 138929260}, + {80903221, 140119674}, + {81789848, 141978454}, + {82447387, 143105575}, + {83288436, 144264328}, + {84593582, 145846542}, + {84971939, 146242813}, + {86905578, 147321304}, + {87874191, 147594131}, + {89249092, 147245132}, + {89541542, 147169052}, + {98759140, 144071609}, + {98894233, 144024261}, + {113607818, 137992843}, + {128324356, 131649307}, + {139610076, 126210189}, + {146999572, 122112884}, + {147119415, 122036041}, + {148717330, 120934616}, + {149114776, 120652725}, + {171640289, 92086624}, + {171677917, 92036224}, + {171721191, 91973869}, + {171851608, 91721557}, + {171927795, 91507644}, + {172398696, 89846351}, + {172436752, 89559959}, + {169361663, 64753852}, + {169349029, 64687164}, + {169115127, 63616458}, + {168965728, 63218254}, + {168911788, 63121219}, + {168901611, 63106807}, + {168896896, 63100486}, + {168890686, 63092460}, + {168876586, 63081058}, + {168855529, 63067909}, + {168808746, 63046024}, + {167251068, 62405864}, + {164291717, 63716899}, + {152661651, 69910156}, + {142312393, 75421356}, + {78778053, 111143295}, + {77887222, 113905914}, + {77591979, 124378433}, + {77563247, 126586669}, + {77755004, 128712387}, + }, + { + {105954101, 131182754}, + {105959197, 131275848}, + {105972801, 131473556}, + {105981498, 131571044}, + {106077903, 132298553}, + {106134094, 132715255}, + {106155700, 132832351}, + {106180099, 132942657}, + {106326797, 133590347}, + {106375099, 133719345}, + {106417602, 133829345}, + {106471000, 133930343}, + {106707901, 134308654}, + {106728401, 134340545}, + {106778198, 134417556}, + {106832397, 134491851}, + {106891296, 134562957}, + {106981300, 134667358}, + {107044204, 134736557}, + {107111000, 134802658}, + {107180999, 134865661}, + {107291099, 134961349}, + {107362998, 135020355}, + {107485397, 135112854}, + {107558998, 135166946}, + {107690399, 135256256}, + {107765098, 135305252}, + {107903594, 135390548}, + {108183898, 135561843}, + {108459503, 135727951}, + {108532501, 135771850}, + {108796096, 135920059}, + {108944099, 135972549}, + {109102401, 136010757}, + {109660598, 136071044}, + {109971595, 136100250}, + {110209594, 136116851}, + {110752799, 136122344}, + {111059906, 136105758}, + {111152900, 136100357}, + {111237197, 136091354}, + {111316101, 136075057}, + {111402000, 136050949}, + {111475296, 136026657}, + {143546600, 123535949}, + {143899002, 122454353}, + {143917404, 122394348}, + {143929199, 122354652}, + {143944793, 122295753}, + {143956207, 122250953}, + {143969497, 122192253}, + {143980102, 122143249}, + {143991302, 122083053}, + {144000396, 122031753}, + {144009796, 121970954}, + {144017303, 121917655}, + {144025405, 121850250}, + {144030609, 121801452}, + {144036804, 121727455}, + {144040008, 121683456}, + {144043502, 121600952}, + {144044708, 121565048}, + {144045700, 121470352}, + {144045898, 121446952}, + {144041503, 121108657}, + {144037506, 121023452}, + {143733795, 118731750}, + {140461395, 95238647}, + {140461105, 95236755}, + {140433807, 95115249}, + {140392608, 95011650}, + {134840805, 84668952}, + {134824996, 84642456}, + {134781494, 84572952}, + {134716796, 84480850}, + {127473899, 74425453}, + {127467002, 74417152}, + {127431701, 74381652}, + {127402603, 74357147}, + {127375503, 74334457}, + {127294906, 74276649}, + {127181900, 74207649}, + {127177597, 74205451}, + {127123901, 74178451}, + {127078903, 74155853}, + {127028999, 74133148}, + {126870803, 74070953}, + {126442901, 73917648}, + {126432403, 73914955}, + {126326004, 73889846}, + {126262405, 73880645}, + {126128097, 73878456}, + {125998199, 73877655}, + {108701095, 74516647}, + {108644599, 74519348}, + {108495201, 74528953}, + {108311302, 74556457}, + {108252799, 74569458}, + {108079002, 74612152}, + {107981399, 74638954}, + {107921295, 74657951}, + {107862197, 74685951}, + {107601303, 74828948}, + {107546997, 74863449}, + {107192794, 75098846}, + {107131202, 75151153}, + {106260002, 76066146}, + {106195098, 76221145}, + {106168502, 76328453}, + {106144699, 76437454}, + {106124496, 76538452}, + {106103698, 76649650}, + {106084197, 76761650}, + {106066299, 76874450}, + {106049903, 76987457}, + {106034797, 77101150}, + {106020904, 77214950}, + {106008201, 77328948}, + {105996902, 77443145}, + {105986099, 77565849}, + {105977005, 77679649}, + {105969299, 77793151}, + {105963096, 77906349}, + {105958297, 78019149}, + {105955299, 78131454}, + {105954101, 78242950}, + {105954101, 131182754}, + }, + { + {91355499, 77889205}, + {114834197, 120804504}, + {114840301, 120815200}, + {124701507, 132324798}, + {124798805, 132436706}, + {124901504, 132548309}, + {125126602, 132788909}, + {125235000, 132901901}, + {125337707, 133005401}, + {125546302, 133184707}, + {125751602, 133358703}, + {126133300, 133673004}, + {126263900, 133775604}, + {126367401, 133855499}, + {126471908, 133935104}, + {126596008, 134027496}, + {127119308, 134397094}, + {127135101, 134408203}, + {127433609, 134614303}, + {127554107, 134695709}, + {128155395, 135070907}, + {128274505, 135141799}, + {129132003, 135573211}, + {129438003, 135713195}, + {129556106, 135767196}, + {131512695, 136648498}, + {132294509, 136966598}, + {132798400, 137158798}, + {133203796, 137294494}, + {133377410, 137350799}, + {133522399, 137396606}, + {133804397, 137480697}, + {134017807, 137542205}, + {134288696, 137618408}, + {134564208, 137680099}, + {134844696, 137740097}, + {135202606, 137807098}, + {135489105, 137849807}, + {135626800, 137864898}, + {135766906, 137878692}, + {135972808, 137895797}, + {136110107, 137905502}, + {136235000, 137913101}, + {136485809, 137907196}, + {139194305, 136979202}, + {140318298, 136536209}, + {140380004, 136505004}, + {140668197, 136340499}, + {140724304, 136298904}, + {140808197, 136228210}, + {140861801, 136180603}, + {140917404, 136129104}, + {140979202, 136045104}, + {141022903, 135984207}, + {147591094, 126486999}, + {147661315, 126356101}, + {147706100, 126261901}, + {147749099, 126166000}, + {147817108, 126007507}, + {147859100, 125908599}, + {153693206, 111901100}, + {153731109, 111807800}, + {153760894, 111698806}, + {158641998, 92419303}, + {158644500, 92263702}, + {158539703, 92013504}, + {158499603, 91918899}, + {158335510, 91626800}, + {158264007, 91516304}, + {158216308, 91449203}, + {158178314, 91397506}, + {158094299, 91283203}, + {157396408, 90368202}, + {157285491, 90224700}, + {157169906, 90079200}, + {157050003, 89931304}, + {156290603, 89006805}, + {156221099, 88922897}, + {156087707, 88771003}, + {155947906, 88620498}, + {155348602, 88004203}, + {155113204, 87772796}, + {154947296, 87609703}, + {154776306, 87448204}, + {154588806, 87284301}, + {153886306, 86716400}, + {153682403, 86560501}, + {152966705, 86032402}, + {152687805, 85828704}, + {152484313, 85683204}, + {152278808, 85539001}, + {150878204, 84561401}, + {150683013, 84426498}, + {150599395, 84372703}, + {150395599, 84243202}, + {149988906, 83989395}, + {149782897, 83864501}, + {149568908, 83739799}, + {148872100, 83365303}, + {148625396, 83242202}, + {128079010, 73079605}, + {127980506, 73031005}, + {126701103, 72407104}, + {126501701, 72312202}, + {126431503, 72280601}, + {126311706, 72230606}, + {126260101, 72210899}, + {126191902, 72187599}, + {126140106, 72170303}, + {126088203, 72155303}, + {126036102, 72142700}, + {125965904, 72126899}, + {125913009, 72116600}, + {125859603, 72108505}, + {125788101, 72100296}, + {125733505, 72094398}, + {125678100, 72090400}, + {125621398, 72088302}, + {125548805, 72087303}, + {125490707, 72086898}, + {125430908, 72088203}, + {125369804, 72091094}, + {125306900, 72095306}, + {125233505, 72100997}, + {125168609, 72106506}, + {125102203, 72113601}, + {125034103, 72122207}, + {124964309, 72132095}, + {124890701, 72143707}, + {124819305, 72155105}, + {91355499, 77889099}, + {91355499, 77889205}, + }, + { + {84531845, 127391708}, + {84916946, 130417510}, + {86133247, 131166900}, + {86338447, 131292892}, + {86748847, 131544799}, + {102193946, 136599502}, + {103090942, 136796798}, + {103247146, 136822509}, + {104083549, 136911499}, + {106119346, 137109802}, + {106265853, 137122207}, + {106480247, 137139205}, + {110257850, 137133605}, + {116917747, 136131408}, + {117054946, 136106704}, + {119043945, 135244293}, + {119249046, 135154708}, + {136220947, 126833007}, + {165896347, 91517105}, + {166032546, 91314697}, + {166055435, 91204902}, + {166056152, 91176803}, + {166047256, 91100006}, + {166039733, 91063705}, + {165814849, 90080802}, + {165736450, 89837707}, + {165677246, 89732101}, + {165676956, 89731803}, + {165560241, 89629302}, + {154419952, 82608505}, + {153822143, 82239700}, + {137942749, 74046104}, + {137095245, 73845504}, + {135751342, 73537704}, + {134225952, 73208602}, + {132484344, 72860801}, + {124730346, 73902000}, + {120736549, 74464401}, + {100401245, 78685401}, + {90574645, 90625701}, + {90475944, 90748809}, + {90430747, 90808700}, + {90321548, 90958305}, + {90254852, 91077903}, + {90165641, 91244003}, + {90134941, 91302398}, + {84474647, 103745697}, + {84328048, 104137901}, + {84288543, 104327606}, + {84038047, 106164604}, + {84013351, 106368698}, + {83943847, 110643203}, + {84531845, 127391708}, + }, +}; + +using Slic3r::ExPolygon; +using Slic3r::Polygon; +using Slic3r::Polygons; +using Slic3r::ExPolygons; + +struct MyPoly { + ExPolygon poly; + MyPoly(Polygon contour, Polygons holes) + : poly(std::move(contour)) + { + poly.holes = std::move(holes); + } + + operator ExPolygon () { return poly; } +}; + +const TestDataEx PRUSA_PART_POLYGONS_EX = { + ExPolygons{ + // "x-carriage.stl": + MyPoly{{ + {-22097700, -14878600}, {-21981300, -14566100}, + {-21807600, -14303900}, {-21354100, -13619200}, + {-20514800, -12806600}, {-19500000, -12163900}, + {-18553700, -11796600}, {-18354100, -11719200}, + {-18146200, -11680600}, {-17127200, -11491800}, + {-15872800, -11491800}, {-14853800, -11680600}, + {-14645900, -11719200}, {-14446300, -11796600}, + {-13500000, -12163900}, {-12485200, -12806600}, + {-11645900, -13619200}, {-11192400, -14303900}, + {-11018700, -14566100}, {-10902300, -14878600}, + {-10857000, -15000000}, {-2200000, -15000000}, + {-2242640, -14957400}, {500000, -12214700}, + {500000, 5500000}, {9450000, 5500000}, + {9450000, 7500000}, {273885, 7500000}, + {273885, 11050000}, {2706110, 11050000}, + {2706110, 11000000}, {9500000, 11000000}, + {9500000, 66500000}, {7466310, 68533696}, + {999999, 75000000}, {-8500000, 75000000}, + {-8500000, 74250000}, {-7500000, 74250000}, + {-7500000, 71750000}, {-8500000, 71750000}, + {-8500000, 68250000}, {-7500000, 68250000}, + {-7500000, 65750000}, {-8500000, 65750000}, + {-8500000, 64000000}, {-12500000, 64000000}, + {-12500000, 67000000}, {-14500000, 67000000}, + {-14500000, 73000000}, {-12500000, 73000000}, + {-12500000, 75000000}, {-23000000, 75000000}, + {-23000000, 59500000}, {-38500000, 59500000}, + {-42500000, 55500000}, {-42500000, 19536000}, + {-36767700, 18000000}, {-34000000, 18000000}, + {-34000000, 13000000}, {-39900000, 13000000}, + {-39900000, 11000000}, {-34000000, 11000000}, + {-34000000, 7500000}, {-39900000, 7500000}, + {-39900000, 5500000}, {-34000000, 5500000}, + {-34000000, -11714700}, {-30757400, -14957400}, + {-30800000, -15000000}, {-22143000, -15000000}, + }, + { + { + {2311850, 65709900}, {2076590, 65759904}, + {1943770, 65788100}, {1600000, 65941200}, + {1362567, 66113636}, {1329590, 66137604}, + {1295560, 66162300}, {1043769, 66442000}, + {855618, 66767900}, {739334, 67125800}, + {714193, 67365000}, {700000, 67500000}, + {714193, 67635000}, {739334, 67874200}, + {855618, 68232104}, {1043769, 68558000}, + {1295560, 68837696}, {1329590, 68862400}, + {1352596, 68879119}, {1600000, 69058800}, + {1943770, 69211896}, {2076590, 69240096}, + {2311850, 69290104}, {2688150, 69290104}, + {3056230, 69211896}, {3400000, 69058800}, + {3541910, 68955704}, {3704430, 68837696}, + {3762210, 68773496}, {3865370, 68658896}, + {3956230, 68558000}, {4024119, 68440400}, + {4065821, 68368176}, {4144380, 68232104}, + {4260660, 67874200}, {4300000, 67500000}, + {4260660, 67125800}, {4144380, 66767900}, + {4024119, 66559600}, {3956230, 66442000}, + {3865370, 66341104}, {3762210, 66226500}, + {3704430, 66162300}, {3541910, 66044296}, + {3400000, 65941200}, {3056230, 65788100}, + {2688150, 65709900}, + }, + { + {-27606700, 54303400}, {-27818500, 54330100}, + {-27896000, 54350000}, {-28025300, 54383200}, + {-28223800, 54461800}, {-28410900, 54564600}, + {-28583600, 54690100}, {-28739200, 54836300}, + {-28875300, 55000800}, {-28989700, 55181000}, + {-29080600, 55374200}, {-29146600, 55577200}, + {-29150000, 55595100}, {-29186600, 55786900}, + {-29200000, 56000000}, {-29186600, 56213100}, + {-29150000, 56404900}, {-29146600, 56422800}, + {-29080600, 56625800}, {-28989700, 56819000}, + {-28875300, 56999200}, {-28739200, 57163700}, + {-28583600, 57309900}, {-28410900, 57435400}, + {-28223800, 57538200}, {-28025300, 57616800}, + {-27896000, 57650000}, {-27818500, 57669900}, + {-27606700, 57696600}, {-27393300, 57696600}, + {-27181400, 57669900}, {-27104000, 57650000}, + {-26974700, 57616800}, {-26776200, 57538200}, + {-26589100, 57435400}, {-26416400, 57309900}, + {-26260800, 57163700}, {-26124700, 56999200}, + {-26010300, 56819000}, {-25919400, 56625800}, + {-25853400, 56422800}, {-25850000, 56404900}, + {-25813400, 56213100}, {-25800000, 56000000}, + {-25813400, 55786900}, {-25850000, 55595100}, + {-25853400, 55577200}, {-25919400, 55374200}, + {-26010300, 55181000}, {-26124700, 55000800}, + {-26260800, 54836300}, {-26416400, 54690100}, + {-26589100, 54564600}, {-26776200, 54461800}, + {-26974700, 54383200}, {-27104000, 54350000}, + {-27181400, 54330100}, {-27393300, 54303400}, + }, + { + {-4106740, 54303400}, {-4318550, 54330100}, + {-4396010, 54350000}, {-4525330, 54383200}, + {-4723820, 54461800}, {-4910900, 54564600}, + {-5083620, 54690100}, {-5239250, 54836300}, + {-5375330, 55000800}, {-5489720, 55181000}, + {-5580620, 55374200}, {-5646590, 55577200}, + {-5650000, 55595100}, {-5686590, 55786900}, + {-5700000, 56000000}, {-5686590, 56213100}, + {-5650000, 56404900}, {-5646590, 56422800}, + {-5580620, 56625800}, {-5489720, 56819000}, + {-5375330, 56999200}, {-5239250, 57163700}, + {-5083620, 57309900}, {-4910900, 57435400}, + {-4723820, 57538200}, {-4525330, 57616800}, + {-4396010, 57650000}, {-4318550, 57669900}, + {-4106740, 57696600}, {-3893260, 57696600}, + {-3681450, 57669900}, {-3603990, 57650000}, + {-3474670, 57616800}, {-3276170, 57538200}, + {-3089090, 57435400}, {-2916380, 57309900}, + {-2760750, 57163700}, {-2624670, 56999200}, + {-2510280, 56819000}, {-2419380, 56625800}, + {-2353410, 56422800}, {-2350000, 56404900}, + {-2313400, 56213100}, {-2300000, 56000000}, + {-2313400, 55786900}, {-2350000, 55595100}, + {-2353410, 55577200}, {-2419380, 55374200}, + {-2510280, 55181000}, {-2624670, 55000800}, + {-2760750, 54836300}, {-2916380, 54690100}, + {-3089090, 54564600}, {-3276170, 54461800}, + {-3474670, 54383200}, {-3603990, 54350000}, + {-3681450, 54330100}, {-3893260, 54303400}, + }, + { + {-16103600, 27353300}, {-16309200, 27379200}, + {-16509899, 27430800}, {-16702499, 27507000}, + {-16884100, 27606900}, {-17051800, 27728700}, + {-17202800, 27870500}, {-17334900, 28030200}, + {-17445900, 28205100}, {-17534100, 28392600}, + {-17598200, 28589700}, {-17637000, 28793200}, + {-17650000, 29000000}, {-17637000, 29206800}, + {-17598200, 29410300}, {-17534100, 29607400}, + {-17445900, 29794900}, {-17334900, 29969800}, + {-17202800, 30129500}, {-17051800, 30271300}, + {-16884100, 30393100}, {-16702499, 30493000}, + {-16509899, 30569200}, {-16309200, 30620800}, + {-16103600, 30646700}, {-15896400, 30646700}, + {-15690800, 30620800}, {-15490100, 30569200}, + {-15297500, 30493000}, {-15115900, 30393100}, + {-14948200, 30271300}, {-14797200, 30129500}, + {-14665100, 29969800}, {-14554100, 29794900}, + {-14465900, 29607400}, {-14401800, 29410300}, + {-14363000, 29206800}, {-14350000, 29000000}, + {-14363000, 28793200}, {-14401800, 28589700}, + {-14465900, 28392600}, {-14554100, 28205100}, + {-14665100, 28030200}, {-14797200, 27870500}, + {-14948200, 27728700}, {-15115900, 27606900}, + {-15297500, 27507000}, {-15490100, 27430800}, + {-15690800, 27379200}, {-15896400, 27353300}, + }, + { + {-5809180, 22879200}, {-6202540, 23007000}, + {-6551750, 23228700}, {-6834880, 23530200}, + {-7034130, 23892600}, {-7136990, 24293200}, + {-7136990, 24706800}, {-7034130, 25107400}, + {-6834880, 25469800}, {-6551750, 25771300}, + {-6202540, 25993000}, {-5809180, 26120800}, + {-5396390, 26146700}, {-4990120, 26069200}, + {-4615890, 25893100}, {-4297200, 25629500}, + {-4054090, 25294900}, {-3901840, 24910300}, + {-3850000, 24500000}, {-3901840, 24089700}, + {-4054090, 23705100}, {-4297200, 23370500}, + {-4615890, 23106900}, {-4990120, 22930800}, + {-5396390, 22853300}, + }, + { + {-28809200, 22879200}, {-29202500, 23007000}, + {-29551800, 23228700}, {-29834900, 23530200}, + {-30034100, 23892600}, {-30137000, 24293200}, + {-30137000, 24706800}, {-30034100, 25107400}, + {-29834900, 25469800}, {-29551800, 25771300}, + {-29202500, 25993000}, {-28809200, 26120800}, + {-28396400, 26146700}, {-27990100, 26069200}, + {-27615900, 25893100}, {-27297200, 25629500}, + {-27054100, 25294900}, {-26901800, 24910300}, + {-26850000, 24500000}, {-26901800, 24089700}, + {-27054100, 23705100}, {-27297200, 23370500}, + {-27615900, 23106900}, {-27990100, 22930800}, + {-28396400, 22853300}, + }, + { + {-15718329, 8800000}, + {-15729700, 8808230}, + {-15736300, 8814060}, + {-15742800, 8833890}, + {-15876410, 9243607}, + {-15729700, 9696850}, + {-14969700, 10251100}, + {-14030300, 10251100}, + {-13270300, 9696850}, + {-13123590, 9243607}, + {-13257200, 8833890}, + {-13263700, 8814060}, + {-13270300, 8808230}, + {-13281671, 8800000}, + }, + }}, + }, + ExPolygons{ + // "Spool-holder.stl": + MyPoly{{ + {338485792, -31307222}, {338867040, -31018436}, + {339248320, -30729652}, {339769915, -30334566}, + {340010848, -30152082}, {340392096, -29863298}, + {340773344, -29574512}, {341244704, -27899436}, + {341480384, -27061900}, {342060734, -24999444}, + {342187424, -24549286}, {343068058, -21419626}, + {343130112, -21199134}, {343521972, -19806477}, + {344953440, -14719350}, {345583712, -12479458}, + {345898880, -11359512}, {346213984, -10239566}, + {346529152, -9119620}, {346684120, -8568830}, + {347258496, -6527694}, {348879776, -765989}, + {351121248, 7199785}, {351160318, 7338666}, + {351581888, 8836852}, {358349952, 32889144}, + {361733984, 44915292}, {362502080, 47644968}, + {365226370, 57326618}, {367181933, 64276284}, + {369782208, 73517048}, {372004549, 81414857}, + {375270080, 93019880}, {377062304, 99389120}, + {380702368, 112325160}, {387982496, 138197232}, + {390913664, 148614048}, {392379232, 153822448}, + {392462848, 154165648}, {392500992, 154371968}, + {392523776, 154527056}, {392558720, 154903888}, + {392560704, 154943056}, {392564832, 155292544}, + {392554016, 155525040}, {392539872, 155688624}, + {392482592, 156087152}, {392479040, 156106240}, + {392392608, 156482704}, {392336512, 156674688}, + {392270816, 156869776}, {392130112, 157218896}, + {392119264, 157242992}, {391941088, 157597536}, + {391865920, 157728704}, {391740320, 157929344}, + {391551872, 158195776}, {391521632, 158235296}, + {391290048, 158513360}, {391187328, 158624592}, + {391050656, 158762640}, {390808320, 158983152}, + {390768160, 159017008}, {390567456, 159175840}, + {390331776, 159342352}, {390300192, 159363104}, + {389874464, 159612112}, {389791584, 159654240}, + {389337216, 159852608}, {389252448, 159883872}, + {388769664, 160029808}, {388694016, 160047936}, + {388177408, 160139328}, {388128128, 160145120}, + {387566176, 160176800}, {375407744, 160176800}, + {374915072, 160152480}, {374676896, 160123104}, + {374431712, 160080592}, {374176224, 160022768}, + {373936576, 159955472}, {373914144, 159948512}, + {373647424, 159856688}, {373378176, 159746368}, + {373213376, 159669552}, {373108832, 159616976}, + {372841920, 159468256}, {372580064, 159300432}, + {372535328, 159269408}, {372325888, 159114096}, + {372081888, 158910256}, {371928512, 158767776}, + {371850368, 158690384}, {371633408, 158456224}, + {371432736, 158209856}, {371413792, 158184848}, + {371249664, 157953552}, {371085088, 157689664}, + {371004448, 157545600}, {370939520, 157420656}, + {370813088, 157148864}, {370705536, 156876560}, + {368543808, 150896416}, {363439712, 136776352}, + {358631104, 123473712}, {358408023, 122856568}, + {356409504, 117327816}, {355922364, 115980139}, + {355915437, 115960977}, {352669088, 106980280}, + {351986938, 105093166}, {351083520, 102593952}, + {349781616, 98992320}, {348098080, 94334952}, + {340262048, 72657256}, {338954668, 69040480}, + {336962208, 63528480}, {332255104, 50506656}, + {327202016, 36527760}, {322789144, 24319856}, + {322760544, 24240790}, {322533686, 23613197}, + {322519552, 23574124}, {320672032, 18463012}, + {320578304, 18203810}, {320531456, 18074210}, + {320484608, 17944606}, {320437760, 17815006}, + {320297248, 17426202}, {320297248, 9238203}, + {321164066, 9238176}, {321689312, 9238155}, + {323777376, 9238073}, {324473408, 9238046}, + {325169440, 9238018}, {325496384, 10782991}, + {325496360, 12868520}, {327892320, 12868504}, + {329090336, 12868496}, {330288320, 12868487}, + {331486336, 12868480}, {332684279, 12868472}, + {332096736, 10092249}, {332096736, -26761938}, + {325697024, -26761692}, {325697024, -26311692}, + {324897056, -26311692}, {323897056, -27061692}, + {323897056, -29536704}, {323897088, -30511784}, + {323897088, -32461944}, {323897091, -32461944}, + {328084288, -32462100}, {329480000, -32462150}, + {332271456, -32462258}, {335062912, -32462360}, + {336960768, -32462360}, + }, + { + { + {376588032, 136952960}, {375810912, 137178704}, + {375411104, 137361136}, {375032960, 137582800}, + {374679360, 137841776}, {374353152, 138136224}, + {374058688, 138462448}, {373799680, 138816048}, + {373578048, 139194192}, {373395584, 139594016}, + {373169856, 140371136}, {373093728, 141176800}, + {373169856, 141982464}, {373395584, 142759600}, + {373578048, 143159392}, {373799680, 143537536}, + {374058688, 143891136}, {374353152, 144217360}, + {374679360, 144511824}, {375032960, 144770816}, + {375411104, 144992464}, {375810912, 145174896}, + {376588032, 145400656}, {377393696, 145476800}, + {378199360, 145400656}, {378976512, 145174896}, + {379376320, 144992480}, {379754464, 144770816}, + {380108064, 144511808}, {380434272, 144217360}, + {380728736, 143891136}, {380987744, 143537536}, + {381209376, 143159392}, {381391776, 142759600}, + {381617568, 141982464}, {381693696, 141176800}, + {381617568, 140371136}, {381391776, 139594000}, + {381209376, 139194192}, {380987744, 138816064}, + {380728736, 138462464}, {380434272, 138136240}, + {380108064, 137841792}, {379754464, 137582800}, + {379376320, 137361136}, {378976512, 137178704}, + {378199360, 136952960}, {377393696, 136876800}, + }, + { + {354604704, 97626944}, {355293600, 99532704}, + {355982496, 101438472}, {356671392, 103344232}, + {357360288, 105250000}, {358424054, 108192839}, + {358738080, 109061520}, {359426976, 110967296}, + {360115840, 112873056}, {362111392, 113825960}, + {368097952, 116684672}, {370093472, 117637584}, + {372089024, 118590488}, {374084544, 119543392}, + {378075584, 121449192}, {377003072, 117637704}, + {375930560, 113826200}, {375394304, 111920456}, + {374321792, 108108952}, {373249280, 104297464}, + {368952928, 102391616}, {362508448, 99532856}, + {360360288, 98579944}, {358212128, 97627024}, + {356063968, 96674096}, {353915808, 95721176}, + }, + { + {342204640, 63323192}, {342893536, 65228960}, + {344271328, 69040480}, {344960192, 70946248}, + {345649120, 72852016}, {346338016, 74757776}, + {347026880, 76663536}, {347715776, 78569304}, + {354618176, 81428112}, {356918976, 82381040}, + {359219776, 83333976}, {361520576, 84286920}, + {363821376, 85239856}, {366122176, 86192784}, + {368422976, 87145720}, {367886720, 85239976}, + {366814208, 81428472}, {366277952, 79522728}, + {365741664, 77616984}, {365205408, 75711224}, + {364132896, 71899736}, {363596640, 69993984}, + {361143232, 69041032}, {356236352, 67135128}, + {353782944, 66182184}, {351329504, 65229232}, + {348876064, 64276284}, {346422592, 63323328}, + {343969184, 62370380}, {341515744, 61417428}, + }, + { + {329804576, 29019442}, {330493472, 30925208}, + {331182368, 32830970}, {331871232, 34736732}, + {332560160, 36642500}, {333249056, 38548264}, + {333937920, 40454024}, {334626816, 42359792}, + {335315744, 44265552}, {337921792, 45218520}, + {340527872, 46171484}, {343133952, 47124452}, + {345740000, 48077416}, {348346080, 49030384}, + {350952160, 49983348}, {353558208, 50936312}, + {356164288, 51889284}, {358770368, 52842248}, + {358234112, 50936496}, {357697856, 49030752}, + {357161568, 47125000}, {356089056, 43313504}, + {355552800, 41407752}, {355016544, 39502008}, + {354480288, 37596256}, {353944032, 35690508}, + {351185344, 34737524}, {348426624, 33784544}, + {345667904, 32831566}, {342909216, 31878584}, + {340150528, 30925604}, {334633088, 29019640}, + {331874400, 28066660}, {329115680, 27113678}, + }, + }}, + }, + ExPolygons{ + // "x-end-idler.stl": + MyPoly{{ + {-6500000, -10475000}, {0, -10475000}, + {0, -10468600}, {365572, -10468600}, + {1094940, -10417600}, {1818960, -10315900}, + {2534130, -10163800}, {3236950, -9962320}, + {3924000, -9712250}, {4591940, -9414870}, + {5237500, -9071620}, {5857540, -8684170}, + {6449050, -8254410}, {7009140, -7784440}, + {7535080, -7276550}, {8024310, -6733200}, + {8474450, -6157050}, {8883300, -5550900}, + {9248880, -4917710}, {9569390, -4260570}, + {9843280, -3582660}, {10069200, -2887300}, + {10246100, -2177870}, {10373100, -1457840}, + {10449500, -730699}, {10475000, 0}, + {10449500, 730699}, {10373100, 1457840}, + {10246100, 2177870}, {10069200, 2887300}, + {9843280, 3582660}, {9569390, 4260570}, + {9248880, 4917710}, {8883300, 5550900}, + {8474450, 6157050}, {8024310, 6733200}, + {7739860, 7049120}, {8047300, 7272490}, + {9203020, 8357790}, {10213600, 9579380}, + {11063100, 10918000}, {11738200, 12352500}, + {12228100, 13860400}, {12525200, 15417700}, + {12624700, 17000000}, {12525200, 18582300}, + {12228100, 20139600}, {11738200, 21647500}, + {11063100, 23082000}, {10213600, 24420600}, + {9203020, 25642200}, {8047300, 26727500}, + {6764660, 27659400}, {5375340, 28423200}, + {3901250, 29006800}, {2365630, 29401100}, + {792712, 29599800}, {-792712, 29599800}, + {-2365630, 29401100}, {-3901250, 29006800}, + {-5181320, 28500000}, {-23500000, 28500000}, + {-23500000, -9000000}, {-22000000, -10500000}, + {-6500000, -10500000}, + }, + { + { + {6562230, 22074800}, {6357580, 22107300}, + {6158600, 22165100}, {5968430, 22247400}, + {5790080, 22352800}, {5626350, 22479800}, + {5479830, 22626400}, {5352830, 22790100}, + {5247350, 22968400}, {5165060, 23158600}, + {5107250, 23357600}, {5074840, 23562200}, + {5068330, 23769300}, {5087830, 23975600}, + {5133030, 24177800}, {5203220, 24372800}, + {5297290, 24557400}, {5413760, 24728800}, + {5550790, 24884200}, {5706220, 25021300}, + {5877600, 25137700}, {6062220, 25231800}, + {6257180, 25302000}, {6459400, 25347200}, + {6665690, 25366700}, {6872790, 25360200}, + {7077450, 25327800}, {7276430, 25270000}, + {7466600, 25187700}, {7644950, 25082200}, + {7808680, 24955200}, {7955200, 24808700}, + {8082200, 24645000}, {8187670, 24466600}, + {8269970, 24276400}, {8327780, 24077400}, + {8360190, 23872800}, {8366700, 23665700}, + {8347200, 23459400}, {8302000, 23257200}, + {8231809, 23062200}, {8137740, 22877600}, + {8021270, 22706200}, {7884240, 22550800}, + {7728810, 22413800}, {7557430, 22297300}, + {7372810, 22203200}, {7177850, 22133000}, + {6975630, 22087800}, {6769340, 22068300}, + }, + { + {1094940, 10417600}, {365572, 10468600}, + {0, 10468600}, {0, 10475000}, + {-1431080, 10475000}, {-6000000, 15108169}, + {-6000000, 19962102}, {-5802370, 20350000}, + {-5420410, 20938200}, {-4979070, 21483200}, + {-4483170, 21979100}, {-3938160, 22420400}, + {-3350000, 22802400}, {-2725130, 23120800}, + {-2070410, 23372100}, {-1393010, 23553600}, + {-700340, 23663300}, {0, 23700000}, + {700340, 23663300}, {1393010, 23553600}, + {2070410, 23372100}, {2725130, 23120800}, + {3350000, 22802400}, {3938160, 22420400}, + {4483170, 21979100}, {4979070, 21483200}, + {5420410, 20938200}, {5802370, 20350000}, + {6120750, 19725100}, {6372080, 19070400}, + {6553590, 18393000}, {6663300, 17700300}, + {6700000, 17000000}, {6663300, 16299700}, + {6553590, 15607000}, {6372080, 14929600}, + {6120750, 14274900}, {5802370, 13650000}, + {5420410, 13061800}, {4979070, 12516800}, + {4483170, 12020900}, {3938160, 11579600}, + {3350000, 11197600}, {2725130, 10879200}, + {2070410, 10627900}, {1393010, 10446400}, + {1156540, 10409000}, + }, + { + {-1455380, -6847030}, {-2847160, -6394820}, + {-4114500, -5663120}, {-5202010, -4683910}, + {-6062180, -3500000}, {-6657400, -2163120}, + {-6961650, -731699}, {-6961650, 731699}, + {-6657400, 2163120}, {-6062180, 3500000}, + {-5202010, 4683910}, {-4114500, 5663120}, + {-2847160, 6394820}, {-1455380, 6847030}, + {0, 7000000}, {1455380, 6847030}, + {2847160, 6394820}, {4114500, 5663120}, + {5018810, 4848870}, {5421400, 5186681}, + {5472037, 5130444}, {6083180, 4451690}, + {6090937, 4443078}, {6007070, 4372710}, + {5647390, 4070900}, {6062180, 3500000}, + {6657400, 2163120}, {6961650, 731699}, + {6961650, -731699}, {6657400, -2163120}, + {6062180, -3500000}, {5202010, -4683910}, + {4114500, -5663120}, {2847160, -6394820}, + {1455380, -6847030}, {0, -7000000}, + }, + }}, + }, + ExPolygons{ + // "Einsy-hinges.stl": + MyPoly{ + { + {865247, 3337040}, {1400000, 3575130}, {1873570, 3919190}, + {2265250, 4354200}, {2557930, 4861140}, {2738810, 5417850}, + {2762880, 5646830}, {2785290, 5860020}, {2786450, 5871067}, + {2796080, 5962680}, {2800000, 6000000}, {2738810, 6582150}, + {2728530, 6613790}, {2344020, 7279790}, {2195738, 7536616}, + {1639530, 8500000}, {1552105, 8651421}, {935040, 9720210}, + {621454, 10263400}, {-3267950, 17000000}, {-5000000, 17000000}, + {-5000000, 6000000}, {-2800000, 6000000}, {-2738810, 5417850}, + {-2557930, 4861140}, {-2265250, 4354200}, {-1873570, 3919190}, + {-1400000, 3575130}, {-865247, 3337040}, {-292679, 3215340}, + {292679, 3215340}, + }, + {}}, + MyPoly{{ + {412054, -4263360}, {725639, -3720210}, + {1315606, -2698356}, {1430130, -2500000}, + {2000000, -1512950}, {2000000, -1309600}, + {2192510, -976168}, {2347550, -498987}, + {2400000, 0}, {2382076, 170521}, + {2362880, 353169}, {2349180, 483565}, + {2347550, 498987}, {2192510, 976168}, + {1941640, 1410680}, {1605910, 1783550}, + {1200000, 2078460}, {741640, 2282540}, + {250868, 2386850}, {-250868, 2386850}, + {-741640, 2282540}, {-1200000, 2078460}, + {-1605910, 1783550}, {-1941640, 1410680}, + {-2192510, 976168}, {-2347550, 498987}, + {-2400000, 0}, {-5000000, 0}, + {-5000000, -11000000}, {-3477350, -11000000}, + }, + {}}, + }, + ExPolygons{ + // "LCD-cover-ORIGINAL-MK3.stl": + MyPoly{{ + {78000000, -11928900}, + {78000000, 51000000}, + {73000000, 56000000}, + {-72000000, 56000000}, + {-77000000, 51000000}, + {-77000000, -11928900}, + {-74928904, -14000000}, + {75928904, -14000000}, + }, + { + { + {44000000, 26000000}, {44000000, 31980000}, + {43992900, 31987100}, {44000000, 31994200}, + {44000000, 32000000}, {44005800, 32000000}, + {56000000, 43994200}, {56000000, 44000000}, + {56005800, 44000000}, {56013700, 44007900}, + {56021600, 44000000}, {69500000, 44000000}, + {69500000, 36020800}, {69510400, 36010400}, + {63500000, 30000000}, {50900000, 30000000}, + {49000000, 28100000}, {49000000, 26000000}, + {48000000, 26000000}, {48000000, 28500000}, + {47992900, 28507100}, {50503100, 31017300}, + {50520500, 31000000}, {63085800, 31000000}, + {68500000, 36414200}, {68500000, 43000000}, + {56420000, 43000000}, {45000000, 31580000}, + {45000000, 26000000}, + }, + { + {-54500000, 8000000}, + {-54500000, 38500000}, + {30500000, 38500000}, + {30500000, 8000000}, + }, + { + {61872800, 15032900}, {60645900, 15293700}, + {59500000, 15803800}, {58485200, 16541100}, + {57645900, 17473300}, {57018700, 18559600}, + {56631100, 19752500}, {56500000, 21000000}, + {56631100, 22247500}, {57018700, 23440400}, + {57645900, 24526700}, {58485200, 25458900}, + {59500000, 26196200}, {60645900, 26706300}, + {61872800, 26967100}, {63127200, 26967100}, + {64354104, 26706300}, {65500000, 26196200}, + {66514800, 25458900}, {67354104, 24526700}, + {67981304, 23440400}, {68368896, 22247500}, + {68500000, 21000000}, {68368896, 19752500}, + {67981304, 18559600}, {67354104, 17473300}, + {66514800, 16541100}, {65500000, 15803800}, + {64354104, 15293700}, {63127200, 15032900}, + }, + { + {57000000, 1500000}, + {57000000, 5500000}, + {58300000, 5500000}, + {58300000, 1500000}, + }, + { + {55000000, 1500000}, + {55000000, 5500000}, + {56300000, 5500000}, + {56300000, 1500000}, + }, + { + {59000000, 1500000}, + {59000000, 5500000}, + {60300000, 5500000}, + {60300000, 1500000}, + }, + { + {61000000, 1500000}, + {61000000, 5500000}, + {62300000, 5500000}, + {62300000, 1500000}, + }, + { + {63000000, 1500000}, + {63000000, 5500000}, + {64300004, 5500000}, + {64300004, 1500000}, + }, + { + {65000000, 1500000}, + {65000000, 5500000}, + {66300004, 5500000}, + {66300004, 1500000}, + }, + { + {67000000, 1500000}, + {67000000, 5500000}, + {68300000, 5500000}, + {68300000, 1500000}, + }, + }}, + }, + ExPolygons{ + // "x-end-motor.stl": + MyPoly{{ + {2365630, -29401100}, {3901250, -29006800}, + {5375340, -28423200}, {6764660, -27659400}, + {8047300, -26727500}, {9203020, -25642200}, + {10213600, -24420600}, {11063100, -23082000}, + {11738200, -21647500}, {12228100, -20139600}, + {12525200, -18582300}, {12624700, -17000000}, + {12525200, -15417700}, {12228100, -13860400}, + {11738200, -12352500}, {11063100, -10918000}, + {10213600, -9579380}, {9203020, -8357790}, + {8047300, -7272490}, {7739860, -7049120}, + {8024310, -6733200}, {8474450, -6157050}, + {8883300, -5550900}, {9248880, -4917710}, + {9569390, -4260570}, {9843280, -3582660}, + {10069200, -2887300}, {10246100, -2177870}, + {10373100, -1457840}, {10449500, -730699}, + {10475000, 0}, {10449500, 730699}, + {10373100, 1457840}, {10246100, 2177870}, + {10069200, 2887300}, {9843280, 3582660}, + {9569390, 4260570}, {9248880, 4917710}, + {8883300, 5550900}, {8474450, 6157050}, + {8024310, 6733200}, {7535080, 7276550}, + {7009140, 7784440}, {6449050, 8254410}, + {5857540, 8684170}, {5237500, 9071620}, + {4591940, 9414870}, {3924000, 9712250}, + {3236950, 9962320}, {2534130, 10163800}, + {1818960, 10315900}, {1094940, 10417600}, + {365572, 10468600}, {0, 10468600}, + {0, 10475000}, {-6500000, 10475000}, + {-6500000, 53000000}, {-23500000, 53000000}, + {-23500000, -28500000}, {-5181320, -28500000}, + {-3901250, -29006800}, {-2365630, -29401100}, + {-792712, -29599800}, {792712, -29599800}, + }, + { + { + {-1455380, -6847030}, {-2847160, -6394820}, + {-4114500, -5663120}, {-5202010, -4683910}, + {-6062180, -3500000}, {-6657400, -2163120}, + {-6961650, -731699}, {-6961650, 731699}, + {-6657400, 2163120}, {-6062180, 3500000}, + {-5202010, 4683910}, {-4114500, 5663120}, + {-2847160, 6394820}, {-1455380, 6847030}, + {0, 7000000}, {1455380, 6847030}, + {2847160, 6394820}, {4114500, 5663120}, + {5202010, 4683910}, {6062180, 3500000}, + {6657400, 2163120}, {6961650, 731699}, + {6961650, -731699}, {6657400, -2163120}, + {6062180, -3500000}, {5641320, -4079259}, + {6084032, -4450744}, {6083180, -4451690}, + {5472037, -5130444}, {5414502, -5194343}, + {5328022, -5121778}, {5011080, -4855830}, + {4114500, -5663120}, {2847160, -6394820}, + {1455380, -6847030}, {0, -7000000}, + }, + { + {-700340, -23663300}, {-1393010, -23553600}, + {-2070410, -23372100}, {-2725130, -23120800}, + {-3350000, -22802400}, {-3938160, -22420400}, + {-4483170, -21979100}, {-4979070, -21483200}, + {-5420410, -20938200}, {-5802370, -20350000}, + {-6120750, -19725100}, {-6372080, -19070400}, + {-6500000, -18593000}, {-6500000, -15515603}, + {-1406282, -10475000}, {0, -10475000}, + {0, -10468600}, {365572, -10468600}, + {1094940, -10417600}, {1156540, -10409000}, + {1393010, -10446400}, {2070410, -10627900}, + {2725130, -10879200}, {3350000, -11197600}, + {3938160, -11579600}, {4483170, -12020900}, + {4979070, -12516800}, {5420410, -13061800}, + {5802370, -13650000}, {6120750, -14274900}, + {6372080, -14929600}, {6553590, -15607000}, + {6663300, -16299700}, {6700000, -17000000}, + {6663300, -17700300}, {6553590, -18393000}, + {6372080, -19070400}, {6120750, -19725100}, + {5802370, -20350000}, {5420410, -20938200}, + {4979070, -21483200}, {4483170, -21979100}, + {3938160, -22420400}, {3350000, -22802400}, + {2725130, -23120800}, {2070410, -23372100}, + {1393010, -23553600}, {700340, -23663300}, + {0, -23700000}, + }, + { + {6459400, -25347200}, {6257180, -25302000}, + {6062220, -25231800}, {5877600, -25137700}, + {5706220, -25021300}, {5550790, -24884200}, + {5413760, -24728800}, {5297290, -24557400}, + {5203220, -24372800}, {5133030, -24177800}, + {5087830, -23975600}, {5068330, -23769300}, + {5074840, -23562200}, {5107250, -23357600}, + {5165060, -23158600}, {5247350, -22968400}, + {5352830, -22790100}, {5479830, -22626400}, + {5626350, -22479800}, {5790080, -22352800}, + {5968430, -22247400}, {6158600, -22165100}, + {6357580, -22107300}, {6562230, -22074800}, + {6769340, -22068300}, {6975630, -22087800}, + {7177850, -22133000}, {7372810, -22203200}, + {7557430, -22297300}, {7728810, -22413800}, + {7884240, -22550800}, {8021270, -22706200}, + {8137740, -22877600}, {8231809, -23062200}, + {8302000, -23257200}, {8347200, -23459400}, + {8366700, -23665700}, {8360190, -23872800}, + {8327780, -24077400}, {8269970, -24276400}, + {8187670, -24466600}, {8082200, -24645000}, + {7955200, -24808700}, {7808680, -24955200}, + {7644950, -25082200}, {7466600, -25187700}, + {7276430, -25270000}, {7077450, -25327800}, + {6872790, -25360200}, {6665690, -25366700}, + }, + }}, + }, + ExPolygons{ + // "y-belt-idler.stl": + MyPoly{{ + {12500000, 40000000}, + {-12500000, 40000000}, + {-12500000, 5000000}, + {12500000, 5000000}, + }, + { + { + {-103604, 34353300}, {-309178, 34379200}, + {-509877, 34430800}, {-702536, 34507000}, + {-884113, 34606900}, {-1051750, 34728700}, + {-1202800, 34870500}, {-1334880, 35030200}, + {-1445910, 35205100}, {-1534130, 35392600}, + {-1598160, 35589700}, {-1636990, 35793200}, + {-1650000, 36000000}, {-1636990, 36206800}, + {-1598160, 36410300}, {-1534130, 36607400}, + {-1445910, 36794900}, {-1334880, 36969800}, + {-1202800, 37129500}, {-1051750, 37271300}, + {-884113, 37393100}, {-702536, 37493000}, + {-509877, 37569200}, {-309178, 37620800}, + {-103604, 37646700}, {103604, 37646700}, + {309178, 37620800}, {509877, 37569200}, + {702536, 37493000}, {884113, 37393100}, + {1051750, 37271300}, {1202800, 37129500}, + {1334880, 36969800}, {1445910, 36794900}, + {1534130, 36607400}, {1598160, 36410300}, + {1636990, 36206800}, {1650000, 36000000}, + {1636990, 35793200}, {1598160, 35589700}, + {1534130, 35392600}, {1445910, 35205100}, + {1334880, 35030200}, {1202800, 34870500}, + {1051750, 34728700}, {884113, 34606900}, + {702536, 34507000}, {509877, 34430800}, + {309178, 34379200}, {103604, 34353300}, + }, + { + {-103604, 8353260}, {-309178, 8379229}, + {-509877, 8430760}, {-702536, 8507040}, + {-884113, 8606860}, {-1051750, 8728650}, + {-1202800, 8870500}, {-1334880, 9030150}, + {-1445910, 9205110}, {-1534130, 9392590}, + {-1598160, 9589660}, {-1636990, 9793200}, + {-1650000, 10000000}, {-1636990, 10206800}, + {-1598160, 10410300}, {-1534130, 10607400}, + {-1445910, 10794900}, {-1334880, 10969800}, + {-1202800, 11129500}, {-1051750, 11271300}, + {-884113, 11393100}, {-702536, 11493000}, + {-509877, 11569200}, {-309178, 11620800}, + {-103604, 11646700}, {103604, 11646700}, + {309178, 11620800}, {509877, 11569200}, + {702536, 11493000}, {884113, 11393100}, + {1051750, 11271300}, {1202800, 11129500}, + {1334880, 10969800}, {1445910, 10794900}, + {1534130, 10607400}, {1598160, 10410300}, + {1636990, 10206800}, {1650000, 10000000}, + {1636990, 9793200}, {1598160, 9589660}, + {1534130, 9392590}, {1445910, 9205110}, + {1334880, 9030150}, {1202800, 8870500}, + {1051750, 8728650}, {884113, 8606860}, + {702536, 8507040}, {509877, 8430760}, + {309178, 8379229}, {103604, 8353260}, + }, + }}, + }, + ExPolygons{ + // "z-screw-cover.stl": + MyPoly{{ + {836227, -7956170}, {927804, -7941670}, + {964293, -7941670}, {1029899, -7925500}, + {1663290, -7825180}, {2472140, -7608450}, + {2751896, -7501064}, {2836840, -7480130}, + {2919650, -7436670}, {3253890, -7308360}, + {4000000, -6928200}, {4470019, -6622970}, + {4544520, -6583870}, {4583731, -6549125}, + {4702280, -6472140}, {5353040, -5945160}, + {5945160, -5353040}, {5973910, -5317540}, + {5988090, -5304980}, {6011204, -5271483}, + {6472140, -4702280}, {6928200, -4000000}, + {7039150, -3782250}, {7083650, -3717780}, + {7117560, -3628360}, {7308360, -3253890}, + {7608450, -2472140}, {7734580, -2001420}, + {7767530, -1914530}, {7775550, -1848520}, + {7825180, -1663290}, {7956170, -836227}, + {8000000, 0}, {7956170, 836227}, + {7825180, 1663290}, {7775550, 1848520}, + {7767530, 1914530}, {7734580, 2001420}, + {7608450, 2472140}, {7308360, 3253890}, + {7117560, 3628360}, {7083650, 3717780}, + {7039150, 3782250}, {6928200, 4000000}, + {6472140, 4702280}, {6011204, 5271483}, + {5988090, 5304980}, {5973910, 5317540}, + {5945160, 5353040}, {5353040, 5945160}, + {4702280, 6472140}, {4583731, 6549125}, + {4544520, 6583870}, {4470019, 6622970}, + {4000000, 6928200}, {3253890, 7308360}, + {2919650, 7436670}, {2836840, 7480130}, + {2751896, 7501064}, {2472140, 7608450}, + {1663290, 7825180}, {1029899, 7925500}, + {964293, 7941670}, {927804, 7941670}, + {836227, 7956170}, {0, 8000000}, + {-836227, 7956170}, {-927804, 7941670}, + {-964293, 7941670}, {-1029899, 7925500}, + {-1663290, 7825180}, {-2472140, 7608450}, + {-2751896, 7501064}, {-2836840, 7480130}, + {-2919650, 7436670}, {-3253890, 7308360}, + {-4000000, 6928200}, {-4470019, 6622970}, + {-4544520, 6583870}, {-4583731, 6549125}, + {-4702280, 6472140}, {-5353040, 5945160}, + {-5945160, 5353040}, {-5973910, 5317540}, + {-5988090, 5304980}, {-6011204, 5271483}, + {-6472140, 4702280}, {-6928200, 4000000}, + {-7039150, 3782250}, {-7083650, 3717780}, + {-7117560, 3628360}, {-7308360, 3253890}, + {-7608450, 2472140}, {-7734580, 2001420}, + {-7767530, 1914530}, {-7775550, 1848520}, + {-7825180, 1663290}, {-7956170, 836227}, + {-8000000, 0}, {-7956170, -836227}, + {-7825180, -1663290}, {-7775550, -1848520}, + {-7767530, -1914530}, {-7734580, -2001420}, + {-7608450, -2472140}, {-7308360, -3253890}, + {-7117560, -3628360}, {-7083650, -3717780}, + {-7039150, -3782250}, {-6928200, -4000000}, + {-6472140, -4702280}, {-6011204, -5271483}, + {-5988090, -5304980}, {-5973910, -5317540}, + {-5945160, -5353040}, {-5353040, -5945160}, + {-4702280, -6472140}, {-4583731, -6549125}, + {-4544520, -6583870}, {-4470019, -6622970}, + {-4000000, -6928200}, {-3253890, -7308360}, + {-2919650, -7436670}, {-2836840, -7480130}, + {-2751896, -7501064}, {-2472140, -7608450}, + {-1663290, -7825180}, {-1029899, -7925500}, + {-964293, -7941670}, {-927804, -7941670}, + {-836227, -7956170}, {0, -8000000}, + }, + { + { + {400000, -3200000}, {-400000, -3200000}, + {-440000, -3400000}, {-799999, -3400000}, + {-875001, -3600000}, {-1300000, -3600000}, + {-1416318, -3948965}, {-1708290, -3836890}, + {-2100000, -3637310}, {-2468700, -3397870}, + {-2810350, -3121210}, {-3121210, -2810350}, + {-3397870, -2468700}, {-3637310, -2100000}, + {-3836890, -1708290}, {-3994440, -1297870}, + {-4108220, -873229}, {-4152979, -590596}, + {-3200000, -400000}, {-3200000, 400000}, + {-3400000, 440000}, {-3400000, 799999}, + {-3600000, 874998}, {-3600000, 1300000}, + {-3948965, 1416318}, {-3836890, 1708290}, + {-3637310, 2100000}, {-3397870, 2468700}, + {-3121210, 2810350}, {-2810350, 3121210}, + {-2468700, 3397870}, {-2100000, 3637310}, + {-1708290, 3836890}, {-1297870, 3994440}, + {-873229, 4108220}, {-590596, 4152979}, + {-400000, 3200000}, {400000, 3200000}, + {440000, 3400000}, {799999, 3400000}, + {874998, 3600000}, {1300000, 3600000}, + {1416318, 3948965}, {1708290, 3836890}, + {2100000, 3637310}, {2468700, 3397870}, + {2810350, 3121210}, {3121210, 2810350}, + {3397870, 2468700}, {3637310, 2100000}, + {3836890, 1708290}, {3994440, 1297870}, + {4108220, 873229}, {4152979, 590596}, + {3200000, 400000}, {3200000, -400000}, + {3400000, -440000}, {3400000, -799999}, + {3600000, -874998}, {3600000, -1300000}, + {3948965, -1416318}, {3836890, -1708290}, + {3637310, -2100000}, {3397870, -2468700}, + {3121210, -2810350}, {2810350, -3121210}, + {2468700, -3397870}, {2100000, -3637310}, + {1708290, -3836890}, {1297870, -3994440}, + {873229, -4108220}, {590596, -4152979}, + }, + }}, + }, + ExPolygons{ + // "cable-holder.stl": + MyPoly{{ + {-2043150, -34799100}, {-1990180, -34549300}, + {-1988820, -34542900}, {-1986150, -34536800}, + {-1882530, -34303500}, {-1732900, -34097100}, + {-1728930, -34091600}, {-1723900, -34087100}, + {-1534730, -33916300}, {-1308420, -33785400}, + {-1059890, -33704400}, {-806907, -33677700}, + {-799999, -33677000}, {-793091, -33677700}, + {-540110, -33704400}, {-291578, -33785400}, + {-65267, -33916300}, {123903, -34087100}, + {128930, -34091600}, {132903, -34097100}, + {282532, -34303500}, {386150, -34536800}, + {388820, -34542900}, {390183, -34549300}, + {443151, -34799100}, {443151, -34908100}, + {556848, -34908100}, {556848, -34799100}, + {609816, -34549300}, {611179, -34542900}, + {613849, -34536800}, {717467, -34303500}, + {867096, -34097100}, {871069, -34091600}, + {876096, -34087100}, {1065270, -33916300}, + {1291580, -33785400}, {1540110, -33704400}, + {1793090, -33677700}, {1800000, -33677000}, + {1806910, -33677700}, {2059890, -33704400}, + {2308420, -33785400}, {2534730, -33916300}, + {2723900, -34087100}, {2728930, -34091600}, + {2732900, -34097100}, {2882530, -34303500}, + {2986150, -34536800}, {2988820, -34542900}, + {2990180, -34549300}, {3043150, -34799100}, + {3043150, -34908100}, {4000000, -34908100}, + {4000000, -29539900}, {4215720, -29345700}, + {4830130, -28500000}, {5255280, -27545100}, + {5472610, -26522600}, {5472610, -26000000}, + {5500000, -26000000}, {5500000, -17000000}, + {4805710, -17000000}, {4215720, -17815100}, + {3438930, -18517200}, {2533680, -19041900}, + {1539560, -19366100}, {499999, -19475800}, + {-539558, -19366100}, {-1533680, -19041900}, + {-2438930, -18517200}, {-3215720, -17815100}, + {-3805710, -17000000}, {-4500000, -17000000}, + {-4500000, -26000000}, {-4472610, -26000000}, + {-4472610, -26522600}, {-4255280, -27545100}, + {-3830130, -28500000}, {-3215720, -29345700}, + {-3000000, -29539900}, {-3000000, -34908100}, + {-2043150, -34908100}, + }, + { + { + {136154, -28711800}, {-211788, -28598700}, + {-382749, -28500000}, {-528624, -28415800}, + {-800503, -28171000}, {-1015540, -27875000}, + {-1164350, -27540800}, {-1240410, -27182900}, + {-1240410, -26817100}, {-1164350, -26459200}, + {-1015540, -26125000}, {-800503, -25829000}, + {-528624, -25584200}, {-382751, -25500000}, + {-211788, -25401300}, {136154, -25288200}, + {500000, -25250000}, {863845, -25288200}, + {1211790, -25401300}, {1382750, -25500000}, + {1528620, -25584200}, {1800500, -25829000}, + {2015539, -26125000}, {2164350, -26459200}, + {2240410, -26817100}, {2240410, -27182900}, + {2164350, -27540800}, {2015539, -27875000}, + {1800500, -28171000}, {1528620, -28415800}, + {1382750, -28500000}, {1211790, -28598700}, + {863845, -28711800}, {499999, -28750000}, + }, + }}, + }, + ExPolygons{ + // "Einsy-doors.stl": + MyPoly{{ + {105500000, 91975304}, + {21500000, 91975304}, + {21500000, 87500000}, + {0, 87500000}, + {0, 0}, + {105500000, 0}, + }, + { + { + {46000000, 60500000}, + {46000000, 79500000}, + {49650000, 79500000}, + {49650000, 60500000}, + }, + { + {58000000, 60500000}, + {58000000, 79500000}, + {61650000, 79500000}, + {61650000, 60500000}, + }, + { + {70000000, 60500000}, + {70000000, 79500000}, + {73650000, 79500000}, + {73650000, 60500000}, + }, + { + {64000000, 60500000}, + {64000000, 79500000}, + {67650000, 79500000}, + {67650000, 60500000}, + }, + { + {94000000, 60500000}, + {94000000, 79500000}, + {97650000, 79500000}, + {97650000, 60500000}, + }, + { + {52000000, 60500000}, + {52000000, 79500000}, + {55650000, 79500000}, + {55650000, 60500000}, + }, + { + {88000000, 60500000}, + {88000000, 79500000}, + {91650000, 79500000}, + {91650000, 60500000}, + }, + { + {82000000, 60500000}, + {82000000, 79500000}, + {85650000, 79500000}, + {85650000, 60500000}, + }, + { + {40000000, 60500000}, + {40000000, 79500000}, + {43650000, 79500000}, + {43650000, 60500000}, + }, + { + {76000000, 60500000}, + {76000000, 79500000}, + {79650000, 79500000}, + {79650000, 60500000}, + }, + { + {52000000, 35500000}, + {52000000, 54500000}, + {55650000, 54500000}, + {55650000, 35500000}, + }, + { + {40000000, 35500000}, + {40000000, 54500000}, + {43650000, 54500000}, + {43650000, 35500000}, + }, + { + {58000000, 35500000}, + {58000000, 54500000}, + {61650000, 54500000}, + {61650000, 35500000}, + }, + { + {76000000, 35500000}, + {76000000, 54500000}, + {79650000, 54500000}, + {79650000, 35500000}, + }, + { + {94000000, 35500000}, + {94000000, 54500000}, + {97650000, 54500000}, + {97650000, 35500000}, + }, + { + {82000000, 35500000}, + {82000000, 54500000}, + {85650000, 54500000}, + {85650000, 35500000}, + }, + { + {64000000, 35500000}, + {64000000, 54500000}, + {67650000, 54500000}, + {67650000, 35500000}, + }, + { + {70000000, 35500000}, + {70000000, 54500000}, + {73650000, 54500000}, + {73650000, 35500000}, + }, + { + {46000000, 35500000}, + {46000000, 54500000}, + {49650000, 54500000}, + {49650000, 35500000}, + }, + { + {88000000, 35500000}, + {88000000, 54500000}, + {91650000, 54500000}, + {91650000, 35500000}, + }, + { + {40000000, 10500000}, + {40000000, 29500000}, + {43650000, 29500000}, + {43650000, 10500000}, + }, + { + {82000000, 10500000}, + {82000000, 29500000}, + {85650000, 29500000}, + {85650000, 10500000}, + }, + { + {94000000, 10500000}, + {94000000, 29500000}, + {97650000, 29500000}, + {97650000, 10500000}, + }, + { + {88000000, 10500000}, + {88000000, 29500000}, + {91650000, 29500000}, + {91650000, 10500000}, + }, + { + {76000000, 10500000}, + {76000000, 29500000}, + {79650000, 29500000}, + {79650000, 10500000}, + }, + { + {64000000, 10500000}, + {64000000, 29500000}, + {67650000, 29500000}, + {67650000, 10500000}, + }, + { + {52000000, 10500000}, + {52000000, 29500000}, + {55650000, 29500000}, + {55650000, 10500000}, + }, + { + {70000000, 10500000}, + {70000000, 29500000}, + {73650000, 29500000}, + {73650000, 10500000}, + }, + { + {46000000, 10500000}, + {46000000, 29500000}, + {49650000, 29500000}, + {49650000, 10500000}, + }, + { + {58000000, 10500000}, + {58000000, 29500000}, + {61650000, 29500000}, + {61650000, 10500000}, + }, + }}, + }, + ExPolygons{ + // "y-motor-holder.stl": + MyPoly{{ + {47000000, 0}, {47000000, 15000000}, + {42000000, 20000000}, {37468500, 20000000}, + {37500000, 19500000}, {37409300, 18058700}, + {37138700, 16640100}, {36692400, 15266600}, + {36077500, 13959800}, {35303700, 12740500}, + {34383100, 11627700}, {33330398, 10639100}, + {32161998, 9790230}, {30896500, 9094490}, + {29553700, 8562850}, {28154900, 8203700}, + {26722100, 8022690}, {25277900, 8022690}, + {23845100, 8203700}, {22446300, 8562850}, + {21103500, 9094490}, {19838000, 9790230}, + {18669600, 10639100}, {17616900, 11627700}, + {16696301, 12740500}, {15922500, 13959800}, + {15307600, 15266600}, {14861300, 16640100}, + {14590700, 18058700}, {14500000, 19500000}, + {14590700, 20941300}, {14861300, 22359900}, + {15000000, 22786800}, {15000000, 38000000}, + {12500000, 40500000}, {9642140, 40500000}, + {71067, 30928900}, {0, 31000000}, + {0, -1500000}, {45500000, -1500000}, + }, + { + { + {10396400, 33353298}, {10190800, 33379200}, + {9990120, 33430802}, {9797460, 33507000}, + {9615890, 33606900}, {9448250, 33728700}, + {9297200, 33870500}, {9165120, 34030200}, + {9054090, 34205100}, {8965870, 34392600}, + {8901840, 34589700}, {8863010, 34793200}, + {8850000, 35000000}, {8863010, 35206800}, + {8901840, 35410300}, {8965870, 35607400}, + {9054090, 35794900}, {9165120, 35969800}, + {9297200, 36129500}, {9448250, 36271300}, + {9615890, 36393100}, {9797460, 36493000}, + {9990120, 36569200}, {10190800, 36620800}, + {10396400, 36646700}, {10603600, 36646700}, + {10809200, 36620800}, {11009900, 36569200}, + {11202500, 36493000}, {11384100, 36393100}, + {11551700, 36271300}, {11702800, 36129500}, + {11834900, 35969800}, {11945900, 35794900}, + {12034100, 35607400}, {12098200, 35410300}, + {12137000, 35206800}, {12150000, 35000000}, + {12137000, 34793200}, {12098200, 34589700}, + {12034100, 34392600}, {11945900, 34205100}, + {11834900, 34030200}, {11702800, 33870500}, + {11551700, 33728700}, {11384100, 33606900}, + {11202500, 33507000}, {11009900, 33430802}, + {10809200, 33379200}, {10603600, 33353298}, + }, + { + {41396400, 2353260}, {41190800, 2379230}, + {40990100, 2430760}, {40797500, 2507040}, + {40615900, 2606860}, {40448200, 2728650}, + {40297200, 2870500}, {40165100, 3030150}, + {40054100, 3205110}, {39965900, 3392590}, + {39901800, 3589660}, {39863000, 3793200}, + {39850000, 4000000}, {39863000, 4206800}, + {39901800, 4410340}, {39965900, 4607400}, + {40054100, 4794890}, {40165100, 4969840}, + {40297200, 5129500}, {40448200, 5271350}, + {40615900, 5393140}, {40797500, 5492960}, + {40990100, 5569240}, {41190800, 5620770}, + {41396400, 5646740}, {41603600, 5646740}, + {41809200, 5620770}, {42009900, 5569240}, + {42202500, 5492960}, {42384100, 5393140}, + {42551800, 5271350}, {42702800, 5129500}, + {42834900, 4969840}, {42945900, 4794890}, + {43034100, 4607400}, {43098200, 4410340}, + {43137000, 4206800}, {43150000, 4000000}, + {43137000, 3793200}, {43098200, 3589660}, + {43034100, 3392590}, {42945900, 3205110}, + {42834900, 3030150}, {42702800, 2870500}, + {42551800, 2728650}, {42384100, 2606860}, + {42202500, 2507040}, {42009900, 2430760}, + {41809200, 2379230}, {41603600, 2353260}, + }, + }}, + }, + ExPolygons{ + // "heatbed-cable-cover.stl": + MyPoly{{ + {15000000, 48000000}, + {11000000, 52000000}, + {-11000000, 52000000}, + {-15000000, 48000000}, + {-15000000, 35500000}, + {15000000, 35500000}, + }, + { + { + {-10100500, 43403200}, {-10299800, 43428300}, + {-10494400, 43478300}, {-10681200, 43552300}, + {-10857300, 43649100}, {-11019900, 43767200}, + {-11166300, 43904700}, {-11294400, 44059500}, + {-11402100, 44229200}, {-11487600, 44411000}, + {-11549700, 44602100}, {-11587400, 44799500}, + {-11600000, 45000000}, {-11587400, 45200500}, + {-11549700, 45397900}, {-11487600, 45589000}, + {-11402100, 45770800}, {-11294400, 45940500}, + {-11166300, 46095300}, {-11019900, 46232800}, + {-10857300, 46350900}, {-10681200, 46447700}, + {-10494400, 46521700}, {-10299800, 46571700}, + {-10100500, 46596800}, {-9899530, 46596800}, + {-9700190, 46571700}, {-9505570, 46521700}, + {-9318750, 46447700}, {-9142680, 46350900}, + {-8980120, 46232800}, {-8833650, 46095300}, + {-8705570, 45940500}, {-8597910, 45770800}, + {-8512360, 45589000}, {-8450270, 45397900}, + {-8412620, 45200500}, {-8400000, 45000000}, + {-8412620, 44799500}, {-8450270, 44602100}, + {-8512360, 44411000}, {-8597910, 44229200}, + {-8705570, 44059500}, {-8833650, 43904700}, + {-8980120, 43767200}, {-9142680, 43649100}, + {-9318750, 43552300}, {-9505570, 43478300}, + {-9700190, 43428300}, {-9899530, 43403200}, + }, + { + {9899530, 43403200}, {9700190, 43428300}, + {9505570, 43478300}, {9318750, 43552300}, + {9142680, 43649100}, {8980120, 43767200}, + {8833650, 43904700}, {8705570, 44059500}, + {8597910, 44229200}, {8512360, 44411000}, + {8450270, 44602100}, {8412620, 44799500}, + {8400000, 45000000}, {8412620, 45200500}, + {8450270, 45397900}, {8512360, 45589000}, + {8597910, 45770800}, {8705570, 45940500}, + {8833650, 46095300}, {8980120, 46232800}, + {9142680, 46350900}, {9318750, 46447700}, + {9505570, 46521700}, {9700190, 46571700}, + {9899530, 46596800}, {10100500, 46596800}, + {10299800, 46571700}, {10494400, 46521700}, + {10681200, 46447700}, {10857300, 46350900}, + {11019900, 46232800}, {11166300, 46095300}, + {11294400, 45940500}, {11402100, 45770800}, + {11487600, 45589000}, {11549700, 45397900}, + {11587400, 45200500}, {11600000, 45000000}, + {11587400, 44799500}, {11549700, 44602100}, + {11487600, 44411000}, {11402100, 44229200}, + {11294400, 44059500}, {11166300, 43904700}, + {11019900, 43767200}, {10857300, 43649100}, + {10681200, 43552300}, {10494400, 43478300}, + {10299800, 43428300}, {10100500, 43403200}, + }, + }}, + MyPoly{{ + {18000000, 25000000}, + {16426001, 26574000}, + {11000000, 32000000}, + {-11000000, 32000000}, + {-18000000, 25000000}, + {-18000000, 0}, + {18000000, 0}, + }, + { + { + {-10100500, 23403200}, {-10299800, 23428300}, + {-10494400, 23478300}, {-10681200, 23552300}, + {-10857300, 23649100}, {-11019900, 23767200}, + {-11166300, 23904700}, {-11294400, 24059500}, + {-11402100, 24229200}, {-11487600, 24411000}, + {-11549700, 24602100}, {-11587400, 24799500}, + {-11600000, 25000000}, {-11587400, 25200500}, + {-11549700, 25397900}, {-11487600, 25589000}, + {-11402100, 25770800}, {-11294400, 25940500}, + {-11166300, 26095300}, {-11019900, 26232800}, + {-10857300, 26350900}, {-10681200, 26447700}, + {-10494400, 26521700}, {-10299800, 26571700}, + {-10100500, 26596800}, {-9899530, 26596800}, + {-9700190, 26571700}, {-9505570, 26521700}, + {-9318750, 26447700}, {-9142680, 26350900}, + {-8980120, 26232800}, {-8833650, 26095300}, + {-8705570, 25940500}, {-8597910, 25770800}, + {-8512360, 25589000}, {-8450270, 25397900}, + {-8412620, 25200500}, {-8400000, 25000000}, + {-8412620, 24799500}, {-8450270, 24602100}, + {-8512360, 24411000}, {-8597910, 24229200}, + {-8705570, 24059500}, {-8833650, 23904700}, + {-8980120, 23767200}, {-9142680, 23649100}, + {-9318750, 23552300}, {-9505570, 23478300}, + {-9700190, 23428300}, {-9899530, 23403200}, + }, + { + {9899530, 23403200}, {9700190, 23428300}, + {9505570, 23478300}, {9318750, 23552300}, + {9142680, 23649100}, {8980120, 23767200}, + {8833650, 23904700}, {8705570, 24059500}, + {8597910, 24229200}, {8512360, 24411000}, + {8450270, 24602100}, {8412620, 24799500}, + {8400000, 25000000}, {8412620, 25200500}, + {8450270, 25397900}, {8512360, 25589000}, + {8597910, 25770800}, {8705570, 25940500}, + {8833650, 26095300}, {8980120, 26232800}, + {9142680, 26350900}, {9318750, 26447700}, + {9505570, 26521700}, {9700190, 26571700}, + {9899530, 26596800}, {10100500, 26596800}, + {10299800, 26571700}, {10494400, 26521700}, + {10681200, 26447700}, {10857300, 26350900}, + {11019900, 26232800}, {11166300, 26095300}, + {11294400, 25940500}, {11402100, 25770800}, + {11487600, 25589000}, {11549700, 25397900}, + {11587400, 25200500}, {11600000, 25000000}, + {11587400, 24799500}, {11549700, 24602100}, + {11487600, 24411000}, {11402100, 24229200}, + {11294400, 24059500}, {11166300, 23904700}, + {11019900, 23767200}, {10857300, 23649100}, + {10681200, 23552300}, {10494400, 23478300}, + {10299800, 23428300}, {10100500, 23403200}, + }, + { + {-100465, 5903160}, {-299809, 5928340}, + {-494427, 5978310}, {-681247, 6052280}, + {-857323, 6149070}, {-1019880, 6267180}, + {-1166350, 6404720}, {-1294430, 6559540}, + {-1402090, 6729190}, {-1487640, 6911000}, + {-1549730, 7102100}, {-1587380, 7299470}, + {-1600000, 7500000}, {-1587380, 7700530}, + {-1549730, 7897900}, {-1487640, 8088999}, + {-1402090, 8270810}, {-1294430, 8440460}, + {-1166350, 8595270}, {-1019880, 8732820}, + {-857323, 8850920}, {-681247, 8947720}, + {-494427, 9021690}, {-299809, 9071660}, + {-100465, 9096840}, {100465, 9096840}, + {299809, 9071660}, {494427, 9021690}, + {681247, 8947720}, {857323, 8850920}, + {1019880, 8732820}, {1166350, 8595270}, + {1294430, 8440460}, {1402090, 8270810}, + {1487640, 8088999}, {1549730, 7897900}, + {1587380, 7700530}, {1600000, 7500000}, + {1587380, 7299470}, {1549730, 7102100}, + {1487640, 6911000}, {1402090, 6729190}, + {1294430, 6559540}, {1166350, 6404720}, + {1019880, 6267180}, {857323, 6149070}, + {681247, 6052280}, {494427, 5978310}, + {299809, 5928340}, {100465, 5903160}, + }, + }}, + }, + ExPolygons{ + // "y-rod-holder.stl": + MyPoly{{ + {-4130159, -11630200}, {-4125905, -11625938}, + {-4036359, -11536400}, {-3977214, -11477197}, + {-3893620, -11393600}, {-3968460, -11001300}, + {-4000000, -10500000}, {-3968460, -9998670}, + {-3874330, -9505240}, {-3719110, -9027500}, + {-3505230, -8572980}, {-3236070, -8148860}, + {-2915870, -7761810}, {-2549700, -7417950}, + {-2143310, -7122690}, {-1920140, -7000000}, + {-1703120, -6880690}, {-1236070, -6695770}, + {-749525, -6570850}, {-251162, -6507890}, + {251162, -6507890}, {749525, -6570850}, + {1236070, -6695770}, {1703120, -6880690}, + {1920140, -7000000}, {2143310, -7122690}, + {2549700, -7417950}, {2915870, -7761810}, + {3236070, -8148860}, {3505230, -8572980}, + {3719110, -9027500}, {3874330, -9505240}, + {3968460, -9998670}, {4000000, -10500000}, + {3968460, -11001300}, {3916390, -11274300}, + {4089891, -11447789}, {4113299, -11471200}, + {4642140, -12000000}, {8618800, -12000000}, + {10350900, -9000000}, {11376300, -7223940}, + {13000000, -4411540}, {13000000, 0}, + {4000000, 0}, {4000000, 1500000}, + {-4000000, 1500000}, {-4000000, 0}, + {-13000000, 0}, {-13000000, -4202820}, + {-8498290, -12000000}, {-4500000, -12000000}, + }, + {}}, + }, + ExPolygons{ + // "extruder-body.stl": + MyPoly{{ + {32000000, -41357900}, {32000000, -34500000}, + {27500000, -30000000}, {22600000, -30000000}, + {22600000, -29900000}, {22500000, -30000000}, + {17000000, -24500000}, {17000000, -12000000}, + {23000000, -12000000}, {23000000, -18000000}, + {23928900, -18000000}, {26000000, -15928900}, + {26000000, -10000000}, {23000000, -7000000}, + {17000000, -7000000}, {17000000, 44000000}, + {11000000, 50000000}, {-18000000, 50000000}, + {-25000000, 43000000}, {-25000000, 5750000}, + {-27250000, 5750000}, {-31500000, 1500000}, + {-31500000, -1500000}, {-24500000, -1500000}, + {-24500000, 2500000}, {-21309400, 2500000}, + {-20500000, 1098080}, {-20500000, -44000000}, + {-15500000, -44000000}, {-15500000, -38000000}, + {-14000000, -36500000}, {14000000, -36500000}, + {14000000, -38916000}, {14423151, -39823468}, + {14452100, -39885600}, {15259800, -41617700}, + {18642100, -45000000}, {28357900, -45000000}, + }, + { + { + {-19603600, 40853300}, {-19790500, 40876900}, + {-19809200, 40879200}, {-19991600, 40926100}, + {-20009900, 40930800}, {-20202500, 41007000}, + {-20384100, 41106900}, {-20491899, 41185194}, + {-20551700, 41228700}, {-20551800, 41228700}, + {-20702800, 41370500}, {-20834900, 41530200}, + {-20945900, 41705100}, {-21034100, 41892600}, + {-21098200, 42089700}, {-21137000, 42293200}, + {-21150000, 42500000}, {-21137000, 42706800}, + {-21098200, 42910300}, {-21034100, 43107400}, + {-20945900, 43294900}, {-20834900, 43469800}, + {-20702800, 43629500}, {-20551800, 43771300}, + {-20551700, 43771300}, {-20491899, 43814806}, + {-20384100, 43893100}, {-20202500, 43993000}, + {-20009900, 44069200}, {-19991600, 44073900}, + {-19809200, 44120800}, {-19790500, 44123100}, + {-19603600, 44146700}, {-19396400, 44146700}, + {-19209500, 44123100}, {-19190800, 44120800}, + {-19008400, 44073900}, {-18990100, 44069200}, + {-18797500, 43993000}, {-18615900, 43893100}, + {-18448200, 43771300}, {-18297200, 43629500}, + {-18165100, 43469800}, {-18054100, 43294900}, + {-17965900, 43107400}, {-17901800, 42910300}, + {-17863000, 42706800}, {-17850000, 42500000}, + {-17863000, 42293200}, {-17901800, 42089700}, + {-17965900, 41892600}, {-18054100, 41705100}, + {-18165100, 41530200}, {-18297200, 41370500}, + {-18448200, 41228700}, {-18615900, 41106900}, + {-18797500, 41007000}, {-18990100, 40930800}, + {-19008400, 40926100}, {-19190800, 40879200}, + {-19209500, 40876900}, {-19396400, 40853300}, + }, + { + {11396400, 40853300}, {11190800, 40879200}, + {10990100, 40930800}, {10797500, 41007000}, + {10615900, 41106900}, {10448200, 41228700}, + {10297200, 41370500}, {10165100, 41530200}, + {10054100, 41705100}, {9965870, 41892600}, + {9901840, 42089700}, {9863010, 42293200}, + {9850000, 42500000}, {9863010, 42706800}, + {9901840, 42910300}, {9965870, 43107400}, + {10054100, 43294900}, {10165100, 43469800}, + {10297200, 43629500}, {10448200, 43771300}, + {10615900, 43893100}, {10797500, 43993000}, + {10990100, 44069200}, {11190800, 44120800}, + {11396400, 44146700}, {11603600, 44146700}, + {11809200, 44120800}, {12009900, 44069200}, + {12202500, 43993000}, {12384100, 43893100}, + {12551700, 43771300}, {12702800, 43629500}, + {12834900, 43469800}, {12945900, 43294900}, + {13034100, 43107400}, {13098200, 42910300}, + {13137000, 42706800}, {13150000, 42500000}, + {13137000, 42293200}, {13098200, 42089700}, + {13034100, 41892600}, {12945900, 41705100}, + {12834900, 41530200}, {12702800, 41370500}, + {12551700, 41228700}, {12384100, 41106900}, + {12202500, 41007000}, {12009900, 40930800}, + {11809200, 40879200}, {11603600, 40853300}, + }, + { + {-3181780, 21500000}, {-15111782, 22578598}, + {-15737800, 24505100}, {-15797300, 25071800}, + {-15997500, 26976600}, {-16000000, 27000000}, + {-15737800, 29494900}, {-14962500, 31880800}, + {-13708200, 34053400}, {-12029600, 35917700}, + {-10000000, 37392300}, {-8500000, 38060100}, + {-7708200, 38412700}, {-5282850, 38928200}, + {-5254340, 38934300}, {-3000000, 38934300}, + {-3000000, 33500000}, {-1092350, 31592300}, + {-1091812, 31591773}, {-1065440, 31565400}, + {-1063162, 31563127}, {-963937, 31463900}, + {-889918, 31389900}, {-860013, 31360013}, + {-612800, 31112800}, {-589409, 31089400}, + {-366049, 30866044}, {-339918, 30839900}, + {-206119, 30706100}, {0, 30500000}, + {206119, 30706100}, {339918, 30839900}, + {362385, 30862377}, {889918, 31389900}, + {963937, 31463900}, {1063442, 31563406}, + {1065440, 31565400}, {1091822, 31591782}, + {1092350, 31592300}, {4000000, 34500000}, + {4000000, 35939184}, {4029570, 35917700}, + {5708200, 34053400}, {6962550, 31880800}, + {7737770, 29494900}, {8000000, 27000000}, + {7737770, 24505100}, {6962550, 22119200}, + {6605070, 21500000}, {1727920, 21500000}, + {1698940, 21529000}, {1530830, 21697100}, + {1516490, 21711437}, {1125240, 22102700}, + {894136, 22333800}, {648935, 22579000}, + {318574, 22909343}, {131860, 23096100}, + {0, 23227900}, {-131860, 23096100}, + {-318574, 22909343}, {-648935, 22579000}, + {-894136, 22333800}, {-1125240, 22102700}, + {-1516490, 21711437}, {-1530830, 21697100}, + {-1698940, 21529000}, {-1727920, 21500000}, + }, + { + {-19603600, 9853260}, {-19809200, 9879230}, + {-20009900, 9930760}, {-20202500, 10007000}, + {-20384100, 10106900}, {-20551800, 10228700}, + {-20702800, 10370500}, {-20834900, 10530200}, + {-20945900, 10705100}, {-21034100, 10892600}, + {-21098200, 11089700}, {-21137000, 11293200}, + {-21150000, 11500000}, {-21137000, 11706800}, + {-21098200, 11910300}, {-21034100, 12107400}, + {-20945900, 12294900}, {-20834900, 12469800}, + {-20702800, 12629500}, {-20551800, 12771300}, + {-20384100, 12893100}, {-20202500, 12993000}, + {-20009900, 13069200}, {-19809200, 13120800}, + {-19603600, 13146700}, {-19396400, 13146700}, + {-19190800, 13120800}, {-18990100, 13069200}, + {-18797500, 12993000}, {-18615900, 12893100}, + {-18448200, 12771300}, {-18297200, 12629500}, + {-18165100, 12469800}, {-18054100, 12294900}, + {-17965900, 12107400}, {-17901800, 11910300}, + {-17863000, 11706800}, {-17850000, 11500000}, + {-17863000, 11293200}, {-17901800, 11089700}, + {-17965900, 10892600}, {-18054100, 10705100}, + {-18165100, 10530200}, {-18297200, 10370500}, + {-18448200, 10228700}, {-18615900, 10106900}, + {-18797500, 10007000}, {-18990100, 9930760}, + {-19190800, 9879230}, {-19396400, 9853260}, + }, + { + {11396400, 9853260}, {11190800, 9879230}, + {10990100, 9930760}, {10797500, 10007000}, + {10615900, 10106900}, {10448200, 10228700}, + {10297200, 10370500}, {10165100, 10530200}, + {10054100, 10705100}, {9965870, 10892600}, + {9901840, 11089700}, {9863010, 11293200}, + {9850000, 11500000}, {9863010, 11706800}, + {9901840, 11910300}, {9965870, 12107400}, + {10054100, 12294900}, {10165100, 12469800}, + {10297200, 12629500}, {10448200, 12771300}, + {10615900, 12893100}, {10797500, 12993000}, + {10990100, 13069200}, {11190800, 13120800}, + {11396400, 13146700}, {11603600, 13146700}, + {11809200, 13120800}, {12009900, 13069200}, + {12202500, 12993000}, {12384100, 12893100}, + {12551700, 12771300}, {12702800, 12629500}, + {12834900, 12469800}, {12945900, 12294900}, + {13034100, 12107400}, {13098200, 11910300}, + {13137000, 11706800}, {13150000, 11500000}, + {13137000, 11293200}, {13098200, 11089700}, + {13034100, 10892600}, {12945900, 10705100}, + {12834900, 10530200}, {12702800, 10370500}, + {12551700, 10228700}, {12384100, 10106900}, + {12202500, 10007000}, {12009900, 9930760}, + {11809200, 9879230}, {11603600, 9853260}, + }, + { + {-17177700, 1309310}, {-17525300, 1383200}, + {-17850000, 1527760}, {-18137500, 1736650}, + {-18375300, 2000760}, {-18553000, 2308550}, + {-18662800, 2646550}, {-18700000, 3000000}, + {-18662800, 3353450}, {-18553000, 3691450}, + {-18375300, 3999230}, {-18137500, 4263350}, + {-17850000, 4472240}, {-17525300, 4616800}, + {-17177700, 4690690}, {-16822300, 4690690}, + {-16474701, 4616800}, {-16150000, 4472240}, + {-15862500, 4263350}, {-15624700, 3999230}, + {-15447000, 3691450}, {-15337100, 3353450}, + {-15300000, 3000000}, {-15337100, 2646550}, + {-15447000, 2308550}, {-15624700, 2000760}, + {-15862500, 1736650}, {-16150000, 1527760}, + {-16474701, 1383200}, {-16822300, 1309310}, + }, + { + {-11603600, -2146740}, {-11809200, -2120770}, + {-12009900, -2069240}, {-12202500, -1992960}, + {-12384100, -1893140}, {-12551700, -1771350}, + {-12702800, -1629500}, {-12834900, -1469840}, + {-12945900, -1294890}, {-13034100, -1107400}, + {-13098200, -910337}, {-13137000, -706800}, + {-13150000, -500000}, {-13137000, -293200}, + {-13098200, -89661}, {-13034100, 107405}, + {-12945900, 294893}, {-12834900, 469845}, + {-12702800, 629502}, {-12551700, 771346}, + {-12384100, 893141}, {-12202500, 992964}, + {-12009900, 1069240}, {-11809200, 1120770}, + {-11603600, 1146740}, {-11396400, 1146740}, + {-11190800, 1120770}, {-10990100, 1069240}, + {-10797500, 992964}, {-10615900, 893141}, + {-10448200, 771346}, {-10297200, 629502}, + {-10165100, 469845}, {-10054100, 294893}, + {-9965870, 107405}, {-9901840, -89661}, + {-9863010, -293200}, {-9850000, -499999}, + {-9863010, -706800}, {-9901840, -910337}, + {-9965870, -1107400}, {-10054100, -1294890}, + {-10165100, -1469840}, {-10297200, -1629500}, + {-10448200, -1771350}, {-10615900, -1893140}, + {-10797500, -1992960}, {-10990100, -2069240}, + {-11190800, -2120770}, {-11396400, -2146740}, + }, + { + {11396400, -2146740}, {11190800, -2120770}, + {10990100, -2069240}, {10797500, -1992960}, + {10615900, -1893140}, {10448200, -1771350}, + {10297200, -1629500}, {10165100, -1469840}, + {10054100, -1294890}, {9965870, -1107400}, + {9901840, -910337}, {9863010, -706800}, + {9850000, -500000}, {9863010, -293200}, + {9901840, -89661}, {9965870, 107405}, + {10054100, 294893}, {10165100, 469845}, + {10297200, 629502}, {10448200, 771346}, + {10615900, 893141}, {10797500, 992964}, + {10990100, 1069240}, {11190800, 1120770}, + {11396400, 1146740}, {11603600, 1146740}, + {11809200, 1120770}, {12009900, 1069240}, + {12202500, 992964}, {12384100, 893141}, + {12551700, 771346}, {12702800, 629502}, + {12834900, 469845}, {12945900, 294893}, + {13034100, 107405}, {13098200, -89661}, + {13137000, -293200}, {13150000, -499999}, + {13137000, -706800}, {13098200, -910337}, + {13034100, -1107400}, {12945900, -1294890}, + {12834900, -1469840}, {12702800, -1629500}, + {12551700, -1771350}, {12384100, -1893140}, + {12202500, -1992960}, {12009900, -2069240}, + {11809200, -2120770}, {11603600, -2146740}, + }, + }}, + }, + ExPolygons{ + // "z-axis-top.stl": + MyPoly{{ + {34521100, -3478930}, + {38000000, 0}, + {38000000, 23000000}, + {33000000, 28000000}, + {24000000, 28000000}, + {20000000, 21071800}, + {12000000, 21071800}, + {8000000, 28000000}, + {8000000, 34200000}, + {2200000, 40000000}, + {0, 40000000}, + {0, 1000000}, + {6000000, -5000000}, + {33000000, -5000000}, + }, + { + { + {12000000, 3071800}, + {8000000, 10000000}, + {12000000, 16928200}, + {20000000, 16928200}, + {24000000, 10000000}, + {20000000, 3071800}, + }, + }}, + MyPoly{{ + {8000000, -46200000}, + {8000000, -40000000}, + {12000000, -33071800}, + {20000000, -33071800}, + {24000000, -40000000}, + {33000000, -40000000}, + {38000000, -35000000}, + {38000000, -12000000}, + {34521100, -8521070}, + {33000000, -7000000}, + {6000000, -7000000}, + {0, -13000000}, + {0, -52000000}, + {2200000, -52000000}, + }, + { + { + {12000000, -28928200}, + {8000000, -22000000}, + {12000000, -15071800}, + {20000000, -15071800}, + {24000000, -22000000}, + {20000000, -28928200}, + }, + }}, + }, + ExPolygons{ + // "y-belt-holder.stl": + MyPoly{{ + {12500000, 24000000}, + {5142140, 24000000}, + {4500000, 23357900}, + {4500000, 15000000}, + {-9057860, 15000000}, + {-11000000, 13057900}, + {-11000000, -13057900}, + {-9057860, -15000000}, + {4500000, -15000000}, + {4500000, -23357900}, + {5142140, -24000000}, + {12500000, -24000000}, + }, + {}}, + }, + ExPolygons{ + // "LCD-knob.stl": + MyPoly{{ + {1045280, -9945220}, {2079119, -9781480}, + {3090170, -9510560}, {4067370, -9135450}, + {5000000, -8660250}, {5877850, -8090170}, + {6691310, -7431450}, {7431450, -6691310}, + {8090170, -5877850}, {8660250, -5000000}, + {9135450, -4067370}, {9510560, -3090170}, + {9781480, -2079119}, {9945220, -1045280}, + {10000000, 0}, {9945220, 1045280}, + {9781480, 2079119}, {9510560, 3090170}, + {9135450, 4067370}, {8660250, 5000000}, + {8090170, 5877850}, {7431450, 6691310}, + {6691310, 7431450}, {5877850, 8090170}, + {5000000, 8660250}, {4067370, 9135450}, + {3090170, 9510560}, {2100000, 9775880}, + {2100000, 18221800}, {-2100000, 18221800}, + {-2100000, 9775880}, {-3090170, 9510560}, + {-4067370, 9135450}, {-5000000, 8660250}, + {-5877850, 8090170}, {-6691310, 7431450}, + {-7431450, 6691310}, {-8090170, 5877850}, + {-8660250, 5000000}, {-9135450, 4067370}, + {-9510560, 3090170}, {-9781480, 2079119}, + {-9945220, 1045280}, {-10000000, 0}, + {-9945220, -1045280}, {-9781480, -2079119}, + {-9510560, -3090170}, {-9135450, -4067370}, + {-8660250, -5000000}, {-8090170, -5877850}, + {-7431450, -6691310}, {-6691310, -7431450}, + {-5877850, -8090170}, {-5000000, -8660250}, + {-4067370, -9135450}, {-3090170, -9510560}, + {-2079119, -9781480}, {-1045280, -9945220}, + {0, -10000000}, + }, + {}}, + }, + ExPolygons{ + // "rpi-zero-frame.stl": + MyPoly{{ + {58000000, -25983600}, {58313600, -25983600}, + {58927100, -25853200}, {59500000, -25598100}, + {60007400, -25229400}, {60427100, -24763400}, + {60740600, -24220200}, {60934400, -23623700}, + {61000000, -23000000}, {61000000, 0}, + {60934400, 623734}, {60740600, 1220210}, + {60427100, 1763360}, {60007400, 2229430}, + {59500000, 2598080}, {58927100, 2853170}, + {58313600, 2983570}, {58000000, 2983570}, + {58000000, 3000000}, {55000000, 3000000}, + {55000000, 6000000}, {45000000, 6000000}, + {45000000, 3000000}, {0, 3000000}, + {0, 2983570}, {-313585, 2983570}, + {-927051, 2853170}, {-1500000, 2598080}, + {-2007390, 2229430}, {-2427050, 1763360}, + {-2740640, 1220210}, {-2934440, 623734}, + {-3000000, 0}, {-3000000, -23000000}, + {-2934440, -23623700}, {-2740640, -24220200}, + {-2427050, -24763400}, {-2007390, -25229400}, + {-1500000, -25598100}, {-927051, -25853200}, + {-313585, -25983600}, {313585, -25983600}, + {927051, -25853200}, {1000000, -25820720}, + {1000000, -26000000}, {58000000, -26000000}, + }, + { + { + {44883600, -2063829}, + {44638500, -2012070}, + {44409000, -1909530}, + {44205900, -1762070}, + {44037900, -1575550}, + {43912900, -1358750}, + {43834800, -1120470}, + {43822600, -1000000}, + {46195173, -1000000}, + {46182500, -1120470}, + {46105300, -1358750}, + {45979300, -1575550}, + {45812300, -1762070}, + {45609200, -1909530}, + {45379700, -2012070}, + {45134600, -2063829}, + }, + { + {51045700, -1970080}, + {50800600, -1918320}, + {50571100, -1815780}, + {50368000, -1668320}, + {50200000, -1481800}, + {50075000, -1265000}, + {49996900, -1025740}, + {49994200, -1000000}, + {52347300, -1000000}, + {52344600, -1025740}, + {52267400, -1265000}, + {52141400, -1481800}, + {51973500, -1668320}, + {51771300, -1815780}, + {51541800, -1918320}, + {51296700, -1970080}, + }, + { + {3000000, -20000000}, + {3000000, -3000000}, + {43887500, -3000000}, + {43912900, -2922230}, + {44037900, -2705430}, + {44205900, -2518910}, + {44409000, -2371440}, + {44638500, -2268910}, + {44883600, -2217150}, + {45134600, -2217150}, + {45379700, -2268910}, + {45609200, -2371440}, + {45812300, -2518910}, + {45979300, -2705430}, + {46105300, -2922230}, + {46130400, -3000000}, + {55000000, -3000000}, + {55000000, -20000000}, + }, + { + {22525500, -22729500}, {22321000, -22686100}, + {22130000, -22601000}, {21960900, -22478100}, + {21821000, -22322800}, {21716500, -22141700}, + {21651900, -21942900}, {21630000, -21735000}, + {21651900, -21527100}, {21716500, -21328300}, + {21821000, -21147200}, {21960900, -20991900}, + {22130000, -20869000}, {22321000, -20783900}, + {22525500, -20740500}, {22734500, -20740500}, + {22939000, -20783900}, {23130000, -20869000}, + {23299100, -20991900}, {23439000, -21147200}, + {23543500, -21328300}, {23608100, -21527100}, + {23630000, -21735000}, {23608100, -21942900}, + {23543500, -22141700}, {23439000, -22322800}, + {23299100, -22478100}, {23130000, -22601000}, + {22939000, -22686100}, {22734500, -22729500}, + }, + { + {7285470, -25269500}, {7080980, -25226100}, + {6890000, -25141000}, {6720870, -25018100}, + {6580980, -24862800}, {6476450, -24681700}, + {6411850, -24482900}, {6390000, -24275000}, + {6411850, -24067100}, {6476450, -23868300}, + {6580980, -23687200}, {6720870, -23531900}, + {6890000, -23409000}, {7080980, -23323900}, + {7285470, -23280500}, {7494530, -23280500}, + {7699020, -23323900}, {7890000, -23409000}, + {8059129, -23531900}, {8199020, -23687200}, + {8303540, -23868300}, {8368150, -24067100}, + {8390000, -24275000}, {8368150, -24482900}, + {8303540, -24681700}, {8199020, -24862800}, + {8059129, -25018100}, {7890000, -25141000}, + {7699020, -25226100}, {7494530, -25269500}, + }, + { + {22525500, -25269500}, {22321000, -25226100}, + {22130000, -25141000}, {21960900, -25018100}, + {21821000, -24862800}, {21716500, -24681700}, + {21651900, -24482900}, {21630000, -24275000}, + {21651900, -24067100}, {21716500, -23868300}, + {21821000, -23687200}, {21960900, -23531900}, + {22130000, -23409000}, {22321000, -23323900}, + {22525500, -23280500}, {22734500, -23280500}, + {22939000, -23323900}, {23130000, -23409000}, + {23299100, -23531900}, {23439000, -23687200}, + {23543500, -23868300}, {23608100, -24067100}, + {23630000, -24275000}, {23608100, -24482900}, + {23543500, -24681700}, {23439000, -24862800}, + {23299100, -25018100}, {23130000, -25141000}, + {22939000, -25226100}, {22734500, -25269500}, + }, + { + {14905500, -25269500}, {14701000, -25226100}, + {14510000, -25141000}, {14340900, -25018100}, + {14201000, -24862800}, {14096500, -24681700}, + {14031900, -24482900}, {14010000, -24275000}, + {14031900, -24067100}, {14096500, -23868300}, + {14201000, -23687200}, {14340900, -23531900}, + {14510000, -23409000}, {14701000, -23323900}, + {14905500, -23280500}, {15114500, -23280500}, + {15319000, -23323900}, {15510000, -23409000}, + {15679100, -23531900}, {15819000, -23687200}, + {15923500, -23868300}, {15988100, -24067100}, + {16010000, -24275000}, {15988100, -24482900}, + {15923500, -24681700}, {15819000, -24862800}, + {15679100, -25018100}, {15510000, -25141000}, + {15319000, -25226100}, {15114500, -25269500}, + }, + { + {12365500, -25269500}, {12161000, -25226100}, + {11970000, -25141000}, {11800900, -25018100}, + {11661000, -24862800}, {11556500, -24681700}, + {11491900, -24482900}, {11470000, -24275000}, + {11491900, -24067100}, {11556500, -23868300}, + {11661000, -23687200}, {11800900, -23531900}, + {11970000, -23409000}, {12161000, -23323900}, + {12365500, -23280500}, {12574500, -23280500}, + {12779000, -23323900}, {12970000, -23409000}, + {13139100, -23531900}, {13279000, -23687200}, + {13383500, -23868300}, {13448100, -24067100}, + {13470000, -24275000}, {13448100, -24482900}, + {13383500, -24681700}, {13279000, -24862800}, + {13139100, -25018100}, {12970000, -25141000}, + {12779000, -25226100}, {12574500, -25269500}, + }, + { + {9825470, -25269500}, {9620980, -25226100}, + {9430000, -25141000}, {9260870, -25018100}, + {9120980, -24862800}, {9016450, -24681700}, + {8951850, -24482900}, {8930000, -24275000}, + {8951850, -24067100}, {9016450, -23868300}, + {9120980, -23687200}, {9260870, -23531900}, + {9430000, -23409000}, {9620980, -23323900}, + {9825470, -23280500}, {10034500, -23280500}, + {10239000, -23323900}, {10430000, -23409000}, + {10599100, -23531900}, {10739000, -23687200}, + {10843500, -23868300}, {10908100, -24067100}, + {10930000, -24275000}, {10908100, -24482900}, + {10843500, -24681700}, {10739000, -24862800}, + {10599100, -25018100}, {10430000, -25141000}, + {10239000, -25226100}, {10034500, -25269500}, + }, + }}, + }, + ExPolygons{ + // "extruder-idler.stl": + MyPoly{{ + {31500000, 47000000}, {21500000, 47000000}, + {21500000, 43000000}, {21483600, 43000000}, + {21483600, 42686400}, {21443900, 42500000}, + {21391492, 42253213}, {21356700, 42089700}, + {21353200, 42072900}, {21302200, 41958400}, + {21234000, 41805300}, {21184936, 41695077}, + {21111500, 41530200}, {21098100, 41500000}, + {21058200, 41445200}, {20966500, 41319000}, + {20900900, 41228700}, {20812400, 41106900}, + {20729400, 40992600}, {20660700, 40930800}, + {20566175, 40845649}, {20359300, 40659400}, + {20263400, 40572900}, {19720200, 40259400}, + {19123700, 40065600}, {18688436, 40019806}, + {18500000, 40000000}, {18409800, 40009500}, + {17876300, 40065600}, {17279800, 40259400}, + {16736601, 40572900}, {16640699, 40659400}, + {16435924, 40843758}, {16339300, 40930800}, + {16270599, 40992600}, {16187599, 41106900}, + {16099100, 41228700}, {15996000, 41370500}, + {16270599, 40992600}, {15901900, 41500000}, + {15888500, 41530200}, {15810600, 41705100}, + {15755314, 41829246}, {15735700, 41873300}, + {15646800, 42072900}, {15643300, 42089700}, + {15608508, 42253213}, {15556100, 42500000}, + {15516400, 42686400}, {15516400, 43000000}, + {15500000, 43000000}, {15500000, 47000000}, + {6500000, 47000000}, {6500000, 39000000}, + {3500000, 39000000}, {3500000, 22000000}, + {6500000, 22000000}, {6500000, 13000000}, + {31500000, 13000000}, + }, + { + { + {12923500, 25400000}, + {12144000, 25850000}, + {12144000, 32150002}, + {12923500, 32599998}, + {17600000, 35300000}, + {18379400, 34850000}, + {23056000, 32150002}, + {23056000, 32000000}, + {22750000, 32000000}, + {22750000, 30992100}, + {21750000, 29994100}, + {21750000, 25096023}, + {17600000, 22700000}, + }, + { + {26393300, 16803400}, {26181400, 16830100}, + {25974700, 16883200}, {25776200, 16961800}, + {25589100, 17064600}, {25416400, 17190100}, + {25260800, 17336300}, {25124700, 17500800}, + {25010300, 17681000}, {24919400, 17874200}, + {24853400, 18077200}, {24813400, 18286900}, + {24800000, 18500000}, {24813400, 18713100}, + {24853400, 18922800}, {24919400, 19125800}, + {25010300, 19319000}, {25124700, 19499200}, + {25260800, 19663700}, {25416400, 19809900}, + {25589100, 19935400}, {25776200, 20038200}, + {25974700, 20116800}, {26181400, 20169900}, + {26393300, 20196600}, {26606700, 20196600}, + {26818500, 20169900}, {27025300, 20116800}, + {27223800, 20038200}, {27410900, 19935400}, + {27583600, 19809900}, {27739200, 19663700}, + {27875300, 19499200}, {27989700, 19319000}, + {28080600, 19125800}, {28146600, 18922800}, + {28186600, 18713100}, {28200000, 18500000}, + {28186600, 18286900}, {28146600, 18077200}, + {28080600, 17874200}, {27989700, 17681000}, + {27875300, 17500800}, {27739200, 17336300}, + {27583600, 17190100}, {27410900, 17064600}, + {27223800, 16961800}, {27025300, 16883200}, + {26818500, 16830100}, {26606700, 16803400}, + }, + { + {11393300, 16803400}, {11181500, 16830100}, + {10974700, 16883200}, {10776200, 16961800}, + {10589100, 17064600}, {10416400, 17190100}, + {10260800, 17336300}, {10124700, 17500800}, + {10010300, 17681000}, {9919380, 17874200}, + {9853410, 18077200}, {9813400, 18286900}, + {9800000, 18500000}, {9813400, 18713100}, + {9853410, 18922800}, {9919380, 19125800}, + {10010300, 19319000}, {10124700, 19499200}, + {10260800, 19663700}, {10416400, 19809900}, + {10589100, 19935400}, {10776200, 20038200}, + {10974700, 20116800}, {11181500, 20169900}, + {11393300, 20196600}, {11606700, 20196600}, + {11818500, 20169900}, {12025300, 20116800}, + {12223800, 20038200}, {12410900, 19935400}, + {12583600, 19809900}, {12739200, 19663700}, + {12875300, 19499200}, {12989700, 19319000}, + {13080600, 19125800}, {13146600, 18922800}, + {13186600, 18713100}, {13200000, 18500000}, + {13186600, 18286900}, {13146600, 18077200}, + {13080600, 17874200}, {12989700, 17681000}, + {12875300, 17500800}, {12739200, 17336300}, + {12583600, 17190100}, {12410900, 17064600}, + {12223800, 16961800}, {12025300, 16883200}, + {11818500, 16830100}, {11606700, 16803400}, + }, + }}, + }, + ExPolygons{ + // "filament-sensor-cover.stl": + MyPoly{{ + {18000000, 30500000}, + {-6000000, 30500000}, + {-6000000, -5500000}, + {18000000, -5500000}, + }, + { + { + {-1167240, 22908800}, {-1494430, 22978300}, + {-1800000, 23114400}, {-2070610, 23311000}, + {-2294430, 23559500}, {-2461670, 23849200}, + {-2565040, 24167300}, {-2582520, 24333700}, + {-2600000, 24500000}, {-2582520, 24666300}, + {-2565040, 24832700}, {-2461670, 25150800}, + {-2294430, 25440500}, {-2070610, 25689000}, + {-1800000, 25885600}, {-1494430, 26021700}, + {-1167240, 26091200}, {-832754, 26091200}, + {-505572, 26021700}, {-200000, 25885600}, + {70608, 25689000}, {294427, 25440500}, + {461672, 25150800}, {565036, 24832700}, + {582518, 24666300}, {599999, 24500000}, + {582518, 24333700}, {565036, 24167300}, + {461672, 23849200}, {294427, 23559500}, + {70608, 23311000}, {-200000, 23114400}, + {-505572, 22978300}, {-832754, 22908800}, + }, + { + {-144249, 15627600}, {-426443, 15687500}, + {-689999, 15804900}, {-740738, 15841700}, + {-923400, 15974500}, {-965366, 16021099}, + {-1116440, 16188900}, {-1229330, 16384399}, + {-1260690, 16438700}, {-1280070, 16498400}, + {-1349840, 16713100}, {-1373440, 16937600}, + {-1380000, 17000000}, {-1373440, 17062400}, + {-1349840, 17286900}, {-1280070, 17501600}, + {-1260690, 17561300}, {-1229330, 17615600}, + {-1116440, 17811100}, {-965366, 17978900}, + {-923400, 18025500}, {-872661, 18062400}, + {-689999, 18195100}, {-483738, 18286900}, + {-426443, 18312500}, {-144249, 18372400}, + {144249, 18372400}, {426443, 18312500}, + {483738, 18286900}, {689999, 18195100}, + {872661, 18062400}, {923400, 18025500}, + {965366, 17978900}, {1116440, 17811100}, + {1229330, 17615600}, {1260690, 17561300}, + {1280070, 17501600}, {1349840, 17286900}, + {1373440, 17062400}, {1380000, 17000000}, + {1373440, 16937600}, {1349840, 16713100}, + {1280070, 16498400}, {1260690, 16438700}, + {1229330, 16384399}, {1116440, 16188900}, + {965366, 16021099}, {923400, 15974500}, + {872661, 15937600}, {689999, 15804900}, + {483738, 15713100}, {426443, 15687500}, + {144249, 15627600}, + }, + { + {11832800, 10408800}, {11505600, 10478300}, + {11200000, 10614400}, {10929400, 10811000}, + {10705600, 11059500}, {10538300, 11349200}, + {10435000, 11667300}, {10400000, 12000000}, + {10435000, 12332700}, {10538300, 12650800}, + {10705600, 12940500}, {10929400, 13189000}, + {11200000, 13385600}, {11505600, 13521700}, + {11832800, 13591200}, {12167200, 13591200}, + {12494400, 13521700}, {12800000, 13385600}, + {13070600, 13189000}, {13294400, 12940500}, + {13461700, 12650800}, {13565000, 12332700}, + {13585100, 12141400}, {13600000, 12000000}, + {13582500, 11833700}, {13565000, 11667300}, + {13461700, 11349200}, {13294400, 11059500}, + {13070600, 10811000}, {12800000, 10614400}, + {12494400, 10478300}, {12167200, 10408800}, + }, + }}, + }, + ExPolygons{ + // "nozzle-fan.stl": + MyPoly{{ + {-14922022, 12367866}, {-14205200, 14337200}, + {-13800000, 15450500}, {-13800000, 17000000}, + {-13789800, 17000000}, {-13704300, 17813300}, + {-13694100, 17910800}, {-12789600, 20694300}, + {-11326200, 23229000}, {-9367830, 25404000}, + {-7000000, 27124400}, {-5253290, 27902000}, + {-4326240, 28314800}, {-1463400, 28923300}, + {1463400, 28923300}, {4326240, 28314800}, + {5253290, 27902000}, {7000000, 27124400}, + {9367830, 25404000}, {11326200, 23229000}, + {12789600, 20694300}, {13694100, 17910800}, + {13704300, 17813300}, {13789800, 17000000}, + {13800000, 17000000}, {13800000, 15606900}, + {14240100, 14397700}, {15015200, 12268200}, + {15476800, 11000000}, {17800000, 11000000}, + {17800000, 11032900}, {18427200, 11032900}, + {19654100, 11293700}, {20800000, 11803800}, + {21814800, 12541100}, {22654100, 13473300}, + {23281300, 14559600}, {23668900, 15752500}, + {23706600, 16111099}, {23800000, 17000000}, + {23789800, 17000000}, {23475500, 19989900}, + {21925100, 24761700}, {19416400, 29106800}, + {18612200, 30000000}, {18000000, 30679900}, + {18000000, 35500000}, {18500000, 35500000}, + {18500000, 40500000}, {17839500, 40500000}, + {11200000, 52000000}, {9532010, 52000000}, + {9532010, 52800000}, {5416000, 52800000}, + {5416000, 52000000}, {4793090, 52000000}, + {4793090, 52800000}, {-65918, 52800000}, + {-65918, 52000000}, {-296738, 52000000}, + {-296738, 52800000}, {-4368740, 52800000}, + {-4368740, 52000000}, {-4995880, 52000000}, + {-4995880, 52800000}, {-5997880, 52800000}, + {-5997880, 52000000}, {-6891430, 52000000}, + {-6891430, 52800000}, {-10271400, 52800000}, + {-10271400, 52000000}, {-11139700, 52000000}, + {-18000000, 40117700}, {-18000000, 30679900}, + {-18612200, 30000000}, {-19416400, 29106800}, + {-21925100, 24761700}, {-23475500, 19989900}, + {-23789800, 17000000}, {-23800000, 17000000}, + {-23668900, 15752500}, {-23281300, 14559600}, + {-22654100, 13473300}, {-21814800, 12541100}, + {-20800000, 11803800}, {-19654100, 11293700}, + {-18427200, 11032900}, {-17800000, 11032900}, + {-17800000, 11000000}, {-15419900, 11000000}, + }, + {}}, + }, + ExPolygons{ + // "x-carriage-back.stl": + MyPoly{{ + {-5981270, -38729200}, {-5354100, -37638700}, + {-4514780, -36703000}, {-3500000, -35962900}, + {-2354100, -35450800}, {-1969730, -35368800}, + {-1127170, -35189000}, {127170, -35189000}, + {969727, -35368800}, {1354100, -35450800}, + {2500000, -35962900}, {3514780, -36703000}, + {4354100, -37638700}, {4981270, -38729200}, + {5068930, -39000000}, {13757900, -39000000}, + {16000000, -36757900}, {16000000, -5000000}, + {25500000, -5000000}, {25500000, 13795200}, + {25057400, 14352100}, {24542500, 15342500}, + {24400000, 15200000}, {14600000, 25000000}, + {14500000, 25000000}, {14500000, 33050000}, + {13050000, 34500000}, {-500000, 34500000}, + {-500000, 28500000}, {-8000000, 28500000}, + {-8000000, 32500000}, {-5000000, 32500000}, + {-5000000, 34500000}, {-15550000, 34500000}, + {-17000000, 33050000}, {-17000000, 25000000}, + {-17100000, 25000000}, {-25572200, 16527800}, + {-25903900, 15532800}, {-26500000, 14386100}, + {-26500000, -5000000}, {-17000000, -5000000}, + {-17000000, -36757900}, {-14757900, -39000000}, + {-6068930, -39000000}, + }, + { + { + {-13103600, 29353300}, {-13309200, 29379200}, + {-13509900, 29430800}, {-13702500, 29507000}, + {-13884100, 29606900}, {-14051700, 29728700}, + {-14202800, 29870500}, {-14334900, 30030200}, + {-14445900, 30205100}, {-14534100, 30392600}, + {-14598200, 30589700}, {-14637000, 30793200}, + {-14650000, 31000000}, {-14637000, 31206800}, + {-14598200, 31410300}, {-14534100, 31607400}, + {-14445900, 31794900}, {-14334900, 31969800}, + {-14202800, 32129502}, {-14051700, 32271302}, + {-13884100, 32393100}, {-13702500, 32493000}, + {-13509900, 32569198}, {-13309200, 32620800}, + {-13103600, 32646702}, {-12896400, 32646702}, + {-12690800, 32620800}, {-12490100, 32569198}, + {-12297500, 32493000}, {-12115900, 32393100}, + {-11948200, 32271302}, {-11797200, 32129502}, + {-11665100, 31969800}, {-11554100, 31794900}, + {-11465900, 31607400}, {-11401800, 31410300}, + {-11363000, 31206800}, {-11350000, 31000000}, + {-11363000, 30793200}, {-11401800, 30589700}, + {-11465900, 30392600}, {-11554100, 30205100}, + {-11665100, 30030200}, {-11797200, 29870500}, + {-11948200, 29728700}, {-12115900, 29606900}, + {-12297500, 29507000}, {-12490100, 29430800}, + {-12690800, 29379200}, {-12896400, 29353300}, + }, + { + {10396400, 29353300}, {10190800, 29379200}, + {9990120, 29430800}, {9797460, 29507000}, + {9615890, 29606900}, {9448250, 29728700}, + {9297200, 29870500}, {9165120, 30030200}, + {9054090, 30205100}, {8965870, 30392600}, + {8901840, 30589700}, {8863010, 30793200}, + {8850000, 31000000}, {8863010, 31206800}, + {8901840, 31410300}, {8965870, 31607400}, + {9054090, 31794900}, {9165120, 31969800}, + {9297200, 32129502}, {9448250, 32271302}, + {9615890, 32393100}, {9797460, 32493000}, + {9990120, 32569198}, {10190800, 32620800}, + {10396400, 32646702}, {10603600, 32646702}, + {10809200, 32620800}, {11009900, 32569198}, + {11202500, 32493000}, {11384100, 32393100}, + {11551700, 32271302}, {11702800, 32129502}, + {11834900, 31969800}, {11945900, 31794900}, + {12034100, 31607400}, {12098200, 31410300}, + {12137000, 31206800}, {12150000, 31000000}, + {12137000, 30793200}, {12098200, 30589700}, + {12034100, 30392600}, {11945900, 30205100}, + {11834900, 30030200}, {11702800, 29870500}, + {11551700, 29728700}, {11384100, 29606900}, + {11202500, 29507000}, {11009900, 29430800}, + {10809200, 29379200}, {10603600, 29353300}, + }, + { + {-8000000, 17500000}, + {-8000000, 22500000}, + {-4500000, 22500000}, + {-4500000, 17500000}, + }, + { + {-1103600, 2353260}, {-1309180, 2379230}, + {-1509880, 2430760}, {-1702540, 2507040}, + {-1884110, 2606860}, {-2051750, 2728650}, + {-2202800, 2870500}, {-2334880, 3030150}, + {-2445910, 3205110}, {-2534130, 3392590}, + {-2598160, 3589660}, {-2636990, 3793200}, + {-2650000, 4000000}, {-2636990, 4206800}, + {-2598160, 4410340}, {-2534130, 4607400}, + {-2445910, 4794890}, {-2334880, 4969840}, + {-2202800, 5129500}, {-2051750, 5271350}, + {-1884110, 5393140}, {-1702540, 5492960}, + {-1509880, 5569240}, {-1309180, 5620770}, + {-1103600, 5646740}, {-896395, 5646740}, + {-690821, 5620770}, {-490122, 5569240}, + {-297463, 5492960}, {-115886, 5393140}, + {51749, 5271350}, {202798, 5129500}, + {334878, 4969840}, {445906, 4794890}, + {534131, 4607400}, {598162, 4410340}, + {636989, 4206800}, {650000, 4000000}, + {636989, 3793200}, {598162, 3589660}, + {534131, 3392590}, {445906, 3205110}, + {334878, 3030150}, {202798, 2870500}, + {51749, 2728650}, {-115886, 2606860}, + {-297463, 2507040}, {-490122, 2430760}, + {-690821, 2379230}, {-896395, 2353260}, + }, + { + {10876300, -3434440}, {10279800, -3240640}, + {9736640, -2927050}, {9270570, -2507390}, + {8901920, -2000000}, {8646830, -1427050}, + {8516430, -813585}, {8516430, -186414}, + {8646830, 427051}, {8901920, 999999}, + {9270570, 1507390}, {9736640, 1927050}, + {10279800, 2240640}, {10876300, 2434440}, + {11500000, 2500000}, {12123700, 2434440}, + {12720200, 2240640}, {13263400, 1927050}, + {13729400, 1507390}, {14098100, 1000000}, + {14353200, 427051}, {14483600, -186414}, + {14483600, -813585}, {14353200, -1427050}, + {14098100, -2000000}, {13729400, -2507390}, + {13263400, -2927050}, {12720200, -3240640}, + {12123700, -3434440}, {11500000, -3500000}, + }, + { + {-12123700, -3434440}, {-12720200, -3240640}, + {-13263400, -2927050}, {-13729400, -2507390}, + {-14098100, -2000000}, {-14353200, -1427050}, + {-14483600, -813585}, {-14483600, -186414}, + {-14353200, 427051}, {-14098100, 999999}, + {-13729400, 1507390}, {-13263400, 1927050}, + {-12720200, 2240640}, {-12123700, 2434440}, + {-11500000, 2500000}, {-10876300, 2434440}, + {-10279800, 2240640}, {-9736640, 1927050}, + {-9270570, 1507390}, {-8901920, 1000000}, + {-8646830, 427051}, {-8516430, -186414}, + {-8516430, -813585}, {-8646830, -1427050}, + {-8901920, -2000000}, {-9270570, -2507390}, + {-9736640, -2927050}, {-10279800, -3240640}, + {-10876300, -3434440}, {-11500000, -3500000}, + }, + { + {-1539560, -22890700}, {-2533680, -22567700}, + {-3438930, -22045100}, {-3590280, -21908800}, + {-4215720, -21345700}, {-4806270, -20532800}, + {-4830130, -20500000}, {-5012570, -20090200}, + {-5255280, -19545100}, {-5472610, -18522600}, + {-5472610, -18000000}, {-5500000, -18000000}, + {-5500000, -14000000}, {-5472610, -14000000}, + {-5472610, -13477400}, {-5255280, -12454900}, + {-5052740, -12000000}, {-4830130, -11500000}, + {-4215720, -10654300}, {-3438930, -9954910}, + {-3189080, -9810670}, {-2533680, -9432270}, + {-1539560, -9109260}, {-500000, -9000000}, + {539558, -9109260}, {1309400, -9359400}, + {1533680, -9432270}, {2438930, -9954910}, + {3215720, -10654300}, {3830130, -11500000}, + {4052740, -12000000}, {4255280, -12454900}, + {4472610, -13477400}, {4472610, -14000000}, + {4500000, -14000000}, {4500000, -18000000}, + {4472610, -18000000}, {4472610, -18522600}, + {4255280, -19545100}, {4012570, -20090200}, + {3830130, -20500000}, {3806270, -20532800}, + {3215720, -21345700}, {2590280, -21908800}, + {2438930, -22045100}, {1533680, -22567700}, + {539558, -22890700}, {-499999, -23000000}, + }, + { + {-832658, -28565000}, {-1150780, -28461700}, + {-1440460, -28294400}, {-1689030, -28070600}, + {-1885640, -27800000}, {-2021689, -27494400}, + {-2091229, -27167200}, {-2091229, -26832800}, + {-2021689, -26505600}, {-1885640, -26200000}, + {-1689030, -25929400}, {-1440460, -25705600}, + {-1150780, -25538300}, {-832658, -25435000}, + {-500000, -25400000}, {-167341, -25435000}, + {150778, -25538300}, {440456, -25705600}, + {689032, -25929400}, {885640, -26200000}, + {1021690, -26505600}, {1091230, -26832800}, + {1091230, -27167200}, {1021690, -27494400}, + {885640, -27800000}, {689032, -28070600}, + {440456, -28294400}, {150778, -28461700}, + {-167341, -28565000}, {-499999, -28600000}, + }, + { + {9396390, -37646700}, {9190820, -37620800}, + {8990120, -37569200}, {8797460, -37493000}, + {8615890, -37393100}, {8448250, -37271300}, + {8297200, -37129500}, {8165120, -36969800}, + {8054089, -36794900}, {7965870, -36607400}, + {7901840, -36410300}, {7863010, -36206800}, + {7850000, -36000000}, {7863010, -35793200}, + {7901840, -35589700}, {7965870, -35392600}, + {8054089, -35205100}, {8165120, -35030200}, + {8297200, -34870500}, {8448250, -34728700}, + {8615890, -34606900}, {8797460, -34507000}, + {8990120, -34430800}, {9190820, -34379200}, + {9396390, -34353300}, {9603600, -34353300}, + {9809180, -34379200}, {10009900, -34430800}, + {10202500, -34507000}, {10384100, -34606900}, + {10551700, -34728700}, {10702800, -34870500}, + {10834900, -35030200}, {10945900, -35205100}, + {11034100, -35392600}, {11098200, -35589700}, + {11137000, -35793200}, {11150000, -36000000}, + {11137000, -36206800}, {11098200, -36410300}, + {11034100, -36607400}, {10945900, -36794900}, + {10834900, -36969800}, {10702800, -37129500}, + {10551700, -37271300}, {10384100, -37393100}, + {10202500, -37493000}, {10009900, -37569200}, + {9809180, -37620800}, {9603600, -37646700}, + }, + { + {-10603600, -37646700}, {-10809200, -37620800}, + {-11009900, -37569200}, {-11202500, -37493000}, + {-11384100, -37393100}, {-11551700, -37271300}, + {-11702800, -37129500}, {-11834900, -36969800}, + {-11945900, -36794900}, {-12034100, -36607400}, + {-12098200, -36410300}, {-12137000, -36206800}, + {-12150000, -36000000}, {-12137000, -35793200}, + {-12098200, -35589700}, {-12034100, -35392600}, + {-11945900, -35205100}, {-11834900, -35030200}, + {-11702800, -34870500}, {-11551700, -34728700}, + {-11384100, -34606900}, {-11202500, -34507000}, + {-11009900, -34430800}, {-10809200, -34379200}, + {-10603600, -34353300}, {-10396400, -34353300}, + {-10190800, -34379200}, {-9990120, -34430800}, + {-9797460, -34507000}, {-9615890, -34606900}, + {-9448250, -34728700}, {-9297200, -34870500}, + {-9165120, -35030200}, {-9054090, -35205100}, + {-8965870, -35392600}, {-8901840, -35589700}, + {-8863010, -35793200}, {-8850000, -36000000}, + {-8863010, -36206800}, {-8901840, -36410300}, + {-8965870, -36607400}, {-9054090, -36794900}, + {-9165120, -36969800}, {-9297200, -37129500}, + {-9448250, -37271300}, {-9615890, -37393100}, + {-9797460, -37493000}, {-9990120, -37569200}, + {-10190800, -37620800}, {-10396400, -37646700}, + }, + }}, + }, + ExPolygons{ + // "extruder-idler-plug.stl": + MyPoly{{ + {-13000000, 42500000}, {-12967200, 42811900}, + {-12906100, 43000000}, {-12870300, 43110100}, + {-12713500, 43381700}, {-12503700, 43614700}, + {-12250000, 43799000}, {-11963500, 43926600}, + {-11656800, 43991800}, {-11343200, 43991800}, + {-11036500, 43926600}, {-10750000, 43799000}, + {-10496300, 43614700}, {-10286500, 43381700}, + {-10129700, 43110100}, {-10093900, 43000000}, + {-10032800, 42811900}, {-10000000, 42500000}, + {-10000000, 40200000}, {-7000000, 40200000}, + {-7000000, 46000000}, {-6400000, 46000000}, + {-6400000, 50500000}, {-11937800, 50500000}, + {-17000000, 47577400}, {-17000000, 40200000}, + {-13000000, 40200000}, + }, + {}}, + }, + ExPolygons{ + // "z-axis-bottom.stl": + MyPoly{{ + {45101600, -4898420}, + {50000000, 0}, + {50000000, 40786800}, + {43286800, 47500000}, + {3500000, 47500000}, + {0, 44000000}, + {0, -2000000}, + {3000000, -5000000}, + {45000000, -5000000}, + }, + { + { + {13696400, 33853300}, {13509500, 33876900}, + {13490800, 33879200}, {13308400, 33926100}, + {13290100, 33930800}, {13097500, 34007000}, + {12915900, 34106900}, {12748300, 34228700}, + {12597200, 34370500}, {12465100, 34530200}, + {12354100, 34705100}, {12265900, 34892600}, + {12201800, 35089700}, {12163000, 35293200}, + {12150000, 35500000}, {12163000, 35706800}, + {12201800, 35910300}, {12265900, 36107400}, + {12354100, 36294900}, {12465100, 36469800}, + {12597200, 36629500}, {12748300, 36771300}, + {12915900, 36893100}, {13097500, 36993000}, + {13290100, 37069200}, {13308400, 37073900}, + {13490800, 37120800}, {13509500, 37123100}, + {13696400, 37146700}, {13903600, 37146700}, + {14090500, 37123100}, {14109200, 37120800}, + {14291600, 37073900}, {14309900, 37069200}, + {14502500, 36993000}, {14684100, 36893100}, + {14791899, 36814806}, {14851700, 36771300}, + {14851800, 36771300}, {15002800, 36629500}, + {15134900, 36469800}, {15245900, 36294900}, + {15334100, 36107400}, {15398200, 35910300}, + {15437000, 35706800}, {15450000, 35500000}, + {15437000, 35293200}, {15398200, 35089700}, + {15334100, 34892600}, {15245900, 34705100}, + {15134900, 34530200}, {15002800, 34370500}, + {14851800, 34228700}, {14851700, 34228700}, + {14791899, 34185194}, {14684100, 34106900}, + {14502500, 34007000}, {14309900, 33930800}, + {14291600, 33926100}, {14109200, 33879200}, + {14090500, 33876900}, {13903600, 33853300}, + }, + { + {44696400, 33853300}, {44509500, 33876900}, + {44490800, 33879200}, {44308400, 33926100}, + {44290100, 33930800}, {44097500, 34007000}, + {43915900, 34106900}, {43748200, 34228700}, + {43597200, 34370500}, {43465100, 34530200}, + {43354100, 34705100}, {43265900, 34892600}, + {43201800, 35089700}, {43163000, 35293200}, + {43150000, 35500000}, {43163000, 35706800}, + {43201800, 35910300}, {43265900, 36107400}, + {43354100, 36294900}, {43465100, 36469800}, + {43597200, 36629500}, {43748200, 36771300}, + {43915900, 36893100}, {44097500, 36993000}, + {44290100, 37069200}, {44308400, 37073900}, + {44490800, 37120800}, {44509500, 37123100}, + {44696400, 37146700}, {44903600, 37146700}, + {45090500, 37123100}, {45109200, 37120800}, + {45291600, 37073900}, {45309900, 37069200}, + {45502500, 36993000}, {45684100, 36893100}, + {45851700, 36771300}, {46002800, 36629500}, + {46134900, 36469800}, {46245900, 36294900}, + {46334100, 36107400}, {46398200, 35910300}, + {46437000, 35706800}, {46450000, 35500000}, + {46437000, 35293200}, {46398200, 35089700}, + {46334100, 34892600}, {46245900, 34705100}, + {46134900, 34530200}, {46002800, 34370500}, + {45851700, 34228700}, {45684100, 34106900}, + {45502500, 34007000}, {45309900, 33930800}, + {45291600, 33926100}, {45109200, 33879200}, + {45090500, 33876900}, {44903600, 33853300}, + }, + { + {28300000, 8702230}, {28300000, 8861350}, + {28129300, 8861350}, {25839000, 9348170}, + {23700000, 10300500}, {21805700, 11676800}, + {20239000, 13416800}, {19068300, 15444600}, + {18344700, 17671400}, {18100000, 20000000}, + {18344700, 22328600}, {19068300, 24555500}, + {20239000, 26583200}, {21805700, 28323200}, + {23700000, 29699500}, {25839000, 30651800}, + {28129300, 31138600}, {30470700, 31138600}, + {32761002, 30651800}, {34900000, 29699500}, + {36794300, 28323200}, {38361000, 26583200}, + {39531700, 24555500}, {40255300, 22328600}, + {40500000, 20000000}, {40255300, 17671400}, + {39531700, 15444600}, {38361000, 13416800}, + {36794300, 11676800}, {34900000, 10300500}, + {32761002, 9348170}, {30470700, 8861350}, + {30300000, 8861350}, {30300000, 8702230}, + }, + { + {29045700, -1042009}, {28541100, -978263}, + {28048500, -851778}, {27575600, -664549}, + {27129900, -419528}, {26718400, -120578}, + {26347700, 227584}, {26023500, 619470}, + {25751000, 1048900}, {25534400, 1509100}, + {25377200, 1992810}, {25281900, 2492400}, + {25250000, 3000000}, {25281900, 3507600}, + {25377200, 4007190}, {25534400, 4490900}, + {25751000, 4951100}, {26023500, 5380530}, + {26347700, 5772420}, {26718400, 6120580}, + {27129900, 6419530}, {27575600, 6664550}, + {28048500, 6851780}, {28300000, 6916360}, + {28300000, 7213590}, {28487100, 7261610}, + {28717100, 7290670}, {29027600, 7329900}, + {29572400, 7329900}, {29882900, 7290670}, + {30112900, 7261610}, {30300000, 7213590}, + {30300000, 6916360}, {30551500, 6851780}, + {31024400, 6664550}, {31470100, 6419530}, + {31881600, 6120580}, {32252300, 5772420}, + {32576500, 5380530}, {32849000, 4951100}, + {33065602, 4490900}, {33222802, 4007190}, + {33318100, 3507600}, {33349998, 3000000}, + {33318100, 2492400}, {33222802, 1992810}, + {33065602, 1509100}, {32849000, 1048900}, + {32576500, 619470}, {32252300, 227584}, + {31881600, -120578}, {31470100, -419528}, + {31024400, -664549}, {30551500, -851778}, + {30058900, -978263}, {29554300, -1042009}, + }, + { + {44696400, 2853260}, {44509500, 2876870}, + {44490800, 2879230}, {44308400, 2926070}, + {44290100, 2930760}, {44097500, 3007040}, + {43915900, 3106860}, {43748200, 3228650}, + {43597200, 3370500}, {43465100, 3530160}, + {43354100, 3705110}, {43265900, 3892600}, + {43201800, 4089660}, {43163000, 4293200}, + {43150000, 4500000}, {43163000, 4706800}, + {43201800, 4910340}, {43265900, 5107410}, + {43354100, 5294890}, {43465100, 5469850}, + {43597200, 5629500}, {43748200, 5771350}, + {43915900, 5893140}, {44097500, 5992960}, + {44290100, 6069240}, {44308400, 6073930}, + {44490800, 6120770}, {44696400, 6146740}, + {44903600, 6146740}, {45109200, 6120770}, + {45291600, 6073930}, {45309900, 6069240}, + {45502500, 5992960}, {45684100, 5893140}, + {45851700, 5771350}, {46002800, 5629500}, + {46134900, 5469850}, {46245900, 5294890}, + {46334100, 5107410}, {46398200, 4910340}, + {46437000, 4706800}, {46450000, 4500000}, + {46437000, 4293200}, {46398200, 4089660}, + {46334100, 3892600}, {46245900, 3705110}, + {46134900, 3530160}, {46002800, 3370500}, + {45851700, 3228650}, {45684100, 3106860}, + {45502500, 3007040}, {45309900, 2930760}, + {45291600, 2926070}, {45109200, 2879230}, + {45090500, 2876870}, {44903600, 2853260}, + }, + { + {13696400, 2853260}, {13509500, 2876870}, + {13490800, 2879230}, {13308400, 2926070}, + {13290100, 2930760}, {13097500, 3007040}, + {12915900, 3106860}, {12748300, 3228650}, + {12597200, 3370500}, {12465100, 3530160}, + {12354100, 3705110}, {12265900, 3892600}, + {12201800, 4089660}, {12163000, 4293200}, + {12150000, 4500000}, {12163000, 4706800}, + {12201800, 4910340}, {12265900, 5107410}, + {12354100, 5294890}, {12465100, 5469850}, + {12597200, 5629500}, {12748300, 5771350}, + {12915900, 5893140}, {13097500, 5992960}, + {13290100, 6069240}, {13308400, 6073930}, + {13490800, 6120770}, {13696400, 6146740}, + {13903600, 6146740}, {14109200, 6120770}, + {14291600, 6073930}, {14309900, 6069240}, + {14502500, 5992960}, {14684100, 5893140}, + {14754724, 5841850}, {14851700, 5771350}, + {14851800, 5771350}, {15002800, 5629500}, + {15134900, 5469850}, {15245900, 5294890}, + {15334100, 5107410}, {15398200, 4910340}, + {15437000, 4706800}, {15450000, 4500000}, + {15437000, 4293200}, {15398200, 4089660}, + {15334100, 3892600}, {15245900, 3705110}, + {15134900, 3530160}, {15002800, 3370500}, + {14851800, 3228650}, {14851700, 3228650}, + {14754724, 3158150}, {14684100, 3106860}, + {14502500, 3007040}, {14309900, 2930760}, + {14291600, 2926070}, {14109200, 2879230}, + {14090500, 2876870}, {13903600, 2853260}, + }, + }}, + MyPoly{{ + {50000000, -53786800}, + {50000000, -13000000}, + {45101600, -8101579}, + {45000000, -8000000}, + {3000000, -8000000}, + {0, -11000000}, + {0, -57000000}, + {3500000, -60500000}, + {43286800, -60500000}, + }, + { + { + {29027600, -20329900}, {28717100, -20290700}, + {28487100, -20261600}, {28300000, -20213600}, + {28300000, -19916400}, {28048500, -19851800}, + {27575600, -19664500}, {27129900, -19419500}, + {26718400, -19120600}, {26347700, -18772400}, + {26023500, -18380500}, {25751000, -17951100}, + {25534400, -17490900}, {25377200, -17007200}, + {25281900, -16507601}, {25250000, -16000000}, + {25281900, -15492400}, {25377200, -14992800}, + {25534400, -14509100}, {25751000, -14048900}, + {26023500, -13619500}, {26347700, -13227600}, + {26718400, -12879400}, {27129900, -12580500}, + {27575600, -12335500}, {28048500, -12148200}, + {28541100, -12021700}, {29045700, -11958000}, + {29554300, -11958000}, {30058900, -12021700}, + {30551500, -12148200}, {31024400, -12335500}, + {31470100, -12580500}, {31881600, -12879400}, + {32252300, -13227600}, {32576500, -13619500}, + {32849000, -14048900}, {33065602, -14509100}, + {33222802, -14992800}, {33318100, -15492400}, + {33349998, -16000000}, {33318100, -16507601}, + {33222802, -17007200}, {33065602, -17490900}, + {32849000, -17951100}, {32576500, -18380500}, + {32252300, -18772400}, {31881600, -19120600}, + {31470100, -19419500}, {31024400, -19664500}, + {30551500, -19851800}, {30300000, -19916400}, + {30300000, -20213600}, {30112900, -20261600}, + {29882900, -20290700}, {29572400, -20329900}, + }, + { + {13696400, -19146700}, {13509500, -19123100}, + {13490800, -19120800}, {13308400, -19073900}, + {13290100, -19069200}, {13097500, -18993000}, + {12915900, -18893100}, {12748300, -18771300}, + {12597200, -18629500}, {12465100, -18469800}, + {12354100, -18294900}, {12265900, -18107400}, + {12201800, -17910300}, {12163000, -17706800}, + {12150000, -17500000}, {12163000, -17293200}, + {12201800, -17089700}, {12265900, -16892600}, + {12354100, -16705099}, {12465100, -16530199}, + {12597200, -16370501}, {12748300, -16228701}, + {12915900, -16106899}, {13097500, -16007000}, + {13290100, -15930800}, {13308400, -15926100}, + {13490800, -15879200}, {13509500, -15876900}, + {13696400, -15853300}, {13903600, -15853300}, + {14090500, -15876900}, {14109200, -15879200}, + {14291600, -15926100}, {14309900, -15930800}, + {14502500, -16007000}, {14684100, -16106899}, + {14791305, -16184763}, {14851700, -16228701}, + {14851800, -16228701}, {15002800, -16370501}, + {15134900, -16530199}, {15245900, -16705099}, + {15334100, -16892600}, {15398200, -17089700}, + {15437000, -17293200}, {15450000, -17500000}, + {15437000, -17706800}, {15398200, -17910300}, + {15334100, -18107400}, {15245900, -18294900}, + {15134900, -18469800}, {15002800, -18629500}, + {14851800, -18771300}, {14851700, -18771300}, + {14791899, -18814806}, {14684100, -18893100}, + {14502500, -18993000}, {14309900, -19069200}, + {14291600, -19073900}, {14109200, -19120800}, + {14090500, -19123100}, {13903600, -19146700}, + }, + { + {44696400, -19146700}, {44509500, -19123100}, + {44490800, -19120800}, {44308400, -19073900}, + {44290100, -19069200}, {44097500, -18993000}, + {43915900, -18893100}, {43748200, -18771300}, + {43597200, -18629500}, {43465100, -18469800}, + {43354100, -18294900}, {43265900, -18107400}, + {43201800, -17910300}, {43163000, -17706800}, + {43150000, -17500000}, {43163000, -17293200}, + {43201800, -17089700}, {43265900, -16892600}, + {43354100, -16705099}, {43465100, -16530199}, + {43597200, -16370501}, {43748200, -16228701}, + {43915900, -16106899}, {44097500, -16007000}, + {44290100, -15930800}, {44308400, -15926100}, + {44490800, -15879200}, {44509500, -15876900}, + {44696400, -15853300}, {44903600, -15853300}, + {45090500, -15876900}, {45109200, -15879200}, + {45291600, -15926100}, {45309900, -15930800}, + {45502500, -16007000}, {45684100, -16106899}, + {45851700, -16228701}, {46002800, -16370501}, + {46134900, -16530199}, {46245900, -16705099}, + {46334100, -16892600}, {46398200, -17089700}, + {46437000, -17293200}, {46450000, -17500000}, + {46437000, -17706800}, {46398200, -17910300}, + {46334100, -18107400}, {46245900, -18294900}, + {46134900, -18469800}, {46002800, -18629500}, + {45851700, -18771300}, {45684100, -18893100}, + {45502500, -18993000}, {45309900, -19069200}, + {45291600, -19073900}, {45109200, -19120800}, + {45090500, -19123100}, {44903600, -19146700}, + }, + { + {28129300, -44138600}, {25839000, -43651800}, + {23700000, -42699500}, {21805700, -41323200}, + {20239000, -39583200}, {19068300, -37555500}, + {18344700, -35328600}, {18100000, -33000000}, + {18344700, -30671400}, {19068300, -28444500}, + {20239000, -26416800}, {21805700, -24676800}, + {23700000, -23300500}, {25839000, -22348200}, + {28129300, -21861400}, {28300000, -21861400}, + {28300000, -21702200}, {30300000, -21702200}, + {30300000, -21861400}, {30470700, -21861400}, + {32761002, -22348200}, {34900000, -23300500}, + {36794300, -24676800}, {38361000, -26416800}, + {39531700, -28444500}, {40255300, -30671400}, + {40500000, -33000000}, {40255300, -35328600}, + {39531700, -37555500}, {38361000, -39583200}, + {36794300, -41323200}, {34900000, -42699500}, + {32761002, -43651800}, {30470700, -44138600}, + }, + { + {44696400, -50146700}, {44509500, -50123100}, + {44490800, -50120800}, {44308400, -50073900}, + {44290100, -50069200}, {44097500, -49993000}, + {43915900, -49893100}, {43748200, -49771300}, + {43597200, -49629500}, {43465100, -49469800}, + {43354100, -49294900}, {43265900, -49107400}, + {43201800, -48910300}, {43163000, -48706800}, + {43150000, -48500000}, {43163000, -48293200}, + {43201800, -48089700}, {43265900, -47892600}, + {43354100, -47705100}, {43465100, -47530200}, + {43597200, -47370500}, {43748200, -47228700}, + {43915900, -47106900}, {44097500, -47007000}, + {44290100, -46930800}, {44308400, -46926100}, + {44490800, -46879200}, {44509500, -46876900}, + {44696400, -46853300}, {44903600, -46853300}, + {45090500, -46876900}, {45109200, -46879200}, + {45291600, -46926100}, {45309900, -46930800}, + {45502500, -47007000}, {45684100, -47106900}, + {45851700, -47228700}, {46002800, -47370500}, + {46134900, -47530200}, {46245900, -47705100}, + {46334100, -47892600}, {46398200, -48089700}, + {46437000, -48293200}, {46450000, -48500000}, + {46437000, -48706800}, {46398200, -48910300}, + {46334100, -49107400}, {46245900, -49294900}, + {46134900, -49469800}, {46002800, -49629500}, + {45851700, -49771300}, {45684100, -49893100}, + {45502500, -49993000}, {45309900, -50069200}, + {45291600, -50073900}, {45109200, -50120800}, + {45090500, -50123100}, {44903600, -50146700}, + }, + { + {13696400, -50146700}, {13509500, -50123100}, + {13490800, -50120800}, {13308400, -50073900}, + {13290100, -50069200}, {13097500, -49993000}, + {12915900, -49893100}, {12748300, -49771300}, + {12597200, -49629500}, {12465100, -49469800}, + {12354100, -49294900}, {12265900, -49107400}, + {12201800, -48910300}, {12163000, -48706800}, + {12150000, -48500000}, {12163000, -48293200}, + {12201800, -48089700}, {12265900, -47892600}, + {12354100, -47705100}, {12465100, -47530200}, + {12597200, -47370500}, {12748300, -47228700}, + {12915900, -47106900}, {13097500, -47007000}, + {13290100, -46930800}, {13308400, -46926100}, + {13490800, -46879200}, {13509500, -46876900}, + {13696400, -46853300}, {13903600, -46853300}, + {14090500, -46876900}, {14109200, -46879200}, + {14291600, -46926100}, {14309900, -46930800}, + {14502500, -47007000}, {14684100, -47106900}, + {14791899, -47185194}, {14851700, -47228700}, + {14851800, -47228700}, {15002800, -47370500}, + {15134900, -47530200}, {15245900, -47705100}, + {15334100, -47892600}, {15398200, -48089700}, + {15437000, -48293200}, {15450000, -48500000}, + {15437000, -48706800}, {15398200, -48910300}, + {15334100, -49107400}, {15245900, -49294900}, + {15134900, -49469800}, {15002800, -49629500}, + {14851800, -49771300}, {14851700, -49771300}, + {14791899, -49814806}, {14684100, -49893100}, + {14502500, -49993000}, {14309900, -50069200}, + {14291600, -50073900}, {14109200, -50120800}, + {14090500, -50123100}, {13903600, -50146700}, + }, + }}, + }, + ExPolygons{ + // "extruder-cover.stl": + MyPoly{{ + {20500000, 366025}, {21732100, 2500000}, + {24500000, 2500000}, {24500000, -1500000}, + {31500000, -1500000}, {31500000, 1500000}, + {27250000, 5750000}, {-17000000, 5750000}, + {-17000000, -26799100}, {-35109600, -33390400}, + {-40700000, -33390400}, {-43650000, -38500000}, + {-40700000, -43609600}, {-34800000, -43609600}, + {-33470820, -41307370}, {-17000000, -35312500}, + {-17000000, -36500000}, {-15000000, -36500000}, + {-15000000, -44000000}, {20500000, -44000000}, + }, + { + { + {16832800, 1408760}, {16667299, 1434960}, + {16505600, 1478310}, {16349199, 1538330}, + {16200001, 1614360}, {16059500, 1705570}, + {15929400, 1810970}, {15811000, 1929390}, + {15705600, 2059540}, {15614400, 2200000}, + {15538300, 2349220}, {15478300, 2505570}, + {15435000, 2667340}, {15408800, 2832750}, + {15400000, 3000000}, {15408800, 3167240}, + {15435000, 3332660}, {15478300, 3494430}, + {15538300, 3650780}, {15614400, 3800000}, + {15705600, 3940460}, {15811000, 4070610}, + {15929400, 4189030}, {16059500, 4294430}, + {16200001, 4385640}, {16349199, 4461670}, + {16505600, 4521690}, {16667299, 4565040}, + {16832800, 4591230}, {17000000, 4600000}, + {17167200, 4591230}, {17332700, 4565040}, + {17494400, 4521690}, {17650800, 4461670}, + {17800000, 4385640}, {17940500, 4294430}, + {18070600, 4189030}, {18189000, 4070610}, + {18294400, 3940460}, {18385600, 3800000}, + {18461700, 3650780}, {18521700, 3494430}, + {18565000, 3332660}, {18591200, 3167240}, + {18600000, 3000000}, {18591200, 2832750}, + {18565000, 2667340}, {18521700, 2505570}, + {18461700, 2349220}, {18385600, 2200000}, + {18294400, 2059540}, {18189000, 1929390}, + {18070600, 1810970}, {17940500, 1705570}, + {17800000, 1614360}, {17650800, 1538330}, + {17494400, 1478310}, {17332700, 1434960}, + {17167200, 1408760}, {17000000, 1400000}, + }, + { + {-11603600, -2146740}, {-11809200, -2120770}, + {-12009900, -2069240}, {-12202500, -1992960}, + {-12384100, -1893140}, {-12551700, -1771350}, + {-12702800, -1629500}, {-12834900, -1469840}, + {-12945900, -1294890}, {-13034100, -1107400}, + {-13098200, -910337}, {-13137000, -706800}, + {-13150000, -499999}, {-13137000, -293200}, + {-13098200, -89661}, {-13034100, 107405}, + {-12945900, 294893}, {-12834900, 469845}, + {-12702800, 629502}, {-12551700, 771346}, + {-12384100, 893141}, {-12202500, 992964}, + {-12009900, 1069240}, {-11809200, 1120770}, + {-11603600, 1146740}, {-11396400, 1146740}, + {-11190800, 1120770}, {-10990100, 1069240}, + {-10797500, 992964}, {-10615900, 893141}, + {-10448200, 771346}, {-10297200, 629502}, + {-10165100, 469845}, {-10054100, 294893}, + {-9965870, 107405}, {-9901840, -89661}, + {-9863010, -293200}, {-9850000, -500000}, + {-9863010, -706800}, {-9901840, -910337}, + {-9965870, -1107400}, {-10054100, -1294890}, + {-10165100, -1469840}, {-10297200, -1629500}, + {-10448200, -1771350}, {-10615900, -1893140}, + {-10797500, -1992960}, {-10990100, -2069240}, + {-11190800, -2120770}, {-11396400, -2146740}, + }, + { + {-37917200, -40091200}, {-38244400, -40021700}, + {-38550000, -39885600}, {-38820600, -39689000}, + {-39044400, -39440500}, {-39211700, -39150800}, + {-39315000, -38832700}, {-39350000, -38500000}, + {-39315000, -38167300}, {-39211700, -37849200}, + {-39044400, -37559500}, {-38820600, -37311000}, + {-38550000, -37114400}, {-38244400, -36978300}, + {-37917200, -36908800}, {-37582800, -36908800}, + {-37255600, -36978300}, {-36950000, -37114400}, + {-36679400, -37311000}, {-36455600, -37559500}, + {-36288300, -37849200}, {-36185000, -38167300}, + {-36150000, -38500000}, {-36185000, -38832700}, + {-36288300, -39150800}, {-36455600, -39440500}, + {-36679400, -39689000}, {-36950000, -39885600}, + {-37255600, -40021700}, {-37582800, -40091200}, + }, + { + {14353700, -41892300}, {14067400, -41831500}, + {13800000, -41712400}, {13563200, -41540400}, + {13367400, -41322900}, {13221000, -41069400}, + {13130600, -40791100}, {13100000, -40500000}, + {13130600, -40208900}, {13221000, -39930600}, + {13367400, -39677100}, {13563200, -39459600}, + {13800000, -39287600}, {14067400, -39168500}, + {14353700, -39107700}, {14646300, -39107700}, + {14932600, -39168500}, {15200000, -39287600}, + {15436800, -39459600}, {15632600, -39677100}, + {15779000, -39930600}, {15869400, -40208900}, + {15900000, -40500000}, {15869400, -40791100}, + {15779000, -41069400}, {15632600, -41322900}, + {15436800, -41540400}, {15200000, -41712400}, + {14932600, -41831500}, {14646300, -41892300}, + }, + }}, + }, + ExPolygons{ + // "Einsy-base.stl": + MyPoly{{ + {85000000, 2000000}, + {87000000, 5464100}, + {91000000, 5464100}, + {93000000, 2000000}, + {91845296, 0}, + {118500000, 0}, + {118500000, 79000000}, + {105500000, 92000000}, + {0, 92000000}, + {0, 41000000}, + {-5000000, 41000000}, + {-9000000, 38000000}, + {-9000000, 18000000}, + {-5000000, 15000000}, + {0, 15000000}, + {0, 0}, + {86154704, 0}, + }, + { + { + {58301400, 86110400}, {57912900, 86193000}, + {57550000, 86354600}, {57228700, 86588000}, + {56962900, 86883200}, {56764300, 87227200}, + {56641500, 87605000}, {56600000, 88000000}, + {56641500, 88395000}, {56764300, 88772800}, + {56962900, 89116800}, {57228700, 89412000}, + {57550000, 89645400}, {57912900, 89807000}, + {58301400, 89889600}, {58698600, 89889600}, + {59087100, 89807000}, {59450000, 89645400}, + {59771300, 89412000}, {60037100, 89116800}, + {60235700, 88772800}, {60358500, 88395000}, + {60400000, 88000000}, {60358500, 87605000}, + {60235700, 87227200}, {60037100, 86883200}, + {59771300, 86588000}, {59450000, 86354600}, + {59087100, 86193000}, {58698600, 86110400}, + }, + { + {78916400, 80204400}, {78752800, 80239200}, + {78600000, 80307200}, {78464696, 80405504}, + {78352800, 80529800}, {78269200, 80674600}, + {78217496, 80833704}, {78200000, 81000000}, + {78217496, 81166296}, {78269200, 81325400}, + {78352800, 81470200}, {78464696, 81594496}, + {78600000, 81692800}, {78752800, 81760800}, + {78916400, 81795600}, {79083600, 81795600}, + {79247200, 81760800}, {79400000, 81692800}, + {79535304, 81594496}, {79647200, 81470200}, + {79730800, 81325400}, {79782504, 81166296}, + {79800000, 81000000}, {79782504, 80833704}, + {79730800, 80674600}, {79647200, 80529800}, + {79535304, 80405504}, {79400000, 80307200}, + {79247200, 80239200}, {79083600, 80204400}, + }, + { + {20916400, 80204400}, {20752800, 80239200}, + {20600000, 80307200}, {20464700, 80405504}, + {20352800, 80529800}, {20269200, 80674600}, + {20217500, 80833704}, {20200000, 81000000}, + {20217500, 81166296}, {20269200, 81325400}, + {20352800, 81470200}, {20464700, 81594496}, + {20600000, 81692800}, {20752800, 81760800}, + {20916400, 81795600}, {21083600, 81795600}, + {21247200, 81760800}, {21400000, 81692800}, + {21535300, 81594496}, {21647200, 81470200}, + {21730800, 81325400}, {21782500, 81166296}, + {21800000, 81000000}, {21782500, 80833704}, + {21730800, 80674600}, {21647200, 80529800}, + {21535300, 80405504}, {21400000, 80307200}, + {21247200, 80239200}, {21083600, 80204400}, + }, + { + {81000000, 60500000}, + {81000000, 78500000}, + {84650000, 78500000}, + {84650000, 60500000}, + }, + { + {70000000, 60500000}, + {70000000, 78500000}, + {73650000, 78500000}, + {73650000, 60500000}, + }, + { + {75500000, 60500000}, + {75500000, 78500000}, + {79150000, 78500000}, + {79150000, 60500000}, + }, + { + {26000000, 60500000}, + {26000000, 78500000}, + {29650000, 78500000}, + {29650000, 60500000}, + }, + { + {86500000, 60500000}, + {86500000, 78500000}, + {90150000, 78500000}, + {90150000, 60500000}, + }, + { + {48000000, 60500000}, + {48000000, 78500000}, + {51650000, 78500000}, + {51650000, 60500000}, + }, + { + {64500000, 60500000}, + {64500000, 78500000}, + {68150000, 78500000}, + {68150000, 60500000}, + }, + { + {59000000, 60500000}, + {59000000, 78500000}, + {62650000, 78500000}, + {62650000, 60500000}, + }, + { + {20500000, 60500000}, + {20500000, 78500000}, + {24150000, 78500000}, + {24150000, 60500000}, + }, + { + {92000000, 60500000}, + {92000000, 78500000}, + {95650000, 78500000}, + {95650000, 60500000}, + }, + { + {42500000, 60500000}, + {42500000, 78500000}, + {46150000, 78500000}, + {46150000, 60500000}, + }, + { + {31500000, 60500000}, + {31500000, 78500000}, + {35150000, 78500000}, + {35150000, 60500000}, + }, + { + {37000000, 60500000}, + {37000000, 78500000}, + {40650000, 78500000}, + {40650000, 60500000}, + }, + { + {53500000, 60500000}, + {53500000, 78500000}, + {57150000, 78500000}, + {57150000, 60500000}, + }, + { + {7301400, 73110400}, {6912870, 73193000}, + {6550000, 73354600}, {6228650, 73588000}, + {5962870, 73883200}, {5900000, 73992104}, + {5764260, 74227200}, {5641520, 74605000}, + {5600000, 75000000}, {5641520, 75395000}, + {5764260, 75772800}, {5900000, 76007896}, + {5962870, 76116800}, {6228650, 76412000}, + {6550000, 76645400}, {6912870, 76807000}, + {7301400, 76889600}, {7698600, 76889600}, + {8087129, 76807000}, {8450000, 76645400}, + {8771350, 76412000}, {9037130, 76116800}, + {9100000, 76007896}, {9235740, 75772800}, + {9358480, 75395000}, {9400000, 75000000}, + {9358480, 74605000}, {9235740, 74227200}, + {9100000, 73992104}, {9037130, 73883200}, + {8771350, 73588000}, {8450000, 73354600}, + {8087129, 73193000}, {7698600, 73110400}, + }, + { + {102301000, 73110400}, {101913000, 73193000}, + {101550000, 73354600}, {101229000, 73588000}, + {100963000, 73883200}, {100764000, 74227200}, + {100642000, 74605000}, {100600000, 75000000}, + {100642000, 75395000}, {100764000, 75772800}, + {100963000, 76116800}, {101229000, 76412000}, + {101550000, 76645400}, {101913000, 76807000}, + {102301000, 76889600}, {102699000, 76889600}, + {103087000, 76807000}, {103450000, 76645400}, + {103771000, 76412000}, {104037000, 76116800}, + {104236000, 75772800}, {104358000, 75395000}, + {104400000, 75000000}, {104358000, 74605000}, + {104236000, 74227200}, {104037000, 73883200}, + {103771000, 73588000}, {103450000, 73354600}, + {103087000, 73193000}, {102699000, 73110400}, + }, + { + {37000000, 35500000}, + {37000000, 53500000}, + {40650000, 53500000}, + {40650000, 35500000}, + }, + { + {53500000, 35500000}, + {53500000, 53500000}, + {57150000, 53500000}, + {57150000, 35500000}, + }, + { + {75500000, 35500000}, + {75500000, 53500000}, + {79150000, 53500000}, + {79150000, 35500000}, + }, + { + {31500000, 35500000}, + {31500000, 53500000}, + {35150000, 53500000}, + {35150000, 35500000}, + }, + { + {92000000, 35500000}, + {92000000, 53500000}, + {95650000, 53500000}, + {95650000, 35500000}, + }, + { + {81000000, 35500000}, + {81000000, 53500000}, + {84650000, 53500000}, + {84650000, 35500000}, + }, + { + {86500000, 35500000}, + {86500000, 53500000}, + {90150000, 53500000}, + {90150000, 35500000}, + }, + { + {48000000, 35500000}, + {48000000, 53500000}, + {51650000, 53500000}, + {51650000, 35500000}, + }, + { + {42500000, 35500000}, + {42500000, 53500000}, + {46150000, 53500000}, + {46150000, 35500000}, + }, + { + {70000000, 35500000}, + {70000000, 53500000}, + {73650000, 53500000}, + {73650000, 35500000}, + }, + { + {20500000, 35500000}, + {20500000, 53500000}, + {24150000, 53500000}, + {24150000, 35500000}, + }, + { + {59000000, 35500000}, + {59000000, 53500000}, + {62650000, 53500000}, + {62650000, 35500000}, + }, + { + {64500000, 35500000}, + {64500000, 53500000}, + {68150000, 53500000}, + {68150000, 35500000}, + }, + { + {26000000, 35500000}, + {26000000, 53500000}, + {29650000, 53500000}, + {29650000, 35500000}, + }, + { + {16290899, 8010959}, {15882000, 8097890}, + {15500000, 8267950}, {15161700, 8513710}, + {14882000, 8824430}, {14672900, 9186530}, + {14543700, 9584180}, {14500000, 10000000}, + {14500000, 34000000}, {14543700, 34415800}, + {14672900, 34813500}, {14882000, 35175600}, + {15161700, 35486300}, {15500000, 35732000}, + {15882000, 35902100}, {16290899, 35989000}, + {16709101, 35989000}, {17118000, 35902100}, + {17500000, 35732000}, {17838300, 35486300}, + {18118000, 35175600}, {18327100, 34813500}, + {18456300, 34415800}, {18500000, 34000000}, + {18500000, 10000000}, {18456300, 9584180}, + {18327100, 9186530}, {18118000, 8824430}, + {17838300, 8513710}, {17500000, 8267950}, + {17118000, 8097890}, {16709101, 8010959}, + }, + { + {59000000, 10500000}, + {59000000, 28500000}, + {62650000, 28500000}, + {62650000, 10500000}, + }, + { + {81000000, 10500000}, + {81000000, 28500000}, + {84650000, 28500000}, + {84650000, 10500000}, + }, + { + {75500000, 10500000}, + {75500000, 28500000}, + {79150000, 28500000}, + {79150000, 10500000}, + }, + { + {70000000, 10500000}, + {70000000, 28500000}, + {73650000, 28500000}, + {73650000, 10500000}, + }, + { + {20500000, 10500000}, + {20500000, 28500000}, + {24150000, 28500000}, + {24150000, 10500000}, + }, + { + {92000000, 10500000}, + {92000000, 28500000}, + {95650000, 28500000}, + {95650000, 10500000}, + }, + { + {26000000, 10500000}, + {26000000, 28500000}, + {29650000, 28500000}, + {29650000, 10500000}, + }, + { + {53500000, 10500000}, + {53500000, 28500000}, + {57150000, 28500000}, + {57150000, 10500000}, + }, + { + {48000000, 10500000}, + {48000000, 28500000}, + {51650000, 28500000}, + {51650000, 10500000}, + }, + { + {42500000, 10500000}, + {42500000, 28500000}, + {46150000, 28500000}, + {46150000, 10500000}, + }, + { + {37000000, 10500000}, + {37000000, 28500000}, + {40650000, 28500000}, + {40650000, 10500000}, + }, + { + {31500000, 10500000}, + {31500000, 28500000}, + {35150000, 28500000}, + {35150000, 10500000}, + }, + { + {64500000, 10500000}, + {64500000, 28500000}, + {68150000, 28500000}, + {68150000, 10500000}, + }, + { + {86500000, 10500000}, + {86500000, 28500000}, + {90150000, 28500000}, + {90150000, 10500000}, + }, + { + {102301000, 12110400}, {101913000, 12193000}, + {101550000, 12354600}, {101229000, 12588000}, + {100963000, 12883200}, {100764000, 13227200}, + {100642000, 13605000}, {100600000, 14000000}, + {100642000, 14395000}, {100764000, 14772800}, + {100963000, 15116800}, {101229000, 15412000}, + {101550000, 15645400}, {101913000, 15807000}, + {102301000, 15889600}, {102699000, 15889600}, + {103087000, 15807000}, {103450000, 15645400}, + {103771000, 15412000}, {104037000, 15116800}, + {104236000, 14772800}, {104358000, 14395000}, + {104400000, 14000000}, {104358000, 13605000}, + {104236000, 13227200}, {104037000, 12883200}, + {103771000, 12588000}, {103450000, 12354600}, + {103087000, 12193000}, {102699000, 12110400}, + }, + { + {7301400, 12110400}, {6912870, 12193000}, + {6550000, 12354600}, {6228650, 12588000}, + {5962870, 12883200}, {5900000, 12992100}, + {5764260, 13227200}, {5641520, 13605000}, + {5600000, 14000000}, {5641520, 14395000}, + {5764260, 14772800}, {5900000, 15007900}, + {5962870, 15116800}, {6228650, 15412000}, + {6550000, 15645400}, {6912870, 15807000}, + {7301400, 15889600}, {7698600, 15889600}, + {8087129, 15807000}, {8450000, 15645400}, + {8771350, 15412000}, {9037130, 15116800}, + {9100000, 15007900}, {9235740, 14772800}, + {9358480, 14395000}, {9400000, 14000000}, + {9358480, 13605000}, {9235740, 13227200}, + {9100000, 12992100}, {9037130, 12883200}, + {8771350, 12588000}, {8450000, 12354600}, + {8087129, 12193000}, {7698600, 12110400}, + }, + }}, + }, + ExPolygons{ + // "lcd-supports.stl": + MyPoly{{ + {4192390, 4192390}, {4192390, 5707110}, + {2474870, 7424620}, {1626350, 6576090}, + {3040560, 5161880}, {1767770, 3889090}, + {-2474870, 8131730}, {-5303300, 5303300}, + {-36769600, 36769600}, {-33941100, 39598000}, + {-38183750, 43840650}, {-36911000, 45113400}, + {-35496800, 43699200}, {-34648200, 44547700}, + {-36769600, 46669000}, {-38183800, 46669000}, + {-46852800, 38000000}, {-61500000, 38000000}, + {-61500000, 12000000}, {-50000000, 12000000}, + {-50000000, 11984300}, {-37204500, -811183}, + {-811183, -811183}, + }, + { + { + {-36000000, 8000000}, + {-51500000, 23500000}, + {-37357900, 23500000}, + {-21857900, 8000000}, + }, + }}, + MyPoly{{ + {-13147200, -40000000}, {1500000, -40000000}, + {1500000, -14000000}, {-10000000, -14000000}, + {-10000000, -13984300}, {-22795500, -1188820}, + {-59188800, -1188820}, {-64192400, -6192390}, + {-64192400, -7707110}, {-62474900, -9424620}, + {-61626300, -8576090}, {-63040571, -7161851}, + {-61767800, -5889090}, {-57525100, -10131700}, + {-54696700, -7303300}, {-23230400, -38769600}, + {-26058900, -41598000}, {-21816250, -45840650}, + {-23089000, -47113400}, {-24503200, -45699200}, + {-25351800, -46547700}, {-23230400, -48669000}, + {-21816200, -48669000}, + }, + { + { + {-22642100, -25500000}, + {-38142100, -10000000}, + {-24000000, -10000000}, + {-9357800, -24642200}, + {-9288210, -24711800}, + {-8500000, -25500000}, + }, + }}, + }, +}; diff --git a/tests/data/prusaparts.hpp b/tests/data/prusaparts.hpp new file mode 100644 index 0000000000..abe598b137 --- /dev/null +++ b/tests/data/prusaparts.hpp @@ -0,0 +1,14 @@ +#ifndef PRUSAPARTS_H +#define PRUSAPARTS_H + +#include +#include + +using TestData = std::vector; +using TestDataEx = std::vector; + +extern const TestData PRUSA_PART_POLYGONS; +extern const TestData PRUSA_STEGOSAUR_POLYGONS; +extern const TestDataEx PRUSA_PART_POLYGONS_EX; + +#endif // PRUSAPARTS_H diff --git a/tests/fff_print/test_data.cpp b/tests/fff_print/test_data.cpp index 6d87560579..8f9367ce51 100644 --- a/tests/fff_print/test_data.cpp +++ b/tests/fff_print/test_data.cpp @@ -228,7 +228,7 @@ void init_print(std::vector &&meshes, Slic3r::Print &print, Slic3r object->add_volume(std::move(t)); object->add_instance(); } - arrange_objects(model, InfiniteBed{}, ArrangeParams{ scaled(min_object_distance(config))}); + arrange_objects(model, arr2::InfiniteBed{}, arr2::ArrangeSettings{}.set_distance_from_objects(min_object_distance(config))); model.center_instances_around_point({100, 100}); for (ModelObject *mo : model.objects) { mo->ensure_on_bed(); diff --git a/tests/fff_print/test_model.cpp b/tests/fff_print/test_model.cpp index 23c4a2d9a0..d59a655663 100644 --- a/tests/fff_print/test_model.cpp +++ b/tests/fff_print/test_model.cpp @@ -42,7 +42,7 @@ SCENARIO("Model construction", "[Model]") { } } model_object->add_instance(); - arrange_objects(model, InfiniteBed{scaled(Vec2d(100, 100))}, ArrangeParams{scaled(min_object_distance(config))}); + arrange_objects(model, arr2::InfiniteBed{scaled(Vec2d(100, 100))}, arr2::ArrangeSettings{}.set_distance_from_objects(min_object_distance(config))); model_object->ensure_on_bed(); print.auto_assign_extruders(model_object); THEN("Print works?") { diff --git a/tests/libnest2d/CMakeLists.txt b/tests/libnest2d/CMakeLists.txt deleted file mode 100644 index 8dabc688d3..0000000000 --- a/tests/libnest2d/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -get_filename_component(_TEST_NAME ${CMAKE_CURRENT_LIST_DIR} NAME) -add_executable(${_TEST_NAME}_tests ${_TEST_NAME}_tests_main.cpp printer_parts.cpp printer_parts.hpp) - -# mold linker for successful linking needs also to link TBB library and link it before libslic3r. -target_link_libraries(${_TEST_NAME}_tests test_common TBB::tbb TBB::tbbmalloc libnest2d ) -set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER "tests") - -# catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ") -set(_catch_args "exclude:[NotWorking]") -list(APPEND _catch_args "${CATCH_EXTRA_ARGS}") -add_test(${_TEST_NAME}_tests ${_TEST_NAME}_tests ${_catch_args}) diff --git a/tests/libnest2d/libnest2d_tests_main.cpp b/tests/libnest2d/libnest2d_tests_main.cpp deleted file mode 100644 index f6df83b779..0000000000 --- a/tests/libnest2d/libnest2d_tests_main.cpp +++ /dev/null @@ -1,1233 +0,0 @@ -#include - -#include -#include - -#include -#include "printer_parts.hpp" -//#include -#include "../tools/svgtools.hpp" -#include - -#if defined(_MSC_VER) && defined(__clang__) -#define BOOST_NO_CXX17_HDR_STRING_VIEW -#endif - -#include "boost/multiprecision/integer.hpp" -#include "boost/rational.hpp" - -//#include "../tools/libnfpglue.hpp" -//#include "../tools/nfp_svgnest_glue.hpp" - -namespace libnest2d { -#if !defined(_MSC_VER) && defined(__SIZEOF_INT128__) && !defined(__APPLE__) -using LargeInt = __int128; -#else -using LargeInt = boost::multiprecision::int128_t; -template<> struct _NumTag { using Type = ScalarTag; }; -#endif -template struct _NumTag> { using Type = RationalTag; }; - -using RectangleItem = libnest2d::Rectangle; - -namespace nfp { - -template -struct NfpImpl -{ - NfpResult operator()(const S &sh, const S &other) - { - return nfpConvexOnly>(sh, other); - } -}; - -} -} - -namespace { -using namespace libnest2d; - -template -void exportSVG(const char *loc, It from, It to) { - - static const char* svg_header = - R"raw( - - -)raw"; - - // for(auto r : result) { - std::fstream out(loc, std::fstream::out); - if(out.is_open()) { - out << svg_header; - // Item rbin( RectangleItem(bin.width(), bin.height()) ); - // for(unsigned j = 0; j < rbin.vertexCount(); j++) { - // auto v = rbin.vertex(j); - // setY(v, -getY(v)/SCALE + 500 ); - // setX(v, getX(v)/SCALE); - // rbin.setVertex(j, v); - // } - // out << shapelike::serialize(rbin.rawShape()) << std::endl; - for(auto it = from; it != to; ++it) { - const Item &itm = *it; - Item tsh(itm.transformedShape()); - for(unsigned j = 0; j < tsh.vertexCount(); j++) { - auto v = tsh.vertex(j); - setY(v, -getY(v)/SCALE + 500); - setX(v, getX(v)/SCALE); - tsh.setVertex(j, v); - } - out << shapelike::serialize(tsh.rawShape()) << std::endl; - } - out << "\n" << std::endl; - } - out.close(); - - // i++; - // } -} - -template -void exportSVG(std::vector>& result, int idx = 0) { - exportSVG((std::string("out") + std::to_string(idx) + ".svg").c_str(), - result.begin(), result.end()); -} -} - -static std::vector& prusaParts() { - using namespace libnest2d; - - static std::vector ret; - - if(ret.empty()) { - ret.reserve(PRINTER_PART_POLYGONS.size()); - for(auto& inp : PRINTER_PART_POLYGONS) { - auto inp_cpy = inp; - - if (ClosureTypeV == Closure::OPEN) - inp_cpy.points.pop_back(); - - if constexpr (!libnest2d::is_clockwise()) - std::reverse(inp_cpy.begin(), inp_cpy.end()); - - ret.emplace_back(inp_cpy); - } - } - - return ret; -} - -TEST_CASE("Angles", "[Geometry]") -{ - - using namespace libnest2d; - - Degrees deg(180); - Radians rad(deg); - Degrees deg2(rad); - - REQUIRE(Approx(rad) == Pi); - REQUIRE(Approx(deg) == 180); - REQUIRE(Approx(deg2) == 180); - REQUIRE(Approx(rad) == Radians(deg)); - REQUIRE(Approx(Degrees(rad)) == deg); - - REQUIRE(rad == deg); - - Segment seg = {{0, 0}, {12, -10}}; - - REQUIRE(Degrees(seg.angleToXaxis()) > 270); - REQUIRE(Degrees(seg.angleToXaxis()) < 360); - - seg = {{0, 0}, {12, 10}}; - - REQUIRE(Degrees(seg.angleToXaxis()) > 0); - REQUIRE(Degrees(seg.angleToXaxis()) < 90); - - seg = {{0, 0}, {-12, 10}}; - - REQUIRE(Degrees(seg.angleToXaxis()) > 90); - REQUIRE(Degrees(seg.angleToXaxis()) < 180); - - seg = {{0, 0}, {-12, -10}}; - - REQUIRE(Degrees(seg.angleToXaxis()) > 180); - REQUIRE(Degrees(seg.angleToXaxis()) < 270); - - seg = {{0, 0}, {1, 0}}; - - REQUIRE(Degrees(seg.angleToXaxis()) == Approx(0.)); - - seg = {{0, 0}, {0, 1}}; - - REQUIRE(Degrees(seg.angleToXaxis()) == Approx(90.)); - - seg = {{0, 0}, {-1, 0}}; - - REQUIRE(Degrees(seg.angleToXaxis()) == Approx(180.)); - - seg = {{0, 0}, {0, -1}}; - - REQUIRE(Degrees(seg.angleToXaxis()) == Approx(270.)); -} - -// Simple TEST_CASE, does not use gmock -TEST_CASE("ItemCreationAndDestruction", "[Nesting]") -{ - using namespace libnest2d; - - Item sh = { {0, 0}, {1, 0}, {1, 1}, {0, 1} }; - - REQUIRE(sh.vertexCount() == 4u); - - Item sh2 ({ {0, 0}, {1, 0}, {1, 1}, {0, 1} }); - - REQUIRE(sh2.vertexCount() == 4u); - - // copy - Item sh3 = sh2; - - REQUIRE(sh3.vertexCount() == 4u); - - sh2 = {}; - - REQUIRE(sh2.vertexCount() == 0u); - REQUIRE(sh3.vertexCount() == 4u); -} - -TEST_CASE("boundingCircle", "[Geometry]") { - using namespace libnest2d; - using placers::boundingCircle; - - PolygonImpl p = {{{0, 10}, {10, 0}, {0, -10}, {0, 10}}, {}}; - Circle c = boundingCircle(p); - - REQUIRE(getX(c.center()) == 0); - REQUIRE(getY(c.center()) == 0); - REQUIRE(c.radius() == Approx(10)); - - shapelike::translate(p, PointImpl{10, 10}); - c = boundingCircle(p); - - REQUIRE(getX(c.center()) == 10); - REQUIRE(getY(c.center()) == 10); - REQUIRE(c.radius() == Approx(10)); - - auto parts = prusaParts(); - - int i = 0; - for(auto& part : parts) { - c = boundingCircle(part.transformedShape()); - if(std::isnan(c.radius())) std::cout << "fail: radius is nan" << std::endl; - - else for(auto v : shapelike::contour(part.transformedShape()) ) { - auto d = pointlike::distance(v, c.center()); - if(d > c.radius() ) { - auto e = std::abs( 1.0 - d/c.radius()); - REQUIRE(e <= 1e-3); - } - } - i++; - } - -} - -TEST_CASE("Distance", "[Geometry]") { - using namespace libnest2d; - - Point p1 = {0, 0}; - - Point p2 = {10, 0}; - Point p3 = {10, 10}; - - REQUIRE(pointlike::distance(p1, p2) == Approx(10)); - REQUIRE(pointlike::distance(p1, p3) == Approx(sqrt(200))); - - Segment seg(p1, p3); - - // REQUIRE(pointlike::distance(p2, seg) == Approx(7.0710678118654755)); - - auto result = pointlike::horizontalDistance(p2, seg); - - auto check = [](TCompute val, TCompute expected) { - if(std::is_floating_point>::value) - REQUIRE(static_cast(val) == - Approx(static_cast(expected))); - else - REQUIRE(val == expected); - }; - - REQUIRE(result.second); - check(result.first, 10); - - result = pointlike::verticalDistance(p2, seg); - REQUIRE(result.second); - check(result.first, -10); - - result = pointlike::verticalDistance(Point{10, 20}, seg); - REQUIRE(result.second); - check(result.first, 10); - - - Point p4 = {80, 0}; - Segment seg2 = { {0, 0}, {0, 40} }; - - result = pointlike::horizontalDistance(p4, seg2); - - REQUIRE(result.second); - check(result.first, 80); - - result = pointlike::verticalDistance(p4, seg2); - // Point should not be related to the segment - REQUIRE_FALSE(result.second); - -} - -TEST_CASE("Area", "[Geometry]") { - using namespace libnest2d; - - RectangleItem rect(10, 10); - - REQUIRE(rect.area() == Approx(100)); - - RectangleItem rect2 = {100, 100}; - - REQUIRE(rect2.area() == Approx(10000)); - - Item item = { - {61, 97}, - {70, 151}, - {176, 151}, - {189, 138}, - {189, 59}, - {70, 59}, - {61, 77}, - {61, 97} - }; - - REQUIRE(std::abs(shapelike::area(item.transformedShape())) > 0 ); -} - -TEST_CASE("IsPointInsidePolygon", "[Geometry]") { - using namespace libnest2d; - - RectangleItem rect(10, 10); - - Point p = {1, 1}; - - REQUIRE(rect.isInside(p)); - - p = {11, 11}; - - REQUIRE_FALSE(rect.isInside(p)); - - - p = {11, 12}; - - REQUIRE_FALSE(rect.isInside(p)); - - - p = {3, 3}; - - REQUIRE(rect.isInside(p)); - -} - -//TEST_CASE(GeometryAlgorithms, Intersections) { -// using namespace binpack2d; - -// RectangleItem rect(70, 30); - -// rect.translate({80, 60}); - -// RectangleItem rect2(80, 60); -// rect2.translate({80, 0}); - -//// REQUIRE_FALSE(Item::intersects(rect, rect2)); - -// Segment s1({0, 0}, {10, 10}); -// Segment s2({1, 1}, {11, 11}); -// REQUIRE_FALSE(ShapeLike::intersects(s1, s1)); -// REQUIRE_FALSE(ShapeLike::intersects(s1, s2)); -//} - -TEST_CASE("LeftAndDownPolygon", "[Geometry]") -{ - using namespace libnest2d; - - Box bin(100, 100); - BottomLeftPlacer placer(bin); - - PathImpl pitem = {{70, 75}, {88, 60}, {65, 50}, {60, 30}, {80, 20}, - {42, 20}, {35, 35}, {35, 55}, {40, 75}}; - - PathImpl pleftControl = {{40, 75}, {35, 55}, {35, 35}, - {42, 20}, {0, 20}, {0, 75}}; - - PathImpl pdownControl = {{88, 60}, {88, 0}, {35, 0}, {35, 35}, - {42, 20}, {80, 20}, {60, 30}, {65, 50}}; - - if constexpr (!is_clockwise()) { - std::reverse(sl::begin(pitem), sl::end(pitem)); - std::reverse(sl::begin(pleftControl), sl::end(pleftControl)); - std::reverse(sl::begin(pdownControl), sl::end(pdownControl)); - } - - if constexpr (ClosureTypeV == Closure::CLOSED) { - sl::addVertex(pitem, sl::front(pitem)); - sl::addVertex(pleftControl, sl::front(pleftControl)); - sl::addVertex(pdownControl, sl::front(pdownControl)); - } - - Item item{pitem}, leftControl{pleftControl}, downControl{pdownControl}; - Item leftp(placer.leftPoly(item)); - - auto valid = sl::isValid(leftp.rawShape()); - - std::vector> to_export{ leftp, leftControl }; - exportSVG<1>("leftp.svg", to_export.begin(), to_export.end()); - - REQUIRE(valid.first); - REQUIRE(leftp.vertexCount() == leftControl.vertexCount()); - - for(unsigned long i = 0; i < leftControl.vertexCount(); i++) { - REQUIRE(getX(leftp.vertex(i)) == getX(leftControl.vertex(i))); - REQUIRE(getY(leftp.vertex(i)) == getY(leftControl.vertex(i))); - } - - Item downp(placer.downPoly(item)); - - REQUIRE(shapelike::isValid(downp.rawShape()).first); - REQUIRE(downp.vertexCount() == downControl.vertexCount()); - - for(unsigned long i = 0; i < downControl.vertexCount(); i++) { - REQUIRE(getX(downp.vertex(i)) == getX(downControl.vertex(i))); - REQUIRE(getY(downp.vertex(i)) == getY(downControl.vertex(i))); - } -} - -TEST_CASE("ArrangeRectanglesTight", "[Nesting][NotWorking]") -{ - using namespace libnest2d; - - std::vector rects = { - {80, 80}, - {60, 90}, - {70, 30}, - {80, 60}, - {60, 60}, - {60, 40}, - {40, 40}, - {10, 10}, - {10, 10}, - {10, 10}, - {10, 10}, - {10, 10}, - {5, 5}, - {5, 5}, - {5, 5}, - {5, 5}, - {5, 5}, - {5, 5}, - {5, 5}, - {20, 20} }; - - Box bin(210, 250, {105, 125}); - - REQUIRE(bin.width() == 210); - REQUIRE(bin.height() == 250); - REQUIRE(getX(bin.center()) == 105); - REQUIRE(getY(bin.center()) == 125); - - _Nester arrange(bin); - - arrange.execute(rects.begin(), rects.end()); - - auto max_group = std::max_element(rects.begin(), rects.end(), - [](const Item &i1, const Item &i2) { - return i1.binId() < i2.binId(); - }); - - int groups = max_group == rects.end() ? 0 : max_group->binId() + 1; - - REQUIRE(groups == 1u); - REQUIRE( - std::all_of(rects.begin(), rects.end(), [](const RectangleItem &itm) { - return itm.binId() != BIN_ID_UNSET; - })); - - // check for no intersections, no containment: - - // exportSVG<1>("arrangeRectanglesTight.svg", rects.begin(), rects.end()); - - bool valid = true; - for(Item& r1 : rects) { - for(Item& r2 : rects) { - if(&r1 != &r2 ) { - valid = !Item::intersects(r1, r2) || Item::touches(r1, r2); - REQUIRE(valid); - valid = (valid && !r1.isInside(r2) && !r2.isInside(r1)); - REQUIRE(valid); - } - } - } -} - -TEST_CASE("ArrangeRectanglesLoose", "[Nesting]") -{ - using namespace libnest2d; - - // std::vector rects = { {40, 40}, {10, 10}, {20, 20} }; - std::vector rects = { - {80, 80}, - {60, 90}, - {70, 30}, - {80, 60}, - {60, 60}, - {60, 40}, - {40, 40}, - {10, 10}, - {10, 10}, - {10, 10}, - {10, 10}, - {10, 10}, - {5, 5}, - {5, 5}, - {5, 5}, - {5, 5}, - {5, 5}, - {5, 5}, - {5, 5}, - {20, 20} }; - - Box bin(210, 250, {105, 125}); - - REQUIRE(bin.width() == 210); - REQUIRE(bin.height() == 250); - REQUIRE(getX(bin.center()) == 105); - REQUIRE(getY(bin.center()) == 125); - - Coord min_obj_distance = 5; - - _Nester arrange(bin, min_obj_distance); - - arrange.execute(rects.begin(), rects.end()); - - auto max_group = std::max_element(rects.begin(), rects.end(), - [](const Item &i1, const Item &i2) { - return i1.binId() < i2.binId(); - }); - - auto groups = size_t(max_group == rects.end() ? 0 : max_group->binId() + 1); - - REQUIRE(groups == 1u); - REQUIRE( - std::all_of(rects.begin(), rects.end(), [](const RectangleItem &itm) { - return itm.binId() != BIN_ID_UNSET; - })); - - // check for no intersections, no containment: - bool valid = true; - for(Item& r1 : rects) { - for(Item& r2 : rects) { - if(&r1 != &r2 ) { - valid = !Item::intersects(r1, r2); - valid = (valid && !r1.isInside(r2) && !r2.isInside(r1)); - REQUIRE(valid); - } - } - } - -} - -TEST_CASE("BottomLeftStressTest", "[Geometry][NotWorking]") { - using namespace libnest2d; - - const Coord SCALE = 1000000; - auto& input = prusaParts(); - - Box bin(210*SCALE, 250*SCALE); - BottomLeftPlacer placer(bin); - - auto it = input.begin(); - auto next = it; - int i = 0; - while(it != input.end() && ++next != input.end()) { - placer.pack(*it); - placer.pack(*next); - - auto result = placer.getItems(); - bool valid = true; - - if(result.size() == 2) { - Item& r1 = result[0]; - Item& r2 = result[1]; - valid = !Item::intersects(r1, r2) || Item::touches(r1, r2); - valid = (valid && !r1.isInside(r2) && !r2.isInside(r1)); - if(!valid) { - std::cout << "error index: " << i << std::endl; - exportSVG(result, i); - } - REQUIRE(valid); - } else { - std::cout << "something went terribly wrong!" << std::endl; - FAIL(); - } - - placer.clearItems(); - it++; - i++; - } -} - -TEST_CASE("convexHull", "[Geometry]") { - using namespace libnest2d; - - PathImpl poly = PRINTER_PART_POLYGONS[0]; - - auto chull = sl::convexHull(poly); - - REQUIRE(chull.size() == poly.size()); -} - -TEST_CASE("PrusaPartsShouldFitIntoTwoBins", "[Nesting]") { - - // Get the input items and define the bin. - std::vector input = prusaParts(); - auto bin = Box(250000000, 210000000); - - // Do the nesting. Check in each step if the remaining items are less than - // in the previous step. (Some algorithms can place more items in one step) - size_t pcount = input.size(); - - size_t bins = libnest2d::nest(input, bin, 0, {}, - ProgressFunction{[&pcount](unsigned cnt) { - REQUIRE(cnt < pcount); - pcount = cnt; - }}); - - // For prusa parts, 2 bins should be enough... - REQUIRE(bins > 0u); - REQUIRE(bins <= 2u); - - // All parts should be processed by the algorithm - REQUIRE( - std::all_of(input.begin(), input.end(), [](const Item &itm) { - return itm.binId() != BIN_ID_UNSET; - })); - - // Gather the items into piles of arranged polygons... - using Pile = TMultiShape; - std::vector piles(bins); - - for (auto &itm : input) - piles[size_t(itm.binId())].emplace_back(itm.transformedShape()); - - // Now check all the piles, the bounding box of each pile should be inside - // the defined bin. - for (auto &pile : piles) { - auto bb = sl::boundingBox(pile); - REQUIRE(sl::isInside(bb, bin)); - } - - // Check the area of merged pile vs the sum of area of all the parts - // They should match, otherwise there is an overlap which should not happen. - for (auto &pile : piles) { - double area_sum = 0.; - - for (auto &obj : pile) - area_sum += sl::area(obj); - - auto pile_m = nfp::merge(pile); - double area_merge = sl::area(pile_m); - - REQUIRE(area_sum == Approx(area_merge)); - } -} - -TEST_CASE("EmptyItemShouldBeUntouched", "[Nesting]") { - auto bin = Box(250000000, 210000000); // dummy bin - - std::vector items; - items.emplace_back(Item{}); // Emplace empty item - items.emplace_back(Item{ {0, 200} }); // Emplace zero area item - - size_t bins = libnest2d::nest(items, bin); - - REQUIRE(bins == 0u); - for (auto &itm : items) REQUIRE(itm.binId() == BIN_ID_UNSET); -} - -TEST_CASE("LargeItemShouldBeUntouched", "[Nesting]") { - auto bin = Box(250000000, 210000000); // dummy bin - - std::vector items; - items.emplace_back(RectangleItem{250000001, 210000001}); // Emplace large item - - size_t bins = libnest2d::nest(items, bin); - - REQUIRE(bins == 0u); - REQUIRE(items.front().binId() == BIN_ID_UNSET); -} - -TEST_CASE("Items can be preloaded", "[Nesting]") { - auto bin = Box({0, 0}, {250000000, 210000000}); // dummy bin - - std::vector items; - items.reserve(2); - - NestConfig<> cfg; - cfg.placer_config.alignment = NestConfig<>::Placement::Alignment::DONT_ALIGN; - - items.emplace_back(RectangleItem{10000000, 10000000}); - Item &fixed_rect = items.back(); - fixed_rect.translate(bin.center()); - - items.emplace_back(RectangleItem{20000000, 20000000}); - Item &movable_rect = items.back(); - movable_rect.translate(bin.center()); - - SECTION("Preloaded Item should be untouched") { - fixed_rect.markAsFixedInBin(0); - - size_t bins = libnest2d::nest(items, bin, 0, cfg); - - REQUIRE(bins == 1); - - REQUIRE(fixed_rect.binId() == 0); - REQUIRE(getX(fixed_rect.translation()) == getX(bin.center())); - REQUIRE(getY(fixed_rect.translation()) == getY(bin.center())); - - REQUIRE(movable_rect.binId() == 0); - REQUIRE(getX(movable_rect.translation()) != getX(bin.center())); - REQUIRE(getY(movable_rect.translation()) != getY(bin.center())); - } - - SECTION("Preloaded Item should not affect free bins") { - fixed_rect.markAsFixedInBin(1); - - size_t bins = libnest2d::nest(items, bin, 0, cfg); - - REQUIRE(bins == 2); - - REQUIRE(fixed_rect.binId() == 1); - REQUIRE(getX(fixed_rect.translation()) == getX(bin.center())); - REQUIRE(getY(fixed_rect.translation()) == getY(bin.center())); - - REQUIRE(movable_rect.binId() == 0); - - auto bb = movable_rect.boundingBox(); - REQUIRE(getX(bb.center()) == getX(bin.center())); - REQUIRE(getY(bb.center()) == getY(bin.center())); - } -} - -namespace { - -struct ItemPair { - Item orbiter; - Item stationary; -}; - -std::vector nfp_testdata = { - { - { - {80, 50}, - {100, 70}, - {120, 50} - }, - { - {10, 10}, - {10, 40}, - {40, 40}, - {40, 10} - } - }, - { - { - {80, 50}, - {60, 70}, - {80, 90}, - {120, 90}, - {140, 70}, - {120, 50} - }, - { - {10, 10}, - {10, 40}, - {40, 40}, - {40, 10} - } - }, - { - { - {40, 10}, - {30, 10}, - {20, 20}, - {20, 30}, - {30, 40}, - {40, 40}, - {50, 30}, - {50, 20} - }, - { - {80, 0}, - {80, 30}, - {110, 30}, - {110, 0} - } - }, - { - { - {117, 107}, - {118, 109}, - {120, 112}, - {122, 113}, - {128, 113}, - {130, 112}, - {132, 109}, - {133, 107}, - {133, 103}, - {132, 101}, - {130, 98}, - {128, 97}, - {122, 97}, - {120, 98}, - {118, 101}, - {117, 103} - }, - { - {102, 116}, - {111, 126}, - {114, 126}, - {144, 106}, - {148, 100}, - {148, 85}, - {147, 84}, - {102, 84} - } - }, - { - { - {99, 122}, - {108, 140}, - {110, 142}, - {139, 142}, - {151, 122}, - {151, 102}, - {142, 70}, - {139, 68}, - {111, 68}, - {108, 70}, - {99, 102} - }, - { - {107, 124}, - {128, 125}, - {133, 125}, - {136, 124}, - {140, 121}, - {142, 119}, - {143, 116}, - {143, 109}, - {141, 93}, - {139, 89}, - {136, 86}, - {134, 85}, - {108, 85}, - {107, 86} - } - }, - { - { - {91, 100}, - {94, 144}, - {117, 153}, - {118, 153}, - {159, 112}, - {159, 110}, - {156, 66}, - {133, 57}, - {132, 57}, - {91, 98} - }, - { - {101, 90}, - {103, 98}, - {107, 113}, - {114, 125}, - {115, 126}, - {135, 126}, - {136, 125}, - {144, 114}, - {149, 90}, - {149, 89}, - {148, 87}, - {145, 84}, - {105, 84}, - {102, 87}, - {101, 89} - } - } -}; - - std::vector nfp_concave_testdata = { - { // ItemPair - { - { - {533726, 142141}, - {532359, 143386}, - {530141, 142155}, - {528649, 160091}, - {533659, 157607}, - {538669, 160091}, - {537178, 142155}, - {534959, 143386} - } - }, - { - { - {118305, 11603}, - {118311, 26616}, - {113311, 26611}, - {109311, 29604}, - {109300, 44608}, - {109311, 49631}, - {113300, 52636}, - {118311, 52636}, - {118308, 103636}, - {223830, 103636}, - {236845, 90642}, - {236832, 11630}, - {232825, 11616}, - {210149, 11616}, - {211308, 13625}, - {209315, 17080}, - {205326, 17080}, - {203334, 13629}, - {204493, 11616} - } - }, - } -}; - -template -void testNfp(const std::vector& testdata) { - using namespace libnest2d; - - Box bin(210*SCALE, 250*SCALE); - - int TEST_CASEcase = 0; - - auto& exportfun = exportSVG; - - auto onetest = [&](Item& orbiter, Item& stationary, unsigned /*testidx*/){ - TEST_CASEcase++; - - orbiter.translate({210*SCALE, 0}); - - auto&& nfp = nfp::noFitPolygon(stationary.rawShape(), - orbiter.transformedShape()); - - placers::correctNfpPosition(nfp, stationary, orbiter); - - auto valid = shapelike::isValid(nfp.first); - - /*Item infp(nfp.first); - if(!valid.first) { - std::cout << "TEST_CASE instance: " << TEST_CASEidx << " " - << valid.second << std::endl; - std::vector> inp = {std::ref(infp)}; - exportfun(inp, bin, TEST_CASEidx); - }*/ - - REQUIRE(valid.first); - - Item infp(nfp.first); - - int i = 0; - auto rorbiter = orbiter.transformedShape(); - auto vo = nfp::referenceVertex(rorbiter); - - REQUIRE(stationary.isInside(infp)); - - for(auto v : infp) { - auto dx = getX(v) - getX(vo); - auto dy = getY(v) - getY(vo); - - Item tmp = orbiter; - - tmp.translate({dx, dy}); - - bool touching = Item::touches(tmp, stationary); - - if(!touching || !valid.first) { - std::vector> inp = { - std::ref(stationary), std::ref(tmp), std::ref(infp) - }; - - exportfun(inp, TEST_CASEcase*i++); - } - - REQUIRE(touching); - } - }; - - unsigned tidx = 0; - for(auto& td : testdata) { - auto orbiter = td.orbiter; - auto stationary = td.stationary; - if (!libnest2d::is_clockwise()) { - auto porb = orbiter.rawShape(); - auto pstat = stationary.rawShape(); - std::reverse(sl::begin(porb), sl::end(porb)); - std::reverse(sl::begin(pstat), sl::end(pstat)); - orbiter = Item{porb}; - stationary = Item{pstat}; - } - onetest(orbiter, stationary, tidx++); - } - - tidx = 0; - for(auto& td : testdata) { - auto orbiter = td.stationary; - auto stationary = td.orbiter; - if (!libnest2d::is_clockwise()) { - auto porb = orbiter.rawShape(); - auto pstat = stationary.rawShape(); - std::reverse(sl::begin(porb), sl::end(porb)); - std::reverse(sl::begin(pstat), sl::end(pstat)); - orbiter = Item{porb}; - stationary = Item{pstat}; - } - onetest(orbiter, stationary, tidx++); - } -} -} - -TEST_CASE("nfpConvexConvex", "[Geometry]") { - testNfp(nfp_testdata); -} - -//TEST_CASE(GeometryAlgorithms, nfpConcaveConcave) { -// TEST_CASENfp(nfp_concave_TEST_CASEdata); -//} - -TEST_CASE("pointOnPolygonContour", "[Geometry]") { - using namespace libnest2d; - - RectangleItem input(10, 10); - - placers::EdgeCache ecache(input); - - auto first = *input.begin(); - REQUIRE(getX(first) == getX(ecache.coords(0))); - REQUIRE(getY(first) == getY(ecache.coords(0))); - - if constexpr (ClosureTypeV == Closure::CLOSED) { - auto last = *std::prev(input.end()); - REQUIRE(getX(last) == getX(ecache.coords(1.0))); - REQUIRE(getY(last) == getY(ecache.coords(1.0))); - } else { - auto last = *input.begin(); - REQUIRE(getX(last) == getX(ecache.coords(1.0))); - REQUIRE(getY(last) == getY(ecache.coords(1.0))); - } - - for(int i = 0; i <= 100; i++) { - auto v = ecache.coords(i*(0.01)); - REQUIRE(shapelike::touches(v, input.transformedShape())); - } -} - -TEST_CASE("mergePileWithPolygon", "[Geometry]") { - using namespace libnest2d; - - RectangleItem rect1(10, 15); - RectangleItem rect2(15, 15); - RectangleItem rect3(20, 15); - - rect2.translate({10, 0}); - rect3.translate({25, 0}); - - TMultiShape pile; - pile.push_back(rect1.transformedShape()); - pile.push_back(rect2.transformedShape()); - - auto result = nfp::merge(pile, rect3.transformedShape()); - - REQUIRE(result.size() == 1); - - RectangleItem ref(45, 15); - - REQUIRE(shapelike::area(result.front()) == Approx(ref.area())); -} - -namespace { - -long double refMinAreaBox(const PolygonImpl& p) { - - auto it = sl::cbegin(p), itx = std::next(it); - - long double min_area = std::numeric_limits::max(); - - - auto update_min = [&min_area, &it, &itx, &p]() { - Segment s(*it, *itx); - - PolygonImpl rotated = p; - sl::rotate(rotated, -s.angleToXaxis()); - auto bb = sl::boundingBox(rotated); - auto area = cast(sl::area(bb)); - if(min_area > area) min_area = area; - }; - - while(itx != sl::cend(p)) { - update_min(); - ++it; ++itx; - } - - it = std::prev(sl::cend(p)); itx = sl::cbegin(p); - update_min(); - - return min_area; -} - -template struct BoostGCD { - T operator()(const T &a, const T &b) { return boost::gcd(a, b); } -}; - -using Unit = int64_t; -using Ratio = boost::rational; - -} - -//TEST_CASE(GeometryAlgorithms, MinAreaBBCClk) { -// auto u = [](ClipperLib::cInt n) { return n*1000000; }; -// PolygonImpl poly({ {u(0), u(0)}, {u(4), u(1)}, {u(2), u(4)}}); - -// long double arearef = refMinAreaBox(poly); -// long double area = minAreaBoundingBox(poly).area(); - -// REQUIRE(std::abs(area - arearef) <= 500e6 ); -//} - -TEST_CASE("MinAreaBBWithRotatingCalipers", "[Geometry]") { - long double err_epsilon = 500e6l; - - for(PathImpl rinput : PRINTER_PART_POLYGONS) { - PolygonImpl poly(rinput); - - long double arearef = refMinAreaBox(poly); - auto bb = minAreaBoundingBox(rinput); - long double area = cast(bb.area()); - - bool succ = std::abs(arearef - area) < err_epsilon; - - REQUIRE(succ); - } - - for(PathImpl rinput : STEGOSAUR_POLYGONS) { -// rinput.pop_back(); - std::reverse(rinput.begin(), rinput.end()); - - PolygonImpl poly(removeCollinearPoints(rinput, 1000000)); - - long double arearef = refMinAreaBox(poly); - auto bb = minAreaBoundingBox(poly); - long double area = cast(bb.area()); - - - bool succ = std::abs(arearef - area) < err_epsilon; - - REQUIRE(succ); - } -} - -template MultiPolygon merged_pile(It from, It to, int bin_id) -{ - MultiPolygon pile; - pile.reserve(size_t(to - from)); - - for (auto it = from; it != to; ++it) { - if (it->binId() == bin_id) pile.emplace_back(it->transformedShape()); - } - - return nfp::merge(pile); -} - -TEST_CASE("Test for bed center distance optimization", "[Nesting], [NestKernels]") -{ - static const constexpr Slic3r::ClipperLib::cInt W = 10000000; - - // Get the input items and define the bin. - std::vector input(9, {W, W}); - - auto bin = Box::infinite(); - - NfpPlacer::Config pconfig; - - pconfig.object_function = [](const Item &item) -> double { - return pl::magnsq(item.boundingBox().center()); - }; - - size_t bins = nest(input, bin, 0, NestConfig{pconfig}); - - REQUIRE(bins == 1); - - // Gather the items into piles of arranged polygons... - MultiPolygon pile; - pile.reserve(input.size()); - - for (auto &itm : input) { - REQUIRE(itm.binId() == 0); - pile.emplace_back(itm.transformedShape()); - } - - MultiPolygon m = merged_pile(input.begin(), input.end(), 0); - - REQUIRE(m.size() == 1); - - REQUIRE(sl::area(m) == Approx(9. * W * W)); -} - -TEST_CASE("Test for biggest bounding box area", "[Nesting], [NestKernels]") -{ - static const constexpr Slic3r::ClipperLib::cInt W = 10000000; - static const constexpr size_t N = 100; - - // Get the input items and define the bin. - std::vector input(N, {W, W}); - - auto bin = Box::infinite(); - - NfpPlacer::Config pconfig; - pconfig.rotations = {0.}; - Box pile_box; - pconfig.before_packing = - [&pile_box](const MultiPolygon &pile, - const _ItemGroup &/*packed_items*/, - const _ItemGroup &/*remaining_items*/) { - pile_box = sl::boundingBox(pile); - }; - - pconfig.object_function = [&pile_box](const Item &item) -> double { - Box b = sl::boundingBox(item.boundingBox(), pile_box); - double area = b.area() / (double(W) * W); - return -area; - }; - - size_t bins = nest(input, bin, 0, NestConfig{pconfig}); - - // To debug: - exportSVG<1000000>("out", input.begin(), input.end()); - - REQUIRE(bins == 1); - - MultiPolygon pile = merged_pile(input.begin(), input.end(), 0); - Box bb = sl::boundingBox(pile); - - // Here the result shall be a stairway of boxes - REQUIRE(pile.size() == N); - REQUIRE(bb.area() == double(N) * N * W * W); -} diff --git a/tests/libnest2d/printer_parts.cpp b/tests/libnest2d/printer_parts.cpp deleted file mode 100644 index 104b1c12fd..0000000000 --- a/tests/libnest2d/printer_parts.cpp +++ /dev/null @@ -1,3175 +0,0 @@ -#include "printer_parts.hpp" - -const TestData PRINTER_PART_POLYGONS = -{ - { - {-5000000, 8954050}, - {5000000, 8954050}, - {5000000, -45949}, - {4972609, -568550}, - {3500000, -8954050}, - {-3500000, -8954050}, - {-4972609, -568550}, - {-5000000, -45949}, - {-5000000, 8954050}, - }, - { - {-63750000, -8000000}, - {-54750000, 46000000}, - {50750000, 46000000}, - {63750000, 33000000}, - {63750000, -46000000}, - {-54750000, -46000000}, - {-63750000, -28000000}, - {-63750000, -8000000}, - }, - { - {-52750000, 41512348}, - {-31250000, 45987651}, - {52750000, 45987651}, - {52750000, -45987651}, - {-52750000, -45987651}, - {-52750000, 41512348}, - }, - { - {-3900000, 14000000}, - {-2167950, 14000000}, - {1721454, 7263400}, - {3828529, 3613790}, - {3838809, 3582149}, - {3871560, 3270569}, - {3900000, 3000000}, - {3500000, -3000000}, - {3471560, -3270565}, - {3447549, -3498986}, - {3292510, -3976167}, - {3099999, -4512949}, - {2530129, -5500000}, - {807565, -8483570}, - {-2377349, -14000000}, - {-3900000, -14000000}, - {-3900000, 14000000}, - }, - { - {-31750000, -1000000}, - {-25250000, 40500000}, - {-18250000, 47500000}, - {10750000, 47500000}, - {16750000, 41500000}, - {31750000, -37000000}, - {31750000, -43857898}, - {28107900, -47500000}, - {18392099, -47500000}, - {-20750000, -46500000}, - {-31750000, -4000000}, - {-31750000, -1000000}, - }, - { - {-34625000, -14265399}, - {-10924999, 24875000}, - {33325000, 24875000}, - {37575000, 20625000}, - {37575000, 17625000}, - {26575000, -24875000}, - {-8924999, -24875000}, - {-34625000, -24484600}, - {-37575000, -19375000}, - {-34625000, -14265399}, - }, - { - {-14000000, 9000000}, - {-11000000, 17000000}, - {14000000, 17000000}, - {14000000, -17000000}, - {-11000000, -17000000}, - {-14000000, -8000000}, - {-14000000, 9000000}, - }, - { - {-5300000, 2227401}, - {-237800, 5150001}, - {5299999, 5150001}, - {5299999, 650001}, - {4699999, -5149997}, - {-5300000, -5149997}, - {-5300000, 2227401}, - }, - { - {-12000000, 18000000}, - {12000000, 18000000}, - {12000000, -18000000}, - {-12000000, -18000000}, - {-12000000, 18000000}, - }, - { - {-18000000, -1000000}, - {-15000000, 22000000}, - {-11000000, 26000000}, - {11000000, 26000000}, - {15000000, 22000000}, - {18000000, -1000000}, - {18000000, -26000000}, - {-18000000, -26000000}, - {-18000000, -1000000}, - }, - { - {-77500000, 30000000}, - {-72500000, 35000000}, - {72500000, 35000000}, - {77500000, 30000000}, - {77500000, -32928901}, - {75428901, -35000000}, - {-75428901, -35000000}, - {-77500000, -32928901}, - {-77500000, 30000000}, - }, - { - {-9945219, -3065619}, - {-9781479, -2031780}, - {-9510560, -1020730}, - {-9135450, -43529}, - {-2099999, 14110899}, - {2099999, 14110899}, - {9135450, -43529}, - {9510560, -1020730}, - {9781479, -2031780}, - {9945219, -3065619}, - {10000000, -4110899}, - {9945219, -5156179}, - {9781479, -6190019}, - {9510560, -7201069}, - {9135450, -8178270}, - {8660249, -9110899}, - {8090169, -9988750}, - {7431449, -10802209}, - {6691309, -11542349}, - {5877850, -12201069}, - {5000000, -12771149}, - {4067369, -13246350}, - {3090169, -13621459}, - {2079119, -13892379}, - {1045279, -14056119}, - {0, -14110899}, - {-1045279, -14056119}, - {-2079119, -13892379}, - {-3090169, -13621459}, - {-4067369, -13246350}, - {-5000000, -12771149}, - {-5877850, -12201069}, - {-6691309, -11542349}, - {-7431449, -10802209}, - {-8090169, -9988750}, - {-8660249, -9110899}, - {-9135450, -8178270}, - {-9510560, -7201069}, - {-9781479, -6190019}, - {-9945219, -5156179}, - {-10000000, -4110899}, - {-9945219, -3065619}, - }, - { - {-34192394, -5192389}, - {-31499996, 39000000}, - {-8183795, 47668998}, - {-6769596, 47668998}, - {-4648197, 45547698}, - {34192394, 6707109}, - {34192394, 5192389}, - {31500003, -39000000}, - {8183803, -47668998}, - {6769603, -47668998}, - {4648202, -45547698}, - {-32474895, -8424619}, - {-34192394, -6707109}, - {-34192394, -5192389}, - }, - { - {-23475500, -11910099}, - {-18000000, 8217699}, - {-11139699, 20100000}, - {-10271400, 20899999}, - {9532010, 20899999}, - {11199999, 20100000}, - {18500000, 8600000}, - {23475500, -11910099}, - {23799999, -14899999}, - {23706600, -15788900}, - {23668899, -16147499}, - {23281299, -17340400}, - {22654100, -18426700}, - {21814800, -19358900}, - {20799999, -20096199}, - {19654100, -20606300}, - {18427200, -20867099}, - {17799999, -20899999}, - {-17799999, -20899999}, - {-18427200, -20867099}, - {-19654100, -20606300}, - {-20799999, -20096199}, - {-21814800, -19358900}, - {-22654100, -18426700}, - {-23281299, -17340400}, - {-23668899, -16147499}, - {-23799999, -14899999}, - {-23475500, -11910099}, - }, - { - {-32000000, 10000000}, - {-31934440, 10623733}, - {-31740640, 11220210}, - {-31427049, 11763360}, - {-31007389, 12229430}, - {-30500000, 12598079}, - {-29927051, 12853170}, - {-29313585, 12983570}, - {16000000, 16000000}, - {26000000, 16000000}, - {31007400, 12229430}, - {31427101, 11763360}, - {31740600, 11220210}, - {31934398, 10623733}, - {32000000, 10000000}, - {32000000, -13000000}, - {31934398, -13623699}, - {31740600, -14220199}, - {31427101, -14763399}, - {31007400, -15229400}, - {30500000, -15598100}, - {29927101, -15853200}, - {29313598, -15983600}, - {29000000, -16000000}, - {-28000000, -16000000}, - {-29313585, -15983600}, - {-29927051, -15853200}, - {-30500000, -15598100}, - {-31007389, -15229400}, - {-31427049, -14763399}, - {-31740640, -14220199}, - {-31934440, -13623699}, - {-32000000, -13000000}, - {-32000000, 10000000}, - }, - { - {-36133789, -46431022}, - {-36040100, -46171817}, - {-35852722, -45653411}, - {2200073, 59616485}, - {12112792, 87039184}, - {14274505, 93019332}, - {14382049, 93291641}, - {14508483, 93563430}, - {14573425, 93688369}, - {14654052, 93832443}, - {14818634, 94096328}, - {14982757, 94327621}, - {15001708, 94352630}, - {15202392, 94598999}, - {15419342, 94833160}, - {15497497, 94910552}, - {15650848, 95053039}, - {15894866, 95256866}, - {16104309, 95412185}, - {16149047, 95443206}, - {16410888, 95611038}, - {16677795, 95759750}, - {16782348, 95812332}, - {16947143, 95889144}, - {17216400, 95999465}, - {17483123, 96091293}, - {17505554, 96098251}, - {17745178, 96165542}, - {18000671, 96223373}, - {18245880, 96265884}, - {18484039, 96295257}, - {18976715, 96319580}, - {31135131, 96319580}, - {31697082, 96287902}, - {31746368, 96282104}, - {32263000, 96190719}, - {32338623, 96172576}, - {32821411, 96026641}, - {32906188, 95995391}, - {33360565, 95797012}, - {33443420, 95754882}, - {33869171, 95505874}, - {33900756, 95485122}, - {34136413, 95318618}, - {34337127, 95159790}, - {34377288, 95125930}, - {34619628, 94905410}, - {34756286, 94767364}, - {34859008, 94656143}, - {35090606, 94378067}, - {35120849, 94338546}, - {35309295, 94072113}, - {35434875, 93871475}, - {35510070, 93740310}, - {35688232, 93385772}, - {35699096, 93361679}, - {35839782, 93012557}, - {35905487, 92817459}, - {35961578, 92625488}, - {36048004, 92249023}, - {36051574, 92229934}, - {36108856, 91831405}, - {36122985, 91667816}, - {36133789, 91435317}, - {36129669, 91085830}, - {36127685, 91046661}, - {36092742, 90669830}, - {36069946, 90514739}, - {36031829, 90308425}, - {35948211, 89965225}, - {34482635, 84756820}, - {27911407, 61403976}, - {-5872558, -58657440}, - {-14243621, -88406509}, - {-14576812, -89590599}, - {-15421997, -92594200}, - {-15657684, -93431732}, - {-16038940, -93720520}, - {-16420196, -94009307}, - {-17182708, -94586875}, - {-18834838, -95838272}, - {-19470275, -96319580}, - {-21368133, -96319580}, - {-22763854, -96319534}, - {-29742462, -96319274}, - {-32533935, -96319168}, - {-36133789, -54619018}, - {-36133789, -46431022}, - }, - { - {-26000000, 25500000}, - {-6500000, 45000000}, - {17499998, 45000000}, - {23966310, 38533699}, - {26000000, 36500000}, - {26000000, -19000000}, - {25950000, -24500000}, - {17000000, -42214698}, - {14300000, -45000000}, - {-14299999, -45000000}, - {-17500000, -41714698}, - {-23400001, -24500000}, - {-26000000, -10464000}, - {-26000000, 25500000}, - }, - { - {-26000000, 16636100}, - {-25072200, 18777799}, - {-16500000, 35299999}, - {-15050000, 36750000}, - {13550000, 36750000}, - {15000000, 35299999}, - {26000000, 16045200}, - {26000000, -2750000}, - {16500000, -34507900}, - {14840600, -36167301}, - {14257900, -36750000}, - {-14257900, -36750000}, - {-16500000, -34507900}, - {-26000000, -2750000}, - {-26000000, 16636100}, - }, - { - {-18062349, 18950099}, - {4644938, 20049900}, - {6230361, 20049900}, - {7803279, 19851200}, - {9338899, 19456899}, - {10812990, 18873300}, - {12202310, 18109500}, - {13484951, 17177600}, - {14640670, 16092300}, - {15651250, 14870700}, - {16500749, 13532100}, - {17175849, 12097599}, - {17665750, 10589700}, - {17962850, 9032400}, - {18062349, 7450099}, - {17962850, 5867799}, - {15810750, -11007740}, - {15683750, -11727769}, - {15506849, -12437200}, - {15280929, -13132559}, - {15007040, -13810470}, - {14686531, -14467609}, - {14320949, -15100799}, - {13912099, -15706950}, - {13461959, -16283100}, - {12972730, -16826450}, - {12446790, -17334339}, - {11886699, -17804309}, - {11295190, -18234069}, - {10675149, -18621520}, - {10029590, -18964771}, - {9361650, -19262149}, - {8674600, -19512220}, - {7971780, -19713699}, - {7256609, -19865798}, - {6532589, -19967498}, - {5803222, -20018501}, - {5437650, -20024900}, - {-1062349, -20049900}, - {-16562349, -20049900}, - {-18062349, -18549900}, - {-18062349, 18950099}, - }, - { - {-18062349, 41299900}, - {-1062349, 41299900}, - {15280929, -8117440}, - {15506849, -8812799}, - {15683750, -9522230}, - {15810750, -10242259}, - {17962850, -27117799}, - {18062349, -28700099}, - {17962850, -30282400}, - {17665750, -31839700}, - {17175849, -33347599}, - {16500749, -34782100}, - {15651250, -36120700}, - {14640670, -37342300}, - {13484951, -38427600}, - {12202310, -39359500}, - {10812990, -40123298}, - {9338899, -40706901}, - {7803279, -41101200}, - {6230361, -41299900}, - {4644938, -41299900}, - {-18062349, -40200099}, - {-18062349, 41299900}, - }, - { - {-11750000, 13057900}, - {-9807860, 15000000}, - {4392139, 24000000}, - {11750000, 24000000}, - {11750000, -24000000}, - {4392139, -24000000}, - {-9807860, -15000000}, - {-11750000, -13057900}, - {-11750000, 13057900}, - }, - { - {-12500000, 17500000}, - {12500000, 17500000}, - {12500000, -17500000}, - {-12500000, -17500000}, - {-12500000, 17500000}, - }, - { - {-23500000, 11500000}, - {-13857859, 21000000}, - {-11000000, 21000000}, - {18500000, 500000}, - {23500000, -4500000}, - {23500000, -19500000}, - {22000000, -21000000}, - {-23500000, -21000000}, - {-23500000, 11500000}, - }, - { - {-13000000, 5250000}, - {-4000000, 6750000}, - {4000000, 6750000}, - {13000000, 5250000}, - {13000000, 838459}, - {11376299, -1973939}, - {10350899, -3750000}, - {8618800, -6750000}, - {-8498290, -6750000}, - {-13000000, 1047180}, - {-13000000, 5250000}, - }, - { - {-25000000, 50500000}, - {-21500000, 54000000}, - {18286800, 54000000}, - {25000000, 47286800}, - {25000000, -47286800}, - {18286800, -54000000}, - {-21500000, -54000000}, - {-25000000, -50500000}, - {-25000000, 50500000}, - }, - { - {-19000000, 46000000}, - {-16799999, 46000000}, - {14000000, 34000000}, - {19000000, 29000000}, - {19000000, -29000000}, - {14000000, -34000000}, - {-16799999, -46000000}, - {-19000000, -46000000}, - {-19000000, 46000000}, - }, - { - {-7956170, 836226}, - {-7825180, 1663290}, - {-7767529, 1914530}, - {-7608449, 2472140}, - {-7308360, 3253890}, - {-7083650, 3717780}, - {-6928199, 4000000}, - {-6472139, 4702280}, - {-5988090, 5304979}, - {-5945159, 5353040}, - {-5353040, 5945159}, - {-4702280, 6472139}, - {-4544519, 6583869}, - {-4000000, 6928199}, - {-3253890, 7308360}, - {-2836839, 7480130}, - {-2472140, 7608449}, - {-1663290, 7825180}, - {-964293, 7941669}, - {-836226, 7956170}, - {0, 8000000}, - {836226, 7956170}, - {964293, 7941669}, - {1663290, 7825180}, - {2472140, 7608449}, - {2836839, 7480130}, - {3253890, 7308360}, - {4000000, 6928199}, - {4544519, 6583869}, - {4702280, 6472139}, - {5353040, 5945159}, - {5945159, 5353040}, - {5988090, 5304979}, - {6472139, 4702280}, - {6928199, 4000000}, - {7083650, 3717780}, - {7308360, 3253890}, - {7608449, 2472140}, - {7767529, 1914530}, - {7825180, 1663290}, - {7956170, 836226}, - {8000000, 0}, - {7956170, -836226}, - {7825180, -1663290}, - {7767529, -1914530}, - {7608449, -2472140}, - {7308360, -3253890}, - {7083650, -3717780}, - {6928199, -4000000}, - {6472139, -4702280}, - {5988090, -5304979}, - {5945159, -5353040}, - {5353040, -5945159}, - {4702280, -6472139}, - {4544519, -6583869}, - {4000000, -6928199}, - {3253890, -7308360}, - {2836839, -7480130}, - {2472140, -7608449}, - {1663290, -7825180}, - {964293, -7941669}, - {836226, -7956170}, - {0, -8000000}, - {-836226, -7956170}, - {-964293, -7941669}, - {-1663290, -7825180}, - {-2472140, -7608449}, - {-2836839, -7480130}, - {-3253890, -7308360}, - {-4000000, -6928199}, - {-4544519, -6583869}, - {-4702280, -6472139}, - {-5353040, -5945159}, - {-5945159, -5353040}, - {-5988090, -5304979}, - {-6472139, -4702280}, - {-6928199, -4000000}, - {-7083650, -3717780}, - {-7308360, -3253890}, - {-7608449, -2472140}, - {-7767529, -1914530}, - {-7825180, -1663290}, - {-7956170, -836226}, - {-8000000, 0}, - {-7956170, 836226}, - }, -}; - -const TestData STEGOSAUR_POLYGONS = -{ - { - {113210205, 107034095}, - {113561798, 109153793}, - {113750099, 109914001}, - {114396499, 111040199}, - {114599197, 111321998}, - {115570404, 112657096}, - {116920097, 114166595}, - {117630599, 114609390}, - {119703704, 115583900}, - {120559494, 115811996}, - {121045410, 115754493}, - {122698097, 115526496}, - {123373001, 115370193}, - {123482406, 115315689}, - {125664199, 114129798}, - {125920303, 113968193}, - {128551208, 111866195}, - {129075592, 111443199}, - {135044692, 106572608}, - {135254898, 106347694}, - {135415100, 106102897}, - {136121704, 103779891}, - {136325103, 103086303}, - {136690093, 101284896}, - {136798309, 97568496}, - {136798309, 97470397}, - {136787399, 97375297}, - {136753295, 97272102}, - {136687988, 97158699}, - {136539794, 96946899}, - {135526702, 95550994}, - {135388488, 95382293}, - {135272491, 95279098}, - {135214904, 95250595}, - {135122894, 95218002}, - {134966705, 95165191}, - {131753997, 94380798}, - {131226806, 94331001}, - {129603393, 94193893}, - {129224197, 94188003}, - {127874107, 94215103}, - {126812797, 94690200}, - {126558197, 94813896}, - {118361801, 99824195}, - {116550796, 101078796}, - {116189704, 101380493}, - {114634002, 103027999}, - {114118103, 103820297}, - {113399200, 105568000}, - {113201705, 106093597}, - {113210205, 107034095}, - }, - { - {77917999, 130563003}, - {77926300, 131300903}, - {77990196, 132392700}, - {78144195, 133328002}, - {78170593, 133427093}, - {78235900, 133657592}, - {78799598, 135466705}, - {78933296, 135832397}, - {79112899, 136247604}, - {79336303, 136670898}, - {79585197, 137080596}, - {79726303, 137309005}, - {79820297, 137431900}, - {79942199, 137549407}, - {90329193, 145990203}, - {90460197, 146094390}, - {90606399, 146184509}, - {90715194, 146230010}, - {90919601, 146267211}, - {142335296, 153077697}, - {143460296, 153153594}, - {143976593, 153182189}, - {145403991, 153148605}, - {145562301, 153131195}, - {145705993, 153102905}, - {145938796, 153053192}, - {146134094, 153010101}, - {146483184, 152920196}, - {146904693, 152806396}, - {147180099, 152670196}, - {147357788, 152581695}, - {147615295, 152423095}, - {147782287, 152294708}, - {149281799, 150908386}, - {149405303, 150784912}, - {166569305, 126952499}, - {166784301, 126638099}, - {166938491, 126393699}, - {167030899, 126245101}, - {167173004, 126015899}, - {167415298, 125607200}, - {167468292, 125504699}, - {167553100, 125320899}, - {167584594, 125250694}, - {167684997, 125004394}, - {167807098, 124672401}, - {167938995, 124255203}, - {168052307, 123694000}, - {170094100, 112846900}, - {170118408, 112684204}, - {172079101, 88437797}, - {172082000, 88294403}, - {171916290, 82827606}, - {171911590, 82705703}, - {171874893, 82641906}, - {169867004, 79529907}, - {155996795, 58147998}, - {155904998, 58066299}, - {155864791, 58054199}, - {134315704, 56830902}, - {134086486, 56817901}, - {98200096, 56817798}, - {97838195, 56818599}, - {79401695, 56865097}, - {79291297, 56865501}, - {79180694, 56869499}, - {79058799, 56885097}, - {78937301, 56965301}, - {78324691, 57374599}, - {77932998, 57638401}, - {77917999, 57764297}, - {77917999, 130563003}, - }, - { - {75566848, 109289947}, - {75592651, 109421951}, - {75644248, 109534446}, - {95210548, 141223846}, - {95262649, 141307449}, - {95487854, 141401443}, - {95910850, 141511642}, - {96105651, 141550338}, - {106015045, 142803451}, - {106142852, 142815155}, - {166897460, 139500244}, - {167019348, 139484741}, - {168008239, 138823043}, - {168137542, 138735153}, - {168156250, 138616851}, - {173160751, 98882049}, - {174381546, 87916046}, - {174412246, 87579048}, - {174429443, 86988746}, - {174436141, 86297348}, - {174438949, 84912048}, - {174262939, 80999145}, - {174172546, 80477546}, - {173847549, 79140846}, - {173623840, 78294349}, - {173120239, 76485046}, - {173067138, 76300544}, - {173017852, 76137542}, - {172941543, 75903045}, - {172892547, 75753143}, - {172813537, 75533348}, - {172758453, 75387046}, - {172307556, 74196746}, - {171926544, 73192848}, - {171891448, 73100448}, - {171672546, 72524147}, - {171502441, 72085144}, - {171414459, 71859146}, - {171294250, 71552352}, - {171080139, 71019744}, - {171039245, 70928146}, - {170970550, 70813346}, - {170904235, 70704040}, - {170786254, 70524353}, - {168063247, 67259048}, - {167989547, 67184844}, - {83427947, 67184844}, - {78360847, 67201248}, - {78238845, 67220550}, - {78151550, 67350547}, - {77574554, 68220550}, - {77494949, 68342651}, - {77479949, 68464546}, - {75648345, 106513351}, - {75561050, 109165740}, - {75566848, 109289947}, - }, - { - {75619415, 108041595}, - {83609863, 134885772}, - {83806945, 135450820}, - {83943908, 135727371}, - {84799934, 137289794}, - {86547897, 140033782}, - {86674118, 140192962}, - {86810661, 140364715}, - {87045211, 140619918}, - {88187042, 141853240}, - {93924575, 147393783}, - {94058013, 147454803}, - {111640083, 153754562}, - {111762550, 153787933}, - {111975250, 153835311}, - {112127426, 153842803}, - {116797996, 154005157}, - {116969688, 154010681}, - {117141731, 154005935}, - {117333145, 153988037}, - {118007507, 153919952}, - {118159675, 153902130}, - {118931480, 153771942}, - {120878150, 153379089}, - {121172164, 153319259}, - {122074508, 153034362}, - {122260681, 152970367}, - {122313438, 152949584}, - {130755096, 149423736}, - {130996063, 149316818}, - {138893524, 144469665}, - {138896423, 144466918}, - {169883666, 97686134}, - {170115036, 96518981}, - {170144317, 96365257}, - {174395645, 67672065}, - {174396560, 67664222}, - {174288452, 66839241}, - {174170364, 66096923}, - {174112731, 65952033}, - {174021377, 65823486}, - {173948608, 65743225}, - {173863830, 65654769}, - {170408340, 63627494}, - {170004867, 63394714}, - {169585632, 63194389}, - {169441162, 63137046}, - {168944274, 62952133}, - {160605072, 60214218}, - {160331573, 60126396}, - {159674743, 59916877}, - {150337249, 56943778}, - {150267730, 56922073}, - {150080139, 56864868}, - {149435333, 56676422}, - {149310241, 56640579}, - {148055419, 56285041}, - {147828796, 56230949}, - {147598205, 56181800}, - {147149963, 56093917}, - {146834457, 56044700}, - {146727966, 56028717}, - {146519729, 56004882}, - {146328521, 55989326}, - {146170684, 55990036}, - {146151321, 55990745}, - {145800170, 56003616}, - {145639526, 56017753}, - {145599426, 56022491}, - {145481338, 56039184}, - {145389556, 56052757}, - {145325134, 56062591}, - {145176574, 56086135}, - {145017272, 56113922}, - {107163085, 63504539}, - {101013870, 65454101}, - {100921798, 65535285}, - {95362182, 74174079}, - {75652366, 107803443}, - {75635391, 107834983}, - {75628814, 107853294}, - {75603431, 107933692}, - {75619415, 108041595}, - }, - { - {83617141, 120264900}, - {84617370, 126416427}, - {84648635, 126601341}, - {84693695, 126816085}, - {84762496, 127082641}, - {84772140, 127117034}, - {84860748, 127391693}, - {84927398, 127550239}, - {85072967, 127789642}, - {85155151, 127908851}, - {86745422, 130042907}, - {86982666, 130317489}, - {89975143, 133230743}, - {90091384, 133338500}, - {96260833, 138719818}, - {96713928, 139103668}, - {98139297, 140307388}, - {102104766, 143511505}, - {102142089, 143536468}, - {102457626, 143735107}, - {103386764, 144312988}, - {103845001, 144579177}, - {104139175, 144737136}, - {104551254, 144932250}, - {104690155, 144985778}, - {104844238, 145010009}, - {105020034, 145010375}, - {128999633, 144082305}, - {129096542, 144076141}, - {133932327, 143370178}, - {134130615, 143326751}, - {134281250, 143289520}, - {135247116, 142993438}, - {150774948, 137828704}, - {150893478, 137786178}, - {151350921, 137608901}, - {159797760, 134318115}, - {159979827, 134244384}, - {159988128, 134240997}, - {160035186, 134221633}, - {160054962, 134211486}, - {160168762, 134132736}, - {160181228, 134121047}, - {160336425, 133961502}, - {160689147, 133564331}, - {161446258, 132710739}, - {163306427, 130611648}, - {164845474, 128873855}, - {165270233, 128393600}, - {165281478, 128380706}, - {165300598, 128358673}, - {165303497, 128355194}, - {166411590, 122772674}, - {166423767, 122708648}, - {164745605, 66237312}, - {164740341, 66193061}, - {164721755, 66082092}, - {164721160, 66078750}, - {164688476, 65914146}, - {164668426, 65859436}, - {164563110, 65765937}, - {164431152, 65715034}, - {163997619, 65550788}, - {163946426, 65531440}, - {162998107, 65173629}, - {162664978, 65049140}, - {162482696, 64991668}, - {162464660, 64989639}, - {148029083, 66896141}, - {147862396, 66932853}, - {130087829, 73341102}, - {129791564, 73469726}, - {100590927, 90307685}, - {100483535, 90373847}, - {100364990, 90458930}, - {96447448, 93276664}, - {95179656, 94189010}, - {93692718, 95260208}, - {87904327, 99430885}, - {87663711, 99606147}, - {87576202, 99683990}, - {87498199, 99801719}, - {85740264, 104173728}, - {85538925, 104710494}, - {84786132, 107265830}, - {84635955, 107801383}, - {84619506, 107868064}, - {84518463, 108287200}, - {84456848, 108613471}, - {84419158, 108826194}, - {84375244, 109093818}, - {84329818, 109435180}, - {84249862, 110179664}, - {84218429, 110572166}, - {83630020, 117995208}, - {83595535, 118787673}, - {83576217, 119290679}, - {83617141, 120264900}, - }, - { - {91735549, 117640846}, - {91748252, 117958145}, - {91823547, 118515449}, - {92088752, 119477249}, - {97995346, 140538452}, - {98031051, 140660446}, - {98154449, 141060241}, - {98179855, 141133758}, - {98217056, 141232849}, - {98217147, 141233047}, - {98269256, 141337051}, - {98298950, 141387954}, - {98337753, 141445755}, - {99455047, 142984451}, - {99656250, 143247344}, - {102567855, 146783752}, - {102685150, 146906845}, - {102828948, 147031250}, - {102972457, 147120452}, - {103676147, 147539642}, - {103758956, 147586151}, - {103956756, 147682144}, - {104479949, 147931457}, - {104744453, 148044143}, - {104994750, 148123443}, - {105375648, 148158645}, - {109266250, 148178253}, - {109447753, 148169052}, - {109693649, 148129150}, - {113729949, 147337448}, - {113884552, 147303054}, - {115155349, 146956146}, - {117637145, 146174346}, - {154694046, 134048049}, - {156979949, 133128555}, - {157076843, 133059356}, - {157125045, 133001449}, - {157561340, 132300750}, - {157865753, 131795959}, - {157923156, 131667358}, - {158007049, 131297653}, - {158112747, 130777053}, - {158116653, 130640853}, - {158268951, 119981643}, - {158260040, 119824752}, - {158229949, 119563751}, - {149914047, 73458648}, - {149877548, 73331748}, - {144460754, 66413558}, - {144230545, 66153152}, - {144128051, 66075057}, - {143974853, 65973152}, - {142812744, 65353149}, - {141810943, 64837249}, - {141683349, 64805152}, - {141505157, 64784652}, - {108214355, 61896251}, - {107826354, 61866352}, - {107072151, 61821750}, - {106938850, 61873550}, - {106584251, 62055152}, - {106419952, 62147548}, - {100459152, 65546951}, - {100343849, 65615150}, - {100198852, 65716949}, - {99825149, 65979751}, - {94619247, 70330352}, - {94492355, 70480850}, - {94445846, 70547355}, - {94425354, 70588752}, - {94379753, 70687652}, - {94110252, 71443450}, - {94095252, 71569053}, - {91737251, 117308746}, - {91731048, 117430946}, - {91735549, 117640846}, - }, - { - {108231399, 111763748}, - {108335403, 111927955}, - {108865203, 112754745}, - {109206703, 113283851}, - {127117500, 125545951}, - {127212097, 125560951}, - {127358497, 125563652}, - {131348007, 125551147}, - {131412002, 125550849}, - {131509506, 125535446}, - {131579391, 125431343}, - {132041000, 124735656}, - {132104690, 124637847}, - {144108505, 100950546}, - {144120605, 100853042}, - {144123291, 100764648}, - {144122695, 100475143}, - {144086898, 85637748}, - {144083602, 85549346}, - {144071105, 85451843}, - {144007003, 85354545}, - {143679595, 84864547}, - {143468597, 84551048}, - {143367889, 84539146}, - {109847702, 84436347}, - {109684700, 84458953}, - {105946502, 89406143}, - {105915901, 91160446}, - {105880905, 93187744}, - {105876701, 93441345}, - {108231399, 111763748}, - }, - { - {102614700, 117684249}, - {102675102, 118074157}, - {102888999, 118743148}, - {103199707, 119517555}, - {103446800, 120099655}, - {103488204, 120193450}, - {104063903, 121373947}, - {104535499, 122192245}, - {104595802, 122295249}, - {104663002, 122402854}, - {104945701, 122854858}, - {105740501, 124038848}, - {106809700, 125479354}, - {107564399, 126380050}, - {108116203, 126975646}, - {123724700, 142516540}, - {124938400, 143705444}, - {127919601, 146599243}, - {128150894, 146821456}, - {128251602, 146917251}, - {128383605, 147041839}, - {128527709, 147176147}, - {128685699, 147321456}, - {128861007, 147481246}, - {132825103, 151046661}, - {133005493, 151205657}, - {133389007, 151488143}, - {133896499, 151858062}, - {134172302, 151991546}, - {134375000, 152063140}, - {135316101, 152300949}, - {136056304, 152220947}, - {136242706, 152186843}, - {136622207, 152016448}, - {136805404, 151908355}, - {147099594, 145766845}, - {147246704, 144900756}, - {147387603, 144048461}, - {144353698, 99345855}, - {144333801, 99232254}, - {144244598, 98812850}, - {144228698, 98757858}, - {144174606, 98616455}, - {133010101, 72396743}, - {132018905, 70280853}, - {130667404, 67536949}, - {129167297, 64854446}, - {128569198, 64098350}, - {124458503, 59135948}, - {124260597, 58946949}, - {123908706, 58658851}, - {123460098, 58327850}, - {122674499, 57840648}, - {122041801, 57712150}, - {121613403, 57699047}, - {121359901, 57749351}, - {121123199, 57826450}, - {120953498, 57882247}, - {120431701, 58198547}, - {120099205, 58599349}, - {119892303, 58903049}, - {102835296, 115179351}, - {102686599, 115817245}, - {102612396, 116540557}, - {102614700, 117684249}, - }, - { - {98163757, 71203430}, - {98212463, 73314544}, - {98326538, 74432693}, - {98402908, 75169799}, - {98524154, 76328353}, - {99088806, 79911361}, - {99304885, 80947769}, - {100106689, 84244186}, - {100358123, 85080337}, - {101715545, 89252807}, - {101969528, 89987213}, - {107989440, 106391418}, - {126299575, 140277343}, - {127061813, 141486663}, - {127405746, 141872253}, - {127846908, 142318450}, - {130818496, 145301574}, - {134366424, 148100921}, - {135308380, 148798828}, - {135745666, 149117523}, - {136033020, 149251800}, - {136500579, 149387725}, - {136662719, 149418395}, - {136973922, 149474822}, - {137184890, 149484375}, - {137623748, 149434356}, - {137830810, 149355072}, - {138681732, 148971343}, - {139374465, 148463409}, - {139589187, 148264312}, - {139809707, 148010711}, - {139985610, 147685028}, - {140196029, 147284973}, - {140355834, 146978668}, - {142079666, 142575622}, - {146702194, 129469726}, - {151285888, 113275238}, - {151543731, 112046264}, - {151701629, 110884704}, - {151837020, 108986206}, - {151837097, 107724029}, - {151760101, 106529205}, - {151581970, 105441925}, - {151577301, 105413757}, - {151495269, 105014709}, - {151393142, 104551513}, - {151058502, 103296112}, - {150705520, 102477264}, - {150137725, 101686370}, - {149427032, 100938537}, - {102979965, 60772064}, - {101930953, 60515609}, - {101276748, 60634414}, - {100717803, 60918136}, - {100125732, 61584625}, - {99618148, 62413436}, - {99457214, 62709442}, - {99368347, 62914794}, - {99166992, 63728332}, - {98313827, 69634780}, - {98176910, 70615707}, - {98162902, 70798233}, - {98163757, 71203430}, - }, - { - {79090698, 116426399}, - {80959800, 137087692}, - {81030303, 137762298}, - {81190704, 138903503}, - {81253700, 139084197}, - {81479301, 139544998}, - {81952003, 140118896}, - {82319900, 140523895}, - {82967803, 140993896}, - {83022903, 141032104}, - {83777900, 141493606}, - {84722099, 141849899}, - {84944396, 141887207}, - {86144699, 141915893}, - {87643997, 141938095}, - {88277503, 141887695}, - {88582099, 141840606}, - {89395401, 141712203}, - {90531204, 141528396}, - {91014801, 141438400}, - {92097595, 141190093}, - {123348297, 132876998}, - {123399505, 132860000}, - {123452804, 132841506}, - {123515502, 132818908}, - {123543800, 132806198}, - {124299598, 132437393}, - {124975502, 132042098}, - {125047500, 131992202}, - {125119506, 131930603}, - {166848800, 86317703}, - {168976409, 83524902}, - {169359603, 82932701}, - {169852600, 81917800}, - {170686904, 79771202}, - {170829406, 79245597}, - {170885498, 78796295}, - {170909301, 78531898}, - {170899703, 78238700}, - {170842803, 77553199}, - {170701293, 76723495}, - {170302307, 75753898}, - {169924301, 75067398}, - {169359802, 74578796}, - {168148605, 73757499}, - {163261596, 71124702}, - {162986007, 70977798}, - {162248703, 70599098}, - {158193405, 68923995}, - {157514297, 68667495}, - {156892700, 68495201}, - {156607299, 68432998}, - {154301895, 68061904}, - {93440299, 68061904}, - {88732002, 68255996}, - {88627304, 68298500}, - {88111396, 68541900}, - {86393898, 69555404}, - {86138298, 69706695}, - {85871704, 69913200}, - {85387199, 70393402}, - {79854499, 76783203}, - {79209701, 77649398}, - {79108505, 78072502}, - {79090698, 78472198}, - {79090698, 116426399}, - }, - { - {90956314, 84639938}, - {91073814, 85141891}, - {91185752, 85505371}, - {109815368, 137196487}, - {110342590, 138349899}, - {110388549, 138447540}, - {110652862, 138971343}, - {110918045, 139341140}, - {114380859, 143159042}, - {114446723, 143220352}, - {114652198, 143392166}, - {114712196, 143437301}, - {114782165, 143476028}, - {114873054, 143514923}, - {115217086, 143660934}, - {115306060, 143695526}, - {115344009, 143707580}, - {115444541, 143737747}, - {115589378, 143779937}, - {115751358, 143823989}, - {115802780, 143825820}, - {116872810, 143753616}, - {116927055, 143744644}, - {154690734, 133504180}, - {155009704, 133371856}, - {155029907, 133360061}, - {155089141, 133323181}, - {155342315, 133163360}, - {155602294, 132941406}, - {155669158, 132880294}, - {155821624, 132737884}, - {155898986, 132656890}, - {155934936, 132608932}, - {155968627, 132562713}, - {156062896, 132431808}, - {156111694, 132363174}, - {156148147, 132297180}, - {158738342, 127281066}, - {159026672, 126378631}, - {159073699, 125806335}, - {159048522, 125299743}, - {159040313, 125192901}, - {158898300, 123934677}, - {149829376, 70241508}, - {149763031, 69910629}, - {149684692, 69628723}, - {149557800, 69206214}, - {149366485, 68864326}, - {149137390, 68578514}, - {148637466, 68048767}, - {147027725, 66632934}, - {146228607, 66257507}, - {146061309, 66184646}, - {146017929, 66174186}, - {145236465, 66269500}, - {144802490, 66345039}, - {144673995, 66376220}, - {93732284, 79649864}, - {93345336, 79785865}, - {93208084, 79840286}, - {92814521, 79997779}, - {92591087, 80098968}, - {92567016, 80110511}, - {92032684, 80860725}, - {91988853, 80930152}, - {91471725, 82210029}, - {91142349, 83076683}, - {90969284, 83653182}, - {90929664, 84043212}, - {90926315, 84325256}, - {90956314, 84639938}, - }, - { - {114758499, 88719909}, - {114771591, 88860549}, - {115515533, 94195907}, - {115559539, 94383651}, - {119882980, 109502059}, - {120660522, 111909683}, - {126147735, 124949630}, - {127127212, 127107215}, - {129976379, 132117279}, - {130754470, 133257080}, - {130820968, 133340835}, - {130889312, 133423858}, - {131094787, 133652832}, - {131257629, 133828247}, - {131678619, 134164276}, - {131791107, 134248901}, - {131969482, 134335189}, - {132054107, 134373718}, - {132927368, 134701141}, - {133077072, 134749313}, - {133196075, 134785705}, - {133345230, 134804351}, - {133498809, 134809051}, - {133611541, 134797607}, - {134621170, 134565322}, - {134741165, 134527511}, - {134892089, 134465240}, - {135071212, 134353820}, - {135252029, 134185821}, - {135384979, 134003631}, - {135615585, 133576675}, - {135793029, 132859008}, - {135890228, 131382904}, - {135880828, 131261657}, - {135837570, 130787963}, - {135380661, 127428909}, - {132830596, 109495368}, - {132815826, 109411666}, - {132765869, 109199302}, - {132724380, 109068161}, - {127490066, 93353515}, - {125330810, 87852828}, - {125248336, 87647026}, - {125002182, 87088424}, - {124894592, 86872482}, - {121007278, 80019584}, - {120962829, 79941261}, - {120886489, 79833923}, - {120154983, 78949615}, - {119366561, 78111709}, - {119014755, 77776794}, - {116728790, 75636238}, - {116660522, 75593933}, - {116428192, 75458541}, - {116355255, 75416870}, - {116264663, 75372528}, - {115952728, 75233367}, - {115865554, 75205482}, - {115756835, 75190956}, - {115564163, 75197830}, - {115481170, 75202087}, - {115417144, 75230400}, - {115226959, 75337806}, - {115203842, 75351448}, - {114722015, 75746932}, - {114672103, 75795661}, - {114594619, 75891891}, - {114565811, 75973831}, - {114478256, 76240814}, - {114178039, 77252197}, - {114137664, 77769668}, - {114109771, 78154464}, - {114758499, 88719909}, - }, - { - {108135070, 109828002}, - {108200347, 110091529}, - {108319419, 110298500}, - {108439025, 110488388}, - {108663574, 110766731}, - {108812957, 110935768}, - {109321914, 111398925}, - {109368087, 111430320}, - {109421295, 111466331}, - {110058998, 111849746}, - {127160308, 120588981}, - {127350692, 120683456}, - {128052749, 120997207}, - {128326919, 121113449}, - {131669586, 122213058}, - {131754745, 122240592}, - {131854583, 122264770}, - {132662048, 122449813}, - {132782669, 122449897}, - {132909118, 122443687}, - {133013442, 122436058}, - {140561035, 121609939}, - {140786346, 121583320}, - {140876144, 121570228}, - {140962356, 121547996}, - {141052612, 121517837}, - {141231292, 121442184}, - {141309371, 121390007}, - {141370132, 121327003}, - {141456008, 121219932}, - {141591598, 121045005}, - {141905761, 120634796}, - {141894607, 120305725}, - {141881881, 120110855}, - {141840881, 119885009}, - {141685043, 119238922}, - {141617416, 118962882}, - {141570434, 118858856}, - {131617462, 100598548}, - {131542846, 100487213}, - {131229385, 100089019}, - {131091476, 99928108}, - {119824127, 90297180}, - {119636337, 90142387}, - {119507492, 90037765}, - {119436744, 89983657}, - {119423942, 89974159}, - {119207366, 89822471}, - {119117149, 89767097}, - {119039489, 89726867}, - {116322929, 88522857}, - {114817031, 87882110}, - {114683975, 87826751}, - {114306411, 87728507}, - {113876434, 87646003}, - {113792106, 87629974}, - {113658988, 87615974}, - {113574333, 87609275}, - {112813575, 87550102}, - {112578567, 87560157}, - {112439880, 87571647}, - {112306922, 87599395}, - {112225082, 87622535}, - {112132568, 87667175}, - {112103477, 87682830}, - {110795242, 88511634}, - {110373565, 88847793}, - {110286537, 88934989}, - {109730873, 89531501}, - {109648735, 89628883}, - {109552581, 89768859}, - {109514228, 89838470}, - {109501640, 89877586}, - {109480964, 89941864}, - {109461761, 90032417}, - {109457778, 90055458}, - {108105194, 109452575}, - {108094238, 109620979}, - {108135070, 109828002}, - }, - { - {108764694, 108910400}, - {108965499, 112306495}, - {109598602, 120388298}, - {110573898, 128289596}, - {110597801, 128427795}, - {113786201, 137983795}, - {113840301, 138134704}, - {113937202, 138326904}, - {114046005, 138520401}, - {114150802, 138696792}, - {114164703, 138717895}, - {114381896, 139021194}, - {114701004, 139425292}, - {114997398, 139747497}, - {115065597, 139805191}, - {115134498, 139850891}, - {115167098, 139871704}, - {115473396, 139992797}, - {115537498, 139995101}, - {116762596, 139832000}, - {116897499, 139808593}, - {118401802, 139225585}, - {118437500, 139209594}, - {118488204, 139182189}, - {118740097, 139033996}, - {118815795, 138967285}, - {134401000, 116395492}, - {134451507, 116309997}, - {135488098, 113593597}, - {137738006, 106775695}, - {140936492, 97033889}, - {140960006, 96948997}, - {141026504, 96660995}, - {141067291, 96467094}, - {141124893, 95771896}, - {141511795, 90171600}, - {141499801, 90026000}, - {141479598, 89907798}, - {141276794, 88844596}, - {141243804, 88707397}, - {140778305, 87031593}, - {140733306, 86871696}, - {140697204, 86789993}, - {140619796, 86708190}, - {140398391, 86487396}, - {125798797, 72806198}, - {125415802, 72454498}, - {123150398, 70566093}, - {123038803, 70503997}, - {122681198, 70305397}, - {121919204, 70104797}, - {121533699, 70008094}, - {121273696, 70004898}, - {121130599, 70020797}, - {121045097, 70033294}, - {120847099, 70082298}, - {120481895, 70278999}, - {120367004, 70379692}, - {120272796, 70475097}, - {119862098, 71004791}, - {119745101, 71167297}, - {119447799, 71726997}, - {119396499, 71825798}, - {119348701, 71944496}, - {109508796, 98298797}, - {109368598, 98700897}, - {109298400, 98926391}, - {108506301, 102750991}, - {108488197, 102879898}, - {108764694, 108910400}, - }, - { - {106666252, 87231246}, - {106673248, 87358055}, - {107734146, 101975646}, - {107762649, 102357955}, - {108702445, 111208351}, - {108749450, 111345153}, - {108848350, 111542648}, - {110270645, 114264358}, - {110389648, 114445144}, - {138794845, 143461151}, - {139048355, 143648956}, - {139376144, 143885345}, - {139594451, 144022644}, - {139754043, 144110046}, - {139923950, 144185852}, - {140058242, 144234451}, - {140185653, 144259552}, - {140427551, 144292648}, - {141130950, 144281448}, - {141157653, 144278152}, - {141214355, 144266555}, - {141347457, 144223449}, - {141625350, 144098953}, - {141755142, 144040145}, - {141878143, 143971557}, - {142011444, 143858154}, - {142076843, 143796356}, - {142160644, 143691055}, - {142224456, 143560852}, - {142925842, 142090850}, - {142935653, 142065353}, - {142995956, 141899154}, - {143042556, 141719757}, - {143102951, 141436157}, - {143129257, 141230453}, - {143316055, 139447250}, - {143342544, 133704650}, - {143307556, 130890960}, - {142461257, 124025558}, - {141916046, 120671051}, - {141890457, 120526153}, - {140002349, 113455749}, - {139909149, 113144149}, - {139853454, 112974456}, - {137303756, 105228057}, - {134700546, 98161254}, - {134617950, 97961547}, - {133823547, 96118057}, - {133688751, 95837356}, - {133481353, 95448059}, - {133205444, 94948150}, - {131178955, 91529853}, - {131144744, 91482055}, - {113942047, 67481246}, - {113837051, 67360549}, - {113048950, 66601745}, - {112305549, 66002746}, - {112030853, 65790351}, - {111970649, 65767547}, - {111912445, 65755249}, - {111854248, 65743453}, - {111657447, 65716354}, - {111576950, 65707351}, - {111509750, 65708549}, - {111443550, 65718551}, - {111397247, 65737449}, - {111338546, 65764648}, - {111129547, 65863349}, - {111112449, 65871551}, - {110995254, 65927856}, - {110968849, 65946151}, - {110941444, 65966751}, - {110836448, 66057853}, - {110490447, 66445449}, - {110404144, 66576751}, - {106802055, 73202148}, - {106741950, 73384948}, - {106715454, 73469650}, - {106678054, 73627151}, - {106657455, 75433448}, - {106666252, 87231246}, - }, - { - {101852752, 106261352}, - {101868949, 106406051}, - {102347549, 108974250}, - {112286750, 152027954}, - {112305648, 152106536}, - {112325752, 152175857}, - {112391448, 152290863}, - {113558250, 154187454}, - {113592048, 154226745}, - {113694351, 154313156}, - {113736549, 154335647}, - {113818145, 154367462}, - {114284454, 154490951}, - {114415847, 154504547}, - {114520751, 154489151}, - {114571350, 154478057}, - {114594551, 154472854}, - {114630546, 154463958}, - {114715148, 154429443}, - {146873657, 136143051}, - {146941741, 136074249}, - {147190155, 135763549}, - {147262649, 135654937}, - {147309951, 135557159}, - {147702255, 133903945}, - {147934143, 131616348}, - {147967041, 131273864}, - {148185852, 127892250}, - {148195648, 127669754}, - {148179656, 126409851}, - {148119552, 126182151}, - {147874053, 125334152}, - {147818954, 125150352}, - {146958557, 122656646}, - {139070251, 101025955}, - {139002655, 100879051}, - {119028450, 63067649}, - {118846649, 62740753}, - {115676048, 57814651}, - {115550453, 57629852}, - {115330352, 57319751}, - {115094749, 56998352}, - {114978347, 56847454}, - {114853050, 56740550}, - {114695053, 56609550}, - {114582252, 56528148}, - {114210449, 56375953}, - {113636245, 56214950}, - {113470352, 56171649}, - {109580749, 55503551}, - {109491645, 55495452}, - {109238754, 55511550}, - {109080352, 55534049}, - {108027748, 55687351}, - {107839950, 55732349}, - {107614456, 55834953}, - {107488143, 55925952}, - {107302551, 56062553}, - {107218353, 56145751}, - {107199447, 56167251}, - {107052749, 56354850}, - {106978652, 56476348}, - {106869644, 56710754}, - {104541351, 62448753}, - {104454551, 62672554}, - {104441253, 62707351}, - {104231750, 63366348}, - {104222648, 63419952}, - {104155746, 63922649}, - {104127349, 64147552}, - {104110847, 64299957}, - {102235450, 92366752}, - {101804351, 102877655}, - {101852752, 106261352}, - }, - { - {106808700, 120885696}, - {106818695, 120923103}, - {106873901, 121057098}, - {115123603, 133614700}, - {115128799, 133619598}, - {115182197, 133661804}, - {115330101, 133740707}, - {115455398, 133799407}, - {115595001, 133836807}, - {115651000, 133851806}, - {116413604, 134055206}, - {116654495, 134097900}, - {116887603, 134075210}, - {117071098, 134040405}, - {117458801, 133904891}, - {118057998, 133572601}, - {118546997, 133261001}, - {118578498, 133239395}, - {118818603, 133011596}, - {121109695, 130501495}, - {122661598, 128760101}, - {142458190, 102765197}, - {142789001, 102099601}, - {143105010, 101386505}, - {143154800, 101239700}, - {143193908, 100825500}, - {143160507, 100282501}, - {143133499, 100083602}, - {143092697, 99880500}, - {143050689, 99766700}, - {142657501, 98974502}, - {142580307, 98855201}, - {122267196, 76269897}, - {122036399, 76105003}, - {121832000, 76028305}, - {121688796, 75983108}, - {121591598, 75955001}, - {121119697, 75902099}, - {120789596, 75953498}, - {120487495, 76041900}, - {120042701, 76365798}, - {119886695, 76507301}, - {119774200, 76635299}, - {119739097, 76686904}, - {119685195, 76798202}, - {119456199, 77320098}, - {106877601, 119561401}, - {106854797, 119645103}, - {106849098, 119668807}, - {106847099, 119699005}, - {106840400, 119801406}, - {106807800, 120719299}, - {106806098, 120862808}, - {106808700, 120885696}, - }, - { - {99663352, 105328948}, - {99690048, 105797050}, - {99714050, 105921447}, - {99867248, 106439949}, - {100111557, 107256546}, - {104924850, 120873649}, - {105106155, 121284049}, - {105519149, 122184753}, - {105586051, 122292655}, - {105665054, 122400154}, - {106064147, 122838455}, - {106755355, 123453453}, - {106929054, 123577651}, - {107230346, 123771949}, - {107760650, 123930648}, - {108875854, 124205154}, - {108978752, 124228050}, - {131962051, 123738754}, - {135636047, 123513954}, - {135837249, 123500747}, - {136357345, 123442749}, - {136577346, 123394454}, - {136686645, 123367752}, - {137399353, 123185050}, - {137733947, 123063156}, - {137895355, 122997154}, - {138275650, 122829154}, - {138394256, 122767753}, - {138516845, 122670150}, - {139987045, 121111251}, - {149171646, 108517349}, - {149274353, 108372848}, - {149314758, 108314247}, - {149428848, 108140846}, - {149648651, 107650550}, - {149779541, 107290252}, - {149833343, 107115249}, - {149891357, 106920051}, - {150246353, 105630249}, - {150285842, 105423454}, - {150320953, 105233749}, - {150336639, 104981552}, - {150298049, 104374053}, - {150287948, 104271850}, - {150026153, 103481147}, - {149945449, 103301651}, - {149888946, 103213455}, - {149800949, 103103851}, - {149781143, 103079650}, - {149714141, 103005447}, - {149589950, 102914146}, - {149206054, 102698951}, - {128843856, 91378150}, - {128641754, 91283050}, - {119699851, 87248046}, - {117503555, 86311950}, - {117145851, 86178054}, - {116323654, 85925048}, - {115982551, 85834045}, - {115853050, 85819252}, - {115222549, 85771949}, - {107169357, 85771949}, - {107122650, 85776451}, - {106637145, 85831550}, - {105095046, 86423950}, - {104507850, 86703750}, - {104384155, 86763153}, - {104332351, 86790145}, - {104198257, 86882644}, - {103913757, 87109451}, - {103592346, 87388450}, - {103272651, 87666748}, - {103198051, 87779052}, - {101698654, 90600952}, - {101523551, 90958450}, - {101360054, 91347450}, - {101295349, 91542144}, - {99774551, 98278152}, - {99746749, 98417755}, - {99704055, 98675453}, - {99663352, 99022949}, - {99663352, 105328948}, - }, - { - {95036499, 101778106}, - {95479103, 102521301}, - {95587295, 102700103}, - {98306503, 106984901}, - {98573303, 107377700}, - {100622406, 110221702}, - {101252304, 111089599}, - {104669502, 115750198}, - {121838500, 131804107}, - {122000503, 131943695}, - {122176803, 132023406}, - {122474105, 132025390}, - {122703804, 132023101}, - {123278808, 131878112}, - {124072998, 131509109}, - {124466506, 131102508}, - {152779296, 101350906}, - {153016510, 101090606}, - {153269699, 100809097}, - {153731994, 100214096}, - {153927902, 99939796}, - {154641098, 98858100}, - {154864303, 98517601}, - {155056594, 97816604}, - {155083511, 97645599}, - {155084899, 97462097}, - {154682601, 94386100}, - {154376007, 92992599}, - {154198593, 92432403}, - {153830505, 91861701}, - {153686904, 91678695}, - {151907104, 90314605}, - {151368896, 89957603}, - {146983306, 87632202}, - {139082397, 84273605}, - {128947692, 80411399}, - {121179000, 78631301}, - {120264701, 78458198}, - {119279510, 78304603}, - {116913101, 77994102}, - {116151504, 77974601}, - {115435104, 78171401}, - {113544105, 78709106}, - {113231002, 78879898}, - {112726303, 79163604}, - {112310501, 79411102}, - {96169998, 97040802}, - {95196304, 98364402}, - {95167800, 98409599}, - {95083503, 98570701}, - {94986999, 99022201}, - {94915100, 100413299}, - {95036499, 101778106}, - }, - { - {82601348, 96004745}, - {83443847, 128861953}, - {84173248, 136147354}, - {104268249, 141388839}, - {104373649, 141395355}, - {105686950, 141389541}, - {149002243, 140435653}, - {159095748, 133388244}, - {159488143, 133112655}, - {159661849, 132894653}, - {163034149, 128290847}, - {164801849, 124684249}, - {167405746, 72553245}, - {167330444, 71960746}, - {167255050, 71791847}, - {167147155, 71572044}, - {166999557, 71341545}, - {166723937, 70961448}, - {166238250, 70611541}, - {165782348, 70359649}, - {165649444, 70286849}, - {165332946, 70122344}, - {165164154, 70062248}, - {164879150, 69967544}, - {164744949, 69928947}, - {164691452, 69915245}, - {164669448, 69910247}, - {159249938, 68738952}, - {158528259, 68704742}, - {147564254, 68604644}, - {116196655, 68982742}, - {115364944, 69005050}, - {115193145, 69013549}, - {101701248, 70984146}, - {93918449, 72233047}, - {93789749, 72285247}, - {93777046, 72292648}, - {93586044, 72444046}, - {93366348, 72662345}, - {93301147, 72745452}, - {93260345, 72816345}, - {83523948, 92593849}, - {83430145, 92810241}, - {82815048, 94665542}, - {82755554, 94858551}, - {82722953, 95014350}, - {82594253, 95682350}, - {82601348, 96004745}, - }, - { - {110371345, 125796493}, - {110411544, 126159599}, - {110445251, 126362899}, - {111201950, 127863800}, - {112030052, 129270492}, - {112367050, 129799301}, - {113088348, 130525604}, - {113418144, 130853698}, - {117363449, 134705505}, - {118131149, 135444793}, - {118307449, 135607299}, - {119102546, 136297195}, - {119385047, 136531906}, - {120080848, 137094390}, - {120794845, 137645401}, - {121150344, 137896392}, - {121528945, 138162506}, - {121644546, 138242095}, - {122142349, 138506408}, - {127540847, 141363006}, - {127933448, 141516204}, - {128728256, 141766799}, - {129877151, 141989898}, - {130626052, 142113891}, - {130912246, 142135192}, - {131246841, 142109100}, - {131496047, 142027404}, - {131596252, 141957794}, - {131696350, 141873504}, - {131741043, 141803405}, - {138788452, 128037704}, - {139628646, 125946197}, - {138319351, 112395401}, - {130035354, 78066703}, - {124174049, 69908798}, - {123970649, 69676895}, - {123874252, 69571899}, - {123246643, 68961303}, - {123193954, 68924400}, - {121952049, 68110000}, - {121787345, 68021896}, - {121661544, 67970306}, - {121313446, 67877502}, - {121010650, 67864799}, - {120995346, 67869705}, - {120583747, 68122207}, - {120509750, 68170600}, - {120485847, 68189102}, - {112160148, 77252403}, - {111128646, 78690704}, - {110969650, 78939407}, - {110512550, 79663406}, - {110397247, 79958206}, - {110371345, 80038299}, - {110371345, 125796493}, - }, - { - {112163948, 137752700}, - {112171150, 137837997}, - {112203048, 137955993}, - {112240150, 138008209}, - {112343246, 138111099}, - {112556243, 138223205}, - {112937149, 138307998}, - {113318748, 138331909}, - {126076446, 138428298}, - {126165245, 138428695}, - {126312446, 138417907}, - {134075546, 136054504}, - {134322753, 135949401}, - {134649948, 135791198}, - {135234954, 135493408}, - {135290145, 135464691}, - {135326248, 135443695}, - {135920043, 135032592}, - {135993850, 134975799}, - {136244247, 134761199}, - {136649444, 134378692}, - {137067153, 133964294}, - {137188156, 133839096}, - {137298049, 133704498}, - {137318954, 133677795}, - {137413543, 133522201}, - {137687347, 133043792}, - {137816055, 132660705}, - {137836044, 131747695}, - {137807144, 131318603}, - {136279342, 119078704}, - {136249053, 118945800}, - {127306152, 81348602}, - {127114852, 81065505}, - {127034248, 80951400}, - {126971649, 80893707}, - {125093551, 79178001}, - {124935745, 79036003}, - {115573745, 71767601}, - {115411148, 71701805}, - {115191947, 71621002}, - {115017051, 71571304}, - {114870147, 71572898}, - {113869552, 71653900}, - {112863349, 72976104}, - {112756347, 73223899}, - {112498947, 73832206}, - {112429351, 73998504}, - {112366050, 74168098}, - {112273246, 74487098}, - {112239250, 74605400}, - {112195549, 74899902}, - {112163948, 75280700}, - {112163948, 137752700}, - }, - { - {78562347, 141451843}, - {79335624, 142828186}, - {79610343, 143188140}, - {79845077, 143445724}, - {81379173, 145126678}, - {81826751, 145577178}, - {82519126, 146209472}, - {83964973, 147280502}, - {85471343, 148377868}, - {86115539, 148760803}, - {88839988, 150281188}, - {89021247, 150382217}, - {90775917, 151320526}, - {91711380, 151767288}, - {92757591, 152134277}, - {93241058, 152201766}, - {113402145, 153091995}, - {122065994, 146802825}, - {164111053, 91685104}, - {164812759, 90470565}, - {165640182, 89037384}, - {171027435, 66211853}, - {171450805, 64406951}, - {171463150, 64349624}, - {171469787, 64317184}, - {171475585, 64282028}, - {171479812, 64253036}, - {171483596, 64210433}, - {171484405, 64153488}, - {171483001, 64140785}, - {171481719, 64132751}, - {171478668, 64115478}, - {171472702, 64092437}, - {171462768, 64075408}, - {171448089, 64061347}, - {171060333, 63854789}, - {169640502, 63197738}, - {169342147, 63086711}, - {166413101, 62215766}, - {151881774, 58826736}, - {146010574, 57613151}, - {141776962, 56908004}, - {140982940, 57030628}, - {139246154, 57540817}, - {139209609, 57566974}, - {127545310, 66015594}, - {127476654, 66104812}, - {105799087, 98784980}, - {85531921, 129338897}, - {79319717, 138704513}, - {78548156, 140188079}, - {78530448, 140530456}, - {78515594, 141299987}, - {78562347, 141451843}, - }, - { - {77755004, 128712387}, - {78073547, 130552612}, - {78433593, 132017822}, - {79752693, 136839645}, - {80479461, 138929260}, - {80903221, 140119674}, - {81789848, 141978454}, - {82447387, 143105575}, - {83288436, 144264328}, - {84593582, 145846542}, - {84971939, 146242813}, - {86905578, 147321304}, - {87874191, 147594131}, - {89249092, 147245132}, - {89541542, 147169052}, - {98759140, 144071609}, - {98894233, 144024261}, - {113607818, 137992843}, - {128324356, 131649307}, - {139610076, 126210189}, - {146999572, 122112884}, - {147119415, 122036041}, - {148717330, 120934616}, - {149114776, 120652725}, - {171640289, 92086624}, - {171677917, 92036224}, - {171721191, 91973869}, - {171851608, 91721557}, - {171927795, 91507644}, - {172398696, 89846351}, - {172436752, 89559959}, - {169361663, 64753852}, - {169349029, 64687164}, - {169115127, 63616458}, - {168965728, 63218254}, - {168911788, 63121219}, - {168901611, 63106807}, - {168896896, 63100486}, - {168890686, 63092460}, - {168876586, 63081058}, - {168855529, 63067909}, - {168808746, 63046024}, - {167251068, 62405864}, - {164291717, 63716899}, - {152661651, 69910156}, - {142312393, 75421356}, - {78778053, 111143295}, - {77887222, 113905914}, - {77591979, 124378433}, - {77563247, 126586669}, - {77755004, 128712387}, - }, - { - {105954101, 131182754}, - {105959197, 131275848}, - {105972801, 131473556}, - {105981498, 131571044}, - {106077903, 132298553}, - {106134094, 132715255}, - {106155700, 132832351}, - {106180099, 132942657}, - {106326797, 133590347}, - {106375099, 133719345}, - {106417602, 133829345}, - {106471000, 133930343}, - {106707901, 134308654}, - {106728401, 134340545}, - {106778198, 134417556}, - {106832397, 134491851}, - {106891296, 134562957}, - {106981300, 134667358}, - {107044204, 134736557}, - {107111000, 134802658}, - {107180999, 134865661}, - {107291099, 134961349}, - {107362998, 135020355}, - {107485397, 135112854}, - {107558998, 135166946}, - {107690399, 135256256}, - {107765098, 135305252}, - {107903594, 135390548}, - {108183898, 135561843}, - {108459503, 135727951}, - {108532501, 135771850}, - {108796096, 135920059}, - {108944099, 135972549}, - {109102401, 136010757}, - {109660598, 136071044}, - {109971595, 136100250}, - {110209594, 136116851}, - {110752799, 136122344}, - {111059906, 136105758}, - {111152900, 136100357}, - {111237197, 136091354}, - {111316101, 136075057}, - {111402000, 136050949}, - {111475296, 136026657}, - {143546600, 123535949}, - {143899002, 122454353}, - {143917404, 122394348}, - {143929199, 122354652}, - {143944793, 122295753}, - {143956207, 122250953}, - {143969497, 122192253}, - {143980102, 122143249}, - {143991302, 122083053}, - {144000396, 122031753}, - {144009796, 121970954}, - {144017303, 121917655}, - {144025405, 121850250}, - {144030609, 121801452}, - {144036804, 121727455}, - {144040008, 121683456}, - {144043502, 121600952}, - {144044708, 121565048}, - {144045700, 121470352}, - {144045898, 121446952}, - {144041503, 121108657}, - {144037506, 121023452}, - {143733795, 118731750}, - {140461395, 95238647}, - {140461105, 95236755}, - {140433807, 95115249}, - {140392608, 95011650}, - {134840805, 84668952}, - {134824996, 84642456}, - {134781494, 84572952}, - {134716796, 84480850}, - {127473899, 74425453}, - {127467002, 74417152}, - {127431701, 74381652}, - {127402603, 74357147}, - {127375503, 74334457}, - {127294906, 74276649}, - {127181900, 74207649}, - {127177597, 74205451}, - {127123901, 74178451}, - {127078903, 74155853}, - {127028999, 74133148}, - {126870803, 74070953}, - {126442901, 73917648}, - {126432403, 73914955}, - {126326004, 73889846}, - {126262405, 73880645}, - {126128097, 73878456}, - {125998199, 73877655}, - {108701095, 74516647}, - {108644599, 74519348}, - {108495201, 74528953}, - {108311302, 74556457}, - {108252799, 74569458}, - {108079002, 74612152}, - {107981399, 74638954}, - {107921295, 74657951}, - {107862197, 74685951}, - {107601303, 74828948}, - {107546997, 74863449}, - {107192794, 75098846}, - {107131202, 75151153}, - {106260002, 76066146}, - {106195098, 76221145}, - {106168502, 76328453}, - {106144699, 76437454}, - {106124496, 76538452}, - {106103698, 76649650}, - {106084197, 76761650}, - {106066299, 76874450}, - {106049903, 76987457}, - {106034797, 77101150}, - {106020904, 77214950}, - {106008201, 77328948}, - {105996902, 77443145}, - {105986099, 77565849}, - {105977005, 77679649}, - {105969299, 77793151}, - {105963096, 77906349}, - {105958297, 78019149}, - {105955299, 78131454}, - {105954101, 78242950}, - {105954101, 131182754}, - }, - { - {91355499, 77889205}, - {114834197, 120804504}, - {114840301, 120815200}, - {124701507, 132324798}, - {124798805, 132436706}, - {124901504, 132548309}, - {125126602, 132788909}, - {125235000, 132901901}, - {125337707, 133005401}, - {125546302, 133184707}, - {125751602, 133358703}, - {126133300, 133673004}, - {126263900, 133775604}, - {126367401, 133855499}, - {126471908, 133935104}, - {126596008, 134027496}, - {127119308, 134397094}, - {127135101, 134408203}, - {127433609, 134614303}, - {127554107, 134695709}, - {128155395, 135070907}, - {128274505, 135141799}, - {129132003, 135573211}, - {129438003, 135713195}, - {129556106, 135767196}, - {131512695, 136648498}, - {132294509, 136966598}, - {132798400, 137158798}, - {133203796, 137294494}, - {133377410, 137350799}, - {133522399, 137396606}, - {133804397, 137480697}, - {134017807, 137542205}, - {134288696, 137618408}, - {134564208, 137680099}, - {134844696, 137740097}, - {135202606, 137807098}, - {135489105, 137849807}, - {135626800, 137864898}, - {135766906, 137878692}, - {135972808, 137895797}, - {136110107, 137905502}, - {136235000, 137913101}, - {136485809, 137907196}, - {139194305, 136979202}, - {140318298, 136536209}, - {140380004, 136505004}, - {140668197, 136340499}, - {140724304, 136298904}, - {140808197, 136228210}, - {140861801, 136180603}, - {140917404, 136129104}, - {140979202, 136045104}, - {141022903, 135984207}, - {147591094, 126486999}, - {147661315, 126356101}, - {147706100, 126261901}, - {147749099, 126166000}, - {147817108, 126007507}, - {147859100, 125908599}, - {153693206, 111901100}, - {153731109, 111807800}, - {153760894, 111698806}, - {158641998, 92419303}, - {158644500, 92263702}, - {158539703, 92013504}, - {158499603, 91918899}, - {158335510, 91626800}, - {158264007, 91516304}, - {158216308, 91449203}, - {158178314, 91397506}, - {158094299, 91283203}, - {157396408, 90368202}, - {157285491, 90224700}, - {157169906, 90079200}, - {157050003, 89931304}, - {156290603, 89006805}, - {156221099, 88922897}, - {156087707, 88771003}, - {155947906, 88620498}, - {155348602, 88004203}, - {155113204, 87772796}, - {154947296, 87609703}, - {154776306, 87448204}, - {154588806, 87284301}, - {153886306, 86716400}, - {153682403, 86560501}, - {152966705, 86032402}, - {152687805, 85828704}, - {152484313, 85683204}, - {152278808, 85539001}, - {150878204, 84561401}, - {150683013, 84426498}, - {150599395, 84372703}, - {150395599, 84243202}, - {149988906, 83989395}, - {149782897, 83864501}, - {149568908, 83739799}, - {148872100, 83365303}, - {148625396, 83242202}, - {128079010, 73079605}, - {127980506, 73031005}, - {126701103, 72407104}, - {126501701, 72312202}, - {126431503, 72280601}, - {126311706, 72230606}, - {126260101, 72210899}, - {126191902, 72187599}, - {126140106, 72170303}, - {126088203, 72155303}, - {126036102, 72142700}, - {125965904, 72126899}, - {125913009, 72116600}, - {125859603, 72108505}, - {125788101, 72100296}, - {125733505, 72094398}, - {125678100, 72090400}, - {125621398, 72088302}, - {125548805, 72087303}, - {125490707, 72086898}, - {125430908, 72088203}, - {125369804, 72091094}, - {125306900, 72095306}, - {125233505, 72100997}, - {125168609, 72106506}, - {125102203, 72113601}, - {125034103, 72122207}, - {124964309, 72132095}, - {124890701, 72143707}, - {124819305, 72155105}, - {91355499, 77889099}, - {91355499, 77889205}, - }, - { - {84531845, 127391708}, - {84916946, 130417510}, - {86133247, 131166900}, - {86338447, 131292892}, - {86748847, 131544799}, - {102193946, 136599502}, - {103090942, 136796798}, - {103247146, 136822509}, - {104083549, 136911499}, - {106119346, 137109802}, - {106265853, 137122207}, - {106480247, 137139205}, - {110257850, 137133605}, - {116917747, 136131408}, - {117054946, 136106704}, - {119043945, 135244293}, - {119249046, 135154708}, - {136220947, 126833007}, - {165896347, 91517105}, - {166032546, 91314697}, - {166055435, 91204902}, - {166056152, 91176803}, - {166047256, 91100006}, - {166039733, 91063705}, - {165814849, 90080802}, - {165736450, 89837707}, - {165677246, 89732101}, - {165676956, 89731803}, - {165560241, 89629302}, - {154419952, 82608505}, - {153822143, 82239700}, - {137942749, 74046104}, - {137095245, 73845504}, - {135751342, 73537704}, - {134225952, 73208602}, - {132484344, 72860801}, - {124730346, 73902000}, - {120736549, 74464401}, - {100401245, 78685401}, - {90574645, 90625701}, - {90475944, 90748809}, - {90430747, 90808700}, - {90321548, 90958305}, - {90254852, 91077903}, - {90165641, 91244003}, - {90134941, 91302398}, - {84474647, 103745697}, - {84328048, 104137901}, - {84288543, 104327606}, - {84038047, 106164604}, - {84013351, 106368698}, - {83943847, 110643203}, - {84531845, 127391708}, - }, -}; - -const TestDataEx PRINTER_PART_POLYGONS_EX = -{ - { - { - {533726562, 142141690}, - {532359712, 143386134}, - {530141290, 142155145}, - {528649729, 160091460}, - {533659500, 157607547}, - {538669739, 160091454}, - {537178168, 142155145}, - {534959534, 143386102}, - {533726562, 142141690}, - }, - { - }, - }, - { - { - {118305840, 11603332}, - {118311095, 26616786}, - {113311095, 26611146}, - {109311095, 29604752}, - {109300760, 44608489}, - {109311095, 49631801}, - {113300790, 52636806}, - {118311095, 52636806}, - {118308782, 103636810}, - {223830940, 103636981}, - {236845321, 90642174}, - {236832882, 11630488}, - {232825251, 11616786}, - {210149075, 11616786}, - {211308596, 13625149}, - {209315325, 17080886}, - {205326885, 17080886}, - {203334352, 13629720}, - {204493136, 11616786}, - {118305840, 11603332}, - }, - { - }, - }, - { - { - {365619370, 111280336}, - {365609100, 198818091}, - {387109100, 198804367}, - {387109100, 203279701}, - {471129120, 203279688}, - {471128689, 111283937}, - {365619370, 111280336}, - }, - { - }, - }, - { - { - {479997525, 19177632}, - {477473010, 21975778}, - {475272613, 21969219}, - {475267479, 32995796}, - {477026388, 32995796}, - {483041428, 22582411}, - {482560272, 20318630}, - {479997525, 19177632}, - }, - { - }, - }, - { - { - {476809080, 4972372}, - {475267479, 4975778}, - {475272613, 16002357}, - {481018177, 18281994}, - {482638044, 15466085}, - {476809080, 4972372}, - }, - { - }, - }, - { - { - {424866064, 10276075}, - {415113411, 10277960}, - {411723180, 13685293}, - {410473354, 18784347}, - {382490868, 18784008}, - {380996185, 17286945}, - {380996185, 11278161}, - {375976165, 11284347}, - {375976165, 56389754}, - {375169018, 57784347}, - {371996185, 57784347}, - {371996185, 53779177}, - {364976165, 53784347}, - {364969637, 56791976}, - {369214608, 61054367}, - {371474507, 61054367}, - {371473155, 98298160}, - {378476349, 105317193}, - {407491306, 105307497}, - {413509785, 99284903}, - {413496185, 48304367}, - {419496173, 48315719}, - {422501887, 45292801}, - {422500504, 39363184}, - {420425079, 37284347}, - {419476165, 43284347}, - {413496185, 43284347}, - {413497261, 30797428}, - {418986175, 25308513}, - {424005230, 25315076}, - {428496185, 20815924}, - {428512720, 13948847}, - {424866064, 10276075}, - }, - { - }, - }, - { - { - {723893066, 37354349}, - {717673034, 37370791}, - {717673034, 44872138}, - {715673034, 44867768}, - {715673034, 46055353}, - {699219526, 40066777}, - {697880758, 37748547}, - {691985477, 37748293}, - {689014018, 42869257}, - {691985477, 48016003}, - {697575093, 48003007}, - {715671494, 54589493}, - {715656800, 87142158}, - {759954611, 87142158}, - {764193054, 82897328}, - {764193054, 79872138}, - {757173034, 79866968}, - {757173034, 83872138}, - {754419422, 83869509}, - {753193054, 81739327}, - {753193054, 37360571}, - {723893066, 37354349}, - }, - { - }, - }, - { - { - {85607478, 4227596}, - {61739211, 4230337}, - {61739211, 13231393}, - {58725066, 13231405}, - {58721589, 27731406}, - {58738375, 30262521}, - {61739211, 30251413}, - {61736212, 38251411}, - {70759231, 38254724}, - {70905600, 33317391}, - {73749222, 31251468}, - {76592843, 33317393}, - {76739211, 38254516}, - {86765007, 38251411}, - {86759599, 4231393}, - {85607478, 4227596}, - }, - { - }, - }, - { - { - {534839721, 53437770}, - {534839721, 60849059}, - {539898273, 63773857}, - {545461140, 63757881}, - {544859741, 53447836}, - {541839721, 53437862}, - {541710836, 56353878}, - {540193984, 57229659}, - {538859741, 53437862}, - {534839721, 53437770}, - }, - { - }, - }, - { - { - {756086230, 136598477}, - {732054387, 136605752}, - {732052489, 172629505}, - {756091994, 172627853}, - {756086230, 136598477}, - }, - { - }, - }, - { - { - {100337034, 79731391}, - {70296833, 79731391}, - {70311095, 92263567}, - {74329808, 96264260}, - {96344976, 96257215}, - {100344419, 92232243}, - {100337034, 79731391}, - }, - { - }, - }, - { - { - {102331115, 44216643}, - {67311095, 44217252}, - {67311095, 69250964}, - {74329808, 76264260}, - {96334594, 76251411}, - {103335261, 69241401}, - {103345839, 44231404}, - {102331115, 44216643}, - }, - { - }, - }, - { - { - {93849749, 109613798}, - {91771666, 111698636}, - {91772404, 174626800}, - {96782902, 179645338}, - {241790509, 179645349}, - {246800716, 174626800}, - {246802574, 111699755}, - {243934250, 109616385}, - {93849749, 109613798}, - }, - { - }, - }, - { - { - {15856630, 87966835}, - {8414359, 91273170}, - {5891847, 99010553}, - {8403012, 104668172}, - {13739106, 107763252}, - {13739106, 116209175}, - {17959116, 116219127}, - {17959127, 107763252}, - {23952579, 103855773}, - {25806388, 96944174}, - {22553953, 90543787}, - {15856630, 87966835}, - }, - { - }, - }, - { - { - {503922805, 110421794}, - {491110107, 123244292}, - {479598157, 123244304}, - {479601067, 149264312}, - {494260327, 149265241}, - {502929782, 157948320}, - {506490250, 155806171}, - {502950518, 155094962}, - {507193172, 150852294}, - {504364680, 148023895}, - {535816833, 116571757}, - {538656617, 119411542}, - {542887886, 115157558}, - {543594970, 118693080}, - {545330008, 116966050}, - {540309189, 110425901}, - {503922805, 110421794}, - }, - { - }, - }, - { - { - {519310433, 62560296}, - {515749982, 64702434}, - {519289696, 65413661}, - {515047062, 69656303}, - {517875553, 72484703}, - {486423431, 103936848}, - {483595031, 101108448}, - {479352325, 105351055}, - {478645233, 101815525}, - {476917724, 103520870}, - {481923478, 110077233}, - {518337308, 110084297}, - {531130127, 97264312}, - {542630127, 97281049}, - {542639167, 71244292}, - {527979906, 71243363}, - {519310433, 62560296}, - }, - { - }, - }, - { - { - {528658425, 14775300}, - {525975568, 24475413}, - {522556814, 29181341}, - {517517474, 32090757}, - {511736147, 32698600}, - {506200465, 30901018}, - {501879743, 27011092}, - {497782491, 14775300}, - {492372374, 15588397}, - {489384268, 20795320}, - {491253082, 28537271}, - {495185363, 34469052}, - {495178475, 43927542}, - {502032399, 55796416}, - {524402581, 55807400}, - {531706434, 44295318}, - {531205383, 34469052}, - {536679415, 23789946}, - {535868173, 17264403}, - {532873348, 15073849}, - {528658425, 14775300}, - }, - { - }, - }, - { - { - {481122222, 166062916}, - {478115710, 166824472}, - {477103577, 169063247}, - {477106058, 192070670}, - {478623652, 194687013}, - {525109130, 195083267}, - {525117792, 198086965}, - {535129140, 198091624}, - {535129150, 195083267}, - {539038502, 194940807}, - {540865280, 193308821}, - {541132038, 169100183}, - {539614599, 166459484}, - {481122222, 166062916}, - }, - { - }, - }, - { - { - {23771404, 13005453}, - {24774973, 19182457}, - {31971050, 18727127}, - {32556286, 58337520}, - {25390683, 58337566}, - {25063762, 54707065}, - {20168811, 54707252}, - {20171550, 62917175}, - {70810377, 202895528}, - {74314421, 205588631}, - {88674817, 205515176}, - {91837376, 203083756}, - {92280287, 199307207}, - {40674807, 15904975}, - {36849630, 13006690}, - {23771404, 13005453}, - }, - { - }, - }, - { - { - {336421201, 2986256}, - {331176570, 6498191}, - {327552287, 5825511}, - {324913825, 2988891}, - {316226154, 2989990}, - {313040282, 6275291}, - {313040282, 23489990}, - {307126391, 23490002}, - {307140289, 25510010}, - {313040282, 25510010}, - {313040282, 28989990}, - {307126391, 28990002}, - {307140289, 31015515}, - {313040282, 31010010}, - {313040282, 35989990}, - {304534809, 37529785}, - {304524991, 73488855}, - {308554680, 77518546}, - {324040282, 77510010}, - {324040295, 93025333}, - {334574441, 93010010}, - {334574441, 90989990}, - {332560302, 90989990}, - {332560302, 85010010}, - {334560302, 85010010}, - {334561237, 82010010}, - {338540282, 82010010}, - {339540282, 83760010}, - {338540293, 93020012}, - {348060655, 93014679}, - {356564448, 84500000}, - {356560555, 28989990}, - {347334198, 29039989}, - {347334198, 25510010}, - {356510304, 25521084}, - {356510315, 23478922}, - {347560302, 23489990}, - {347560302, 5775291}, - {344874443, 2989990}, - {336421201, 2986256}, - }, - { - }, - }, - { - { - {465152221, 31684687}, - {457606880, 31688302}, - {452659362, 35508617}, - {449044605, 34734089}, - {446478972, 31692751}, - {437784814, 31692957}, - {435521210, 33956565}, - {435532195, 65697616}, - {426028494, 65691361}, - {426025938, 85049712}, - {435532195, 95717636}, - {435524445, 103754026}, - {436995898, 105225463}, - {447552204, 105226323}, - {447552215, 103197497}, - {444552215, 103197616}, - {444552215, 99217636}, - {452032195, 99217636}, - {452032195, 105221758}, - {465588513, 105225463}, - {467059965, 103754026}, - {467052215, 95717636}, - {478053039, 84511285}, - {478056214, 65697616}, - {468552215, 65697616}, - {468563959, 33957323}, - {465152221, 31684687}, - }, - { - }, - }, - { - { - {764927063, 92658416}, - {762115426, 94171595}, - {762122741, 131696443}, - {786415417, 132779578}, - {793690904, 129904572}, - {797383202, 124822853}, - {798269157, 120142660}, - {796710161, 114090278}, - {793387498, 110215980}, - {796094093, 103892242}, - {794107594, 96994001}, - {787445494, 92840355}, - {764927063, 92658416}, - }, - { - }, - }, - { - { - {27496331, 123147467}, - {3202195, 124246400}, - {3203433, 205768600}, - {20223453, 205775606}, - {20223644, 163243606}, - {31297341, 162189074}, - {36789517, 155659691}, - {36967183, 150566416}, - {34468182, 145711036}, - {38465496, 140400171}, - {38952460, 132613091}, - {34771593, 126022444}, - {27496331, 123147467}, - }, - { - }, - }, - { - { - {797556553, 39197820}, - {791313598, 39199767}, - {789506233, 39864015}, - {789522521, 48199767}, - {775974570, 48195721}, - {774022521, 50129235}, - {774008720, 76258022}, - {775974570, 78223833}, - {789522521, 78219787}, - {789522521, 86576919}, - {797556547, 87221747}, - {797556553, 39197820}, - }, - { - }, - }, - { - { - {676593113, 129820144}, - {676565322, 164844636}, - {701599609, 164858650}, - {701599609, 129823260}, - {676593113, 129820144}, - }, - { - }, - }, - { - { - {727646871, 93121321}, - {709122741, 93122138}, - {709122741, 125656310}, - {718769809, 135145243}, - {721622937, 135156111}, - {724152429, 132626619}, - {723734126, 112688301}, - {725837154, 107378546}, - {728976138, 104430846}, - {735847924, 102664848}, - {741289364, 104430846}, - {745202882, 108599767}, - {746590596, 114642158}, - {751137173, 114644887}, - {756151199, 109641674}, - {756149037, 94634278}, - {754642761, 93122138}, - {727646871, 93121321}, - }, - { - }, - }, - { - { - {135915724, 185598906}, - {131396265, 193419009}, - {131399444, 197643260}, - {140399444, 197636810}, - {140399444, 199138818}, - {157419464, 197643916}, - {157422805, 193210743}, - {153046747, 185604789}, - {149044579, 185614655}, - {147324399, 189850396}, - {144168954, 191108901}, - {141187892, 189479768}, - {139917659, 185615382}, - {135915724, 185598906}, - }, - { - }, - }, - { - { - {312619110, 154485844}, - {309601817, 157488332}, - {309599764, 203494810}, - {313109244, 207010010}, - {352900849, 207019221}, - {359629120, 200302405}, - {359638705, 159501827}, - {354621096, 154487830}, - {312619110, 154485844}, - }, - { - }, - }, - { - { - {313120315, 98984639}, - {309609100, 102486971}, - {309596977, 148492024}, - {312591195, 151510010}, - {354608772, 151524494}, - {359629120, 146515788}, - {359638123, 105715491}, - {352907860, 98987790}, - {313120315, 98984639}, - }, - { - }, - }, - { - { - {657746643, 86246732}, - {651722477, 92270881}, - {651720052, 131280884}, - {653947196, 131280884}, - {659746643, 125487816}, - {659746643, 119273826}, - {663742413, 112352691}, - {671726623, 112352691}, - {675733721, 119283349}, - {684745297, 119298573}, - {689758503, 114263168}, - {689752066, 91272158}, - {684746643, 86260871}, - {657746643, 86246732}, - }, - { - }, - }, - { - { - {653940791, 39260871}, - {651720052, 39260871}, - {651726623, 78280611}, - {657746631, 84295035}, - {684746643, 84280891}, - {689752066, 79269604}, - {689746643, 56247942}, - {684745283, 51243184}, - {675733721, 51258413}, - {671726623, 58189071}, - {663742413, 58189071}, - {659746643, 51267936}, - {659746643, 45053950}, - {653940791, 39260871}, - }, - { - }, - }, - { - { - {442365208, 3053303}, - {436408500, 5694021}, - {434342552, 11072741}, - {436986326, 17009033}, - {442365367, 19073360}, - {448299202, 16431441}, - {450365150, 11052721}, - {448299202, 5694021}, - {442365208, 3053303}, - }, - { - }, - }, -}; diff --git a/tests/libnest2d/printer_parts.hpp b/tests/libnest2d/printer_parts.hpp deleted file mode 100644 index 075e32a837..0000000000 --- a/tests/libnest2d/printer_parts.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef PRINTER_PARTS_H -#define PRINTER_PARTS_H - -#include -#include - -using TestData = std::vector; -using TestDataEx = std::vector; - -extern const TestData PRINTER_PART_POLYGONS; -extern const TestData STEGOSAUR_POLYGONS; -extern const TestDataEx PRINTER_PART_POLYGONS_EX; - -#endif // PRINTER_PARTS_H diff --git a/tests/libslic3r/CMakeLists.txt b/tests/libslic3r/CMakeLists.txt index 971db528cd..69ae90178a 100644 --- a/tests/libslic3r/CMakeLists.txt +++ b/tests/libslic3r/CMakeLists.txt @@ -31,13 +31,15 @@ add_executable(${_TEST_NAME}_tests test_png_io.cpp test_surface_mesh.cpp test_timeutils.cpp - test_quadric_edge_collapse.cpp + test_quadric_edge_collapse.cpp test_triangulation.cpp test_emboss.cpp test_indexed_triangle_set.cpp test_astar.cpp - test_jump_point_search.cpp - ../libnest2d/printer_parts.cpp + test_anyptr.cpp + test_jump_point_search.cpp + ../data/prusaparts.cpp + ../data/prusaparts.hpp ) if (TARGET OpenVDB::openvdb) diff --git a/tests/libslic3r/test_anyptr.cpp b/tests/libslic3r/test_anyptr.cpp new file mode 100644 index 0000000000..d7b00a021c --- /dev/null +++ b/tests/libslic3r/test_anyptr.cpp @@ -0,0 +1,198 @@ +#include + +#include +#include +#include + +class Foo +{ +public: + virtual ~Foo() = default; + + virtual void set_foo(int) = 0; + virtual int get_foo() const = 0; +}; + +class Bar: public Foo +{ + int m_i = 0; + +public: + virtual void set_foo(int i) { m_i = i; } + virtual int get_foo() const { return m_i; }; +}; + +class BarPlus: public Foo { + int m_i = 0; + +public: + virtual void set_foo(int i) { m_i = i + 1; } + virtual int get_foo() const { return m_i; }; +}; + +TEST_CASE("Testing AnyPtr", "[anyptr]") { + using Slic3r::AnyPtr; + + SECTION("Construction with various valid arguments using operator=") + { + auto args = std::make_tuple(nullptr, + AnyPtr{nullptr}, + AnyPtr{static_cast(nullptr)}, + AnyPtr{static_cast(nullptr)}, + AnyPtr{static_cast(nullptr)}, + AnyPtr{}, + AnyPtr{}, + AnyPtr{}, + static_cast(nullptr), + static_cast(nullptr), + static_cast(nullptr)); + + auto check_ptr = [](auto &ptr) { + REQUIRE(!ptr); + REQUIRE(!ptr.is_owned()); + + auto shp = ptr.get_shared_cpy(); + REQUIRE(!shp); + }; + + SECTION("operator =") { + Slic3r::for_each_in_tuple([&check_ptr](auto &arg){ + AnyPtr ptr = std::move(arg); + + check_ptr(ptr); + }, args); + } + + SECTION("move construction") + { + Slic3r::for_each_in_tuple([&check_ptr](auto &arg){ + AnyPtr ptr{std::move(arg)}; + + check_ptr(ptr); + }, args); + } + } + + GIVEN("A polymorphic base class type Foo") { + WHEN("Creating a subclass on the stack") { + Bar bar; + auto val = random_value(-100, 100); + bar.set_foo(val); + + THEN("Storing a raw pointer in an AnyPtr should be valid " + "until the object is not destroyed") + { + AnyPtr ptr = &bar; + auto val2 = random_value(-100, 100); + ptr->set_foo(val2); + + REQUIRE(ptr->get_foo() == val2); + } + + THEN("Storing a raw pointer in an AnyPtr should be " + "valid until the object is not destroyed") + { + AnyPtr ptr{&bar}; + + REQUIRE(ptr->get_foo() == val); + } + } + } + + GIVEN("An empty AnyPtr of type Foo") + { + AnyPtr ptr; + + WHEN("Re-assigning a new unique_ptr of object of type Bar to ptr") + { + auto bar = std::make_unique(); + auto val = random_value(-100, 100); + + bar->set_foo(val); + + ptr = std::move(bar); + + THEN("the ptr should contain the new object and should own it") + { + REQUIRE(ptr->get_foo() == val); + REQUIRE(ptr.is_owned()); + } + } + + WHEN("Re-assigning a new unique_ptr of object of type BarPlus to ptr") + { + auto barplus = std::make_unique(); + auto val = random_value(-100, 100); + + barplus->set_foo(val); + + ptr = std::move(barplus); + + THEN("the ptr should contain the new object and should own it") + { + REQUIRE(ptr->get_foo() == val + 1); + REQUIRE(ptr.is_owned()); + } + + THEN("copying the stored object into a shared_ptr should be invalid") + { + std::shared_ptr shptr = ptr.get_shared_cpy(); + + REQUIRE(!shptr); + } + + THEN("copying the stored object into a shared_ptr after calling " + "convert_unique_to_shared should be valid") + { + ptr.convert_unique_to_shared(); + std::shared_ptr shptr = ptr.get_shared_cpy(); + + REQUIRE(shptr); + REQUIRE(shptr->get_foo() == val + 1); + } + } + } + + GIVEN("A vector of AnyPtr pointer to random Bar or BarPlus objects") + { + std::vector> ptrs; + + auto N = random_value(size_t(1), size_t(10)); + INFO("N = " << N); + + std::generate_n(std::back_inserter(ptrs), N, []{ + auto v = random_value(0, 1); + + std::unique_ptr ret; + + if (v) + ret = std::make_unique(); + else + ret = std::make_unique(); + + return ret; + }); + + WHEN("moving the whole array into a vector of AnyPtr") + { + THEN("the move should be valid") + { + std::vector> constptrs; + std::vector vals; + std::transform(ptrs.begin(), ptrs.end(), + std::back_inserter(vals), + [](auto &p) { return p->get_foo(); }); + + std::move(ptrs.begin(), ptrs.end(), std::back_inserter(constptrs)); + + REQUIRE(constptrs.size() == N); + REQUIRE(ptrs.size() == N); + REQUIRE(std::all_of(ptrs.begin(), ptrs.end(), [](auto &p) { return !p; })); + + for (size_t i = 0; i < N; ++i) { + REQUIRE(vals[i] == constptrs[i]->get_foo()); + } + } + } + } +} diff --git a/tests/libslic3r/test_geometry.cpp b/tests/libslic3r/test_geometry.cpp index 16a27665e8..d96a2b3e2a 100644 --- a/tests/libslic3r/test_geometry.cpp +++ b/tests/libslic3r/test_geometry.cpp @@ -15,7 +15,7 @@ //#include "libnest2d/tools/benchmark.h" #include "libslic3r/SVG.hpp" -#include "../libnest2d/printer_parts.hpp" +#include "../data/prusaparts.hpp" #include @@ -683,15 +683,15 @@ struct Pair template<> struct std::hash { size_t operator()(const Pair &c) const { - return c.first * PRINTER_PART_POLYGONS.size() + c.second; + return c.first * PRUSA_PART_POLYGONS.size() + c.second; } }; TEST_CASE("Convex polygon intersection test prusa polygons", "[Geometry][Rotcalip]") { // Overlap of the same polygon should always be an intersection - for (size_t i = 0; i < PRINTER_PART_POLYGONS.size(); ++i) { - Polygon P = PRINTER_PART_POLYGONS[i]; + for (size_t i = 0; i < PRUSA_PART_POLYGONS.size(); ++i) { + Polygon P = PRUSA_PART_POLYGONS[i]; P = Geometry::convex_hull(P.points); bool res = Geometry::convex_polygons_intersect(P, P); if (!res) { @@ -703,8 +703,8 @@ TEST_CASE("Convex polygon intersection test prusa polygons", "[Geometry][Rotcali } std::unordered_set combos; - for (size_t i = 0; i < PRINTER_PART_POLYGONS.size(); ++i) { - for (size_t j = 0; j < PRINTER_PART_POLYGONS.size(); ++j) { + for (size_t i = 0; i < PRUSA_PART_POLYGONS.size(); ++i) { + for (size_t j = 0; j < PRUSA_PART_POLYGONS.size(); ++j) { if (i != j) { size_t a = std::min(i, j), b = std::max(i, j); combos.insert(Pair{a, b}); @@ -714,7 +714,7 @@ TEST_CASE("Convex polygon intersection test prusa polygons", "[Geometry][Rotcali // All disjoint for (const auto &combo : combos) { - Polygon A = PRINTER_PART_POLYGONS[combo.first], B = PRINTER_PART_POLYGONS[combo.second]; + Polygon A = PRUSA_PART_POLYGONS[combo.first], B = PRUSA_PART_POLYGONS[combo.second]; A = Geometry::convex_hull(A.points); B = Geometry::convex_hull(B.points); @@ -741,7 +741,7 @@ TEST_CASE("Convex polygon intersection test prusa polygons", "[Geometry][Rotcali // All intersecting for (const auto &combo : combos) { - Polygon A = PRINTER_PART_POLYGONS[combo.first], B = PRINTER_PART_POLYGONS[combo.second]; + Polygon A = PRUSA_PART_POLYGONS[combo.first], B = PRUSA_PART_POLYGONS[combo.second]; A = Geometry::convex_hull(A.points); B = Geometry::convex_hull(B.points); diff --git a/tests/libslic3r/test_marchingsquares.cpp b/tests/libslic3r/test_marchingsquares.cpp index 0fbe6a5e34..89a86f1eeb 100644 --- a/tests/libslic3r/test_marchingsquares.cpp +++ b/tests/libslic3r/test_marchingsquares.cpp @@ -1,5 +1,3 @@ -#define NOMINMAX - #include #include diff --git a/tests/slic3rutils/CMakeLists.txt b/tests/slic3rutils/CMakeLists.txt index 7c83fb8d75..ac506e9756 100644 --- a/tests/slic3rutils/CMakeLists.txt +++ b/tests/slic3rutils/CMakeLists.txt @@ -3,6 +3,7 @@ add_executable(${_TEST_NAME}_tests ${_TEST_NAME}_tests_main.cpp slic3r_jobs_tests.cpp slic3r_version_tests.cpp + slic3r_arrangejob_tests.cpp ) # mold linker for successful linking needs also to link TBB library and link it before libslic3r. diff --git a/tests/slic3rutils/slic3r_arrangejob_tests.cpp b/tests/slic3rutils/slic3r_arrangejob_tests.cpp new file mode 100644 index 0000000000..b76861af16 --- /dev/null +++ b/tests/slic3rutils/slic3r_arrangejob_tests.cpp @@ -0,0 +1,351 @@ +#include "catch2/catch.hpp" +#include "test_utils.hpp" + +#include + +#include "slic3r/GUI/Jobs/UIThreadWorker.hpp" +#include "slic3r/GUI/Jobs/BoostThreadWorker.hpp" + +#include "slic3r/GUI/Jobs/ArrangeJob2.hpp" + +#include "libslic3r/Model.hpp" +#include "libslic3r/SLAPrint.hpp" + +#include "libslic3r/Format/3mf.hpp" + +class RandomArrangeSettings: public Slic3r::arr2::ArrangeSettingsView { + Slic3r::arr2::ArrangeSettingsDb::Values m_v; + + std::mt19937 m_rng; +public: + explicit RandomArrangeSettings(int seed) : m_rng(seed) + { + std::uniform_real_distribution fdist(0., 100.f); + std::uniform_int_distribution<> bdist(0, 1); + std::uniform_int_distribution<> dist; + m_v.d_obj = fdist(m_rng); + m_v.d_bed = fdist(m_rng); + m_v.rotations = bdist(m_rng); + m_v.geom_handling = static_cast(dist(m_rng) % ghCount); + m_v.arr_strategy = static_cast(dist(m_rng) % asCount); + m_v.xl_align = static_cast(dist(m_rng) % xlpCount); + } + explicit RandomArrangeSettings() : m_rng(std::random_device{} ()) {} + + float get_distance_from_objects() const override { return m_v.d_obj; } + float get_distance_from_bed() const override { return m_v.d_bed; } + bool is_rotation_enabled() const override { return m_v.rotations; } + XLPivots get_xl_alignment() const override { return m_v.xl_align; } + GeometryHandling get_geometry_handling() const override { return m_v.geom_handling; } + ArrangeStrategy get_arrange_strategy() const override { return m_v.arr_strategy; } +}; + +TEMPLATE_TEST_CASE("Arranging empty bed should do nothing", + "[arrangejob][fillbedjob]", + Slic3r::GUI::ArrangeJob2, + Slic3r::GUI::FillBedJob2) +{ + using namespace Slic3r; + using namespace Slic3r::GUI; + + using JobType = TestType; + + Model m; + + UIThreadWorker w; + RandomArrangeSettings settings; + + w.push(std::make_unique(arr2::Scene{ + arr2::SceneBuilder{}.set_model(m).set_arrange_settings(&settings)})); + + w.process_events(); + + REQUIRE(m.objects.empty()); +} + +static void center_first_instance(Slic3r::ModelObject *mo, + const Slic3r::BoundingBox &bedbb) +{ + using namespace Slic3r; + + Vec2d d = unscaled(bedbb).center() - + to_2d(mo->instance_bounding_box(0).center()); + auto tr = mo->instances.front()->get_transformation().get_matrix(); + tr.translate(to_3d(d, 0.)); + mo->instances.front()->set_transformation(Geometry::Transformation(tr)); +} + +TEST_CASE("Basic arrange with cube", "[arrangejob]") { + using namespace Slic3r; + using namespace Slic3r::GUI; + + std::string basepath = TEST_DATA_DIR PATH_SEPARATOR; + + DynamicPrintConfig cfg; + cfg.load_from_ini(basepath + "default_fff.ini", + ForwardCompatibilitySubstitutionRule::Enable); + Model m = Model::read_from_file(basepath + "20mm_cube.obj", &cfg); + + UIThreadWorker w; + arr2::ArrangeSettings settings; + + Points bedpts = get_bed_shape(cfg); + arr2::ArrangeBed bed = arr2::to_arrange_bed(bedpts); + + SECTION("Single cube needs to be centered") { + w.push(std::make_unique(arr2::Scene{ + arr2::SceneBuilder{} + .set_model(m) + .set_arrange_settings(&settings) + .set_bed(cfg)})); + + w.process_events(); + + REQUIRE(m.objects.size() == 1); + REQUIRE(m.objects.front()->instances.size() == 1); + + Vec3d c3 = m.objects.front()->bounding_box_exact().center(); + Point c{scaled(c3.x()), scaled(c3.y())}; + + REQUIRE(c == bounding_box(bed).center()); + } + + SECTION("Selected cube needs to go beside existing") { + REQUIRE(m.objects.size() == 1); + + ModelObject *mo = m.objects.front(); + + // Center the first instance within the bed + center_first_instance(mo, bounding_box(bed)); + + m.objects.front()->add_instance(); + + REQUIRE(m.objects.front()->instances.size() == 2); + + arr2::FixedSelection sel({ {false, true} }); + arr2::Scene scene{arr2::SceneBuilder{} + .set_model(m) + .set_arrange_settings(&settings) + .set_bed(cfg) + .set_selection(&sel)}; + + w.push(std::make_unique(std::move(scene))); + w.process_events(); + + auto bb0 = m.objects.front()->instance_bounding_box(0); + auto bb1 = m.objects.front()->instance_bounding_box(1); + + REQUIRE(!bb0.contains(bb1)); + + bb0.merge(bb1); + Vec2d sz = to_2d(bb0.size()); + if (sz.x() > sz.y()) + std::swap(sz.x(), sz.y()); + + double d_obj = settings.get_distance_from_objects(); + REQUIRE(sz.y() == Approx(2. * bb1.size().y() + d_obj)); + } + + SECTION("Selected cube (different object), needs to go beside existing") { + REQUIRE(m.objects.size() == 1); + + ModelObject *mo = m.objects.front(); + + // Center the first instance within the bed + center_first_instance(mo, bounding_box(bed)); + + ModelObject *mosel = m.add_object(*m.objects.front()); + + arr2::FixedSelection sel({ {false}, {true} }); + arr2::Scene scene{arr2::SceneBuilder{} + .set_model(m) + .set_arrange_settings(&settings) + .set_bed(cfg) + .set_selection(&sel)}; + + w.push(std::make_unique(std::move(scene))); + w.process_events(); + + auto bb0 = mo->instance_bounding_box(0); + auto bb1 = mosel->instance_bounding_box(0); + + REQUIRE(!bb0.contains(bb1)); + + bb0.merge(bb1); + Vec2d sz = to_2d(bb0.size()); + if (sz.x() > sz.y()) + std::swap(sz.x(), sz.y()); + + double d_obj = settings.get_distance_from_objects(); + REQUIRE(sz.y() == Approx(2. * bb1.size().y() + d_obj)); + } + + SECTION("Four cubes needs to touch each other after arrange") { + ModelObject *mo = m.objects.front(); + mo->add_instance(); + mo->add_instance(); + mo->add_instance(); + + auto bedbb = unscaled(bounding_box(bed)); + ModelInstance *mi = mo->instances[0]; + + Vec2d d = bedbb.min - to_2d(mo->instance_bounding_box(0).center()); + auto tr = mi->get_transformation().get_matrix(); + tr.translate(to_3d(d, 0.)); + mi->set_transformation(Geometry::Transformation(tr)); + + mi = mo->instances[1]; + d = Vec2d(bedbb.min.x(), bedbb.max.y()) - + to_2d(mo->instance_bounding_box(1).center()); + tr = mi->get_transformation().get_matrix(); + tr.translate(to_3d(d, 0.)); + mi->set_transformation(Geometry::Transformation(tr)); + + mi = mo->instances[2]; + d = bedbb.max - to_2d(mo->instance_bounding_box(2).center()); + tr = mi->get_transformation().get_matrix(); + tr.translate(to_3d(d, 0.)); + mi->set_transformation(Geometry::Transformation(tr)); + + mi = mo->instances[3]; + d = Vec2d(bedbb.max.x(), bedbb.min.y()) - + to_2d(mo->instance_bounding_box(3).center()); + tr = mi->get_transformation().get_matrix(); + tr.translate(to_3d(d, 0.)); + mi->set_transformation(Geometry::Transformation(tr)); + + arr2::Scene scene{arr2::SceneBuilder{} + .set_model(m) + .set_arrange_settings(&settings) + .set_bed(cfg)}; + + w.push(std::make_unique(std::move(scene))); + w.process_events(); + + auto pilebb = m.objects.front()->bounding_box_exact(); + Vec3d c3 = pilebb.center(); + Point c{scaled(c3.x()), scaled(c3.y())}; + + REQUIRE(c == bounding_box(bed).center()); + + float d_obj = settings.get_distance_from_objects(); + REQUIRE(pilebb.size().x() == Approx(2. * 20. + d_obj)); + REQUIRE(pilebb.size().y() == Approx(2. * 20. + d_obj)); + } +} + +struct DummyProgress: Slic3r::ProgressIndicator { + int range = 100; + int pr = 0; + std::string statustxt; + void set_range(int r) override { range = r; } + void set_cancel_callback(CancelFn = CancelFn()) override {} + void set_progress(int p) override { pr = p; } + void set_status_text(const char *txt) override { statustxt = txt; } + int get_range() const override { return range; } +}; + +TEST_CASE("Test for modifying model during arrangement", "[arrangejob][fillbedjob]") +{ + using namespace Slic3r; + using namespace Slic3r::GUI; + + std::string basepath = TEST_DATA_DIR PATH_SEPARATOR; + + DynamicPrintConfig cfg; + cfg.load_from_ini(basepath + "default_fff.ini", + ForwardCompatibilitySubstitutionRule::Enable); + + Model m; + + ModelObject* new_object = m.add_object(); + new_object->name = "20mm_cyl"; + new_object->add_instance(); + TriangleMesh mesh = make_cylinder(10., 10.); + ModelVolume* new_volume = new_object->add_volume(mesh); + new_volume->name = new_object->name; + + Points bedpts = get_bed_shape(cfg); + arr2::ArrangeBed bed = arr2::to_arrange_bed(bedpts); + + BoostThreadWorker w(std::make_unique()); + RandomArrangeSettings settings; + + SECTION("Remove 10 cylinder instances during arrange") { + for (size_t i = 1; i < 10; ++i) + new_object->add_instance(); + + arr2::Scene scene{arr2::SceneBuilder{} + .set_model(m) + .set_arrange_settings(&settings) + .set_bed(cfg)}; + + ArrangeJob2::Callbacks cbs; + cbs.on_prepared = [&m] (auto &) { + m.clear_objects(); + }; + + w.push(std::make_unique(std::move(scene), cbs)); + w.wait_for_current_job(); + + REQUIRE(m.objects.empty()); + } +} + +//TEST_CASE("Logical bed needs to be used when physical bed is full", +// "[arrangejob][fillbedjob]") +//{ +// using namespace Slic3r; +// using namespace Slic3r::GUI; + +// std::string basepath = TEST_DATA_DIR PATH_SEPARATOR; + +// DynamicPrintConfig cfg; +// cfg.load_from_ini(basepath + "default_fff.ini", +// ForwardCompatibilitySubstitutionRule::Enable); + +// Model m; + +// ModelObject* new_object = m.add_object(); +// new_object->name = "bigbox"; +// new_object->add_instance(); +// TriangleMesh mesh = make_cube(200., 200., 10.); +// ModelVolume* new_volume = new_object->add_volume(mesh); +// new_volume->name = new_object->name; + +// Points bedpts = get_bed_shape(cfg); +// arr2::ArrangeBed bed = arr2::to_arrange_bed(bedpts); +// auto bedbb = bounding_box(bed); + +// center_first_instance(new_object, bedbb); + +// new_object = m.add_object(); +// new_object->name = "40x20mm_box"; +// new_object->add_instance(); +// mesh = make_cube(50., 50., 50.); +// new_volume = new_object->add_volume(mesh); +// new_volume->name = new_object->name; + +// UIThreadWorker w(std::make_unique()); +// arr2::ArrangeSettings settings; + +// SECTION("Single cube needs to be on first logical bed") { +// { +// arr2::Scene scene{&m, &settings, &cfg}; + +// w.push(std::make_unique(std::move(scene))); +// w.process_events(); +// } + +// store_3mf("logicalbed_10mm.3mf", &m, &cfg, false); + +// REQUIRE(m.objects.size() == 2); + +// Vec3d c3 = m.objects[1]->bounding_box_exact().center(); +// Point result_center{scaled(c3.x()), scaled(c3.y())}; + +// auto bedidx_ojb1 = scene.virtual_bed_handler().get_bed_index(m.objects[1]->instances[0]); +// REQUIRE(bedidx_ojb1 == 1); +// } +//} + diff --git a/tests/test_utils.hpp b/tests/test_utils.hpp index b129cc79f1..842576a2c0 100644 --- a/tests/test_utils.hpp +++ b/tests/test_utils.hpp @@ -3,6 +3,7 @@ #include #include +#include #if defined(WIN32) || defined(_WIN32) #define PATH_SEPARATOR R"(\)" @@ -18,4 +19,22 @@ inline Slic3r::TriangleMesh load_model(const std::string &obj_filename) return mesh; } +template +Slic3r::FloatingOnly random_value(T minv, T maxv) +{ + static std::mt19937 rng(std::random_device{}()); + std::uniform_real_distribution dist(minv, maxv); + + return dist(rng); +} + +template +Slic3r::IntegerOnly random_value(T minv, T maxv) +{ + static std::mt19937 rng(std::random_device{}()); + std::uniform_int_distribution dist(minv, maxv); + + return dist(rng); +} + #endif // SLIC3R_TEST_UTILS diff --git a/xs/xsp/Model.xsp b/xs/xsp/Model.xsp index 2194089a54..9763c55904 100644 --- a/xs/xsp/Model.xsp +++ b/xs/xsp/Model.xsp @@ -79,8 +79,8 @@ ModelObjectPtrs* objects() %code%{ RETVAL = &THIS->objects; %}; - bool arrange_objects(double dist) %code%{ ArrangeParams ap{scaled(dist)}; arrange_objects(*THIS, InfiniteBed{}, ap); %}; - void duplicate(unsigned int copies_num, double dist) %code%{ ArrangeParams ap{scaled(dist)}; duplicate(*THIS, copies_num, InfiniteBed{}, ap); %}; + bool arrange_objects(double dist) %code%{ arrange_objects(*THIS, arr2::InfiniteBed{}, arr2::ArrangeSettings{}.set_distance_from_objects(dist) ); %}; + void duplicate(unsigned int copies_num, double dist) %code%{ duplicate(*THIS, copies_num, arr2::InfiniteBed{}, arr2::ArrangeSettings{}.set_distance_from_objects(dist) ); %}; bool looks_like_multipart_object() const; void convert_multipart_object(unsigned int max_extruders);