Browse Source

Merge branch 'master' into fs_dir_per_glyph_SPE-1597

# Conflicts:
#	src/libslic3r/Polygon.hpp
Filip Sykala - NTB T15p 1 year ago
parent
commit
0c9cb02cf1

+ 3 - 2
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)
+#add_subdirectory(wx_gl_test)
+add_subdirectory(print_arrange_polys)

+ 7 - 0
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()

+ 103 - 0
sandboxes/print_arrange_polys/main.cpp

@@ -0,0 +1,103 @@
+#include <iostream>
+#include <ostream>
+
+#include <libslic3r/TriangleMesh.hpp>
+
+#include <boost/filesystem.hpp>
+
+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;
+}

+ 6 - 6
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<ConfigOptionPoint>("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);
                 }

+ 4 - 5
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)
 
-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)

+ 12 - 0
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 {

+ 83 - 47
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 T>
 class AnyPtr {
-    enum { RawPtr, UPtr, ShPtr, WkPtr };
+    enum { RawPtr, UPtr, ShPtr };
 
-    boost::variant<T*, std::unique_ptr<T>, std::shared_ptr<T>, std::weak_ptr<T>> ptr;
+    boost::variant<T*, std::unique_ptr<T>, std::shared_ptr<T>> ptr;
 
     template<class Self> static T *get_ptr(Self &&s)
     {
@@ -28,91 +35,119 @@ class AnyPtr {
         case RawPtr: return boost::get<T *>(s.ptr);
         case UPtr: return boost::get<std::unique_ptr<T>>(s.ptr).get();
         case ShPtr: return boost::get<std::shared_ptr<T>>(s.ptr).get();
-        case WkPtr: {
-            auto shptr = boost::get<std::weak_ptr<T>>(s.ptr).lock();
-            return shptr.get();
-        }
         }
 
         return nullptr;
     }
 
+    template<class TT> friend class AnyPtr;
+
+    template<class TT>
+    using SimilarPtrOnly = std::enable_if_t<std::is_convertible_v<TT*, T*>>;
+
 public:
-    template<class TT = T, class = std::enable_if_t<std::is_convertible_v<TT*, T*>>>
-    AnyPtr(TT *p = nullptr) : ptr{p}
-    {}
-    template<class TT, class = std::enable_if_t<std::is_convertible_v<TT*, T*>>>
-    AnyPtr(std::unique_ptr<TT> p) : ptr{std::unique_ptr<T>(std::move(p))}
+
+    AnyPtr() noexcept = default;
+
+    AnyPtr(T *p) noexcept: ptr{p} {}
+
+    AnyPtr(std::nullptr_t) noexcept {};
+
+    template<class TT, class = SimilarPtrOnly<TT>>
+    AnyPtr(TT *p) noexcept : ptr{p}
     {}
-    template<class TT, class = std::enable_if_t<std::is_convertible_v<TT*, T*>>>
-    AnyPtr(std::shared_ptr<TT> p) : ptr{std::shared_ptr<T>(std::move(p))}
+    template<class TT = T, class = SimilarPtrOnly<TT>>
+    AnyPtr(std::unique_ptr<TT> p) noexcept : ptr{std::unique_ptr<T>(std::move(p))}
     {}
-    template<class TT, class = std::enable_if_t<std::is_convertible_v<TT*, T*>>>
-    AnyPtr(std::weak_ptr<TT> p) : ptr{std::weak_ptr<T>(std::move(p))}
+    template<class TT = T, class = SimilarPtrOnly<TT>>
+    AnyPtr(std::shared_ptr<TT> p) noexcept : ptr{std::shared_ptr<T>(std::move(p))}
     {}
 
-    ~AnyPtr() = default;
-
     AnyPtr(AnyPtr &&other) noexcept : ptr{std::move(other.ptr)} {}
+
+    template<class TT, class = SimilarPtrOnly<TT>>
+    AnyPtr(AnyPtr<TT> &&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<class TT, class = std::enable_if_t<std::is_convertible_v<TT*, T*>>>
-    AnyPtr &operator=(TT *p) { ptr = p; return *this; }
+    template<class TT, class = SimilarPtrOnly<TT>>
+    AnyPtr& operator=(AnyPtr<TT> &&other) noexcept
+    {
+        switch (other.ptr.which()) {
+        case RawPtr: *this = boost::get<TT *>(other.ptr); break;
+        case UPtr: *this = std::move(boost::get<std::unique_ptr<TT>>(other.ptr)); break;
+        case ShPtr: *this = std::move(boost::get<std::shared_ptr<TT>>(other.ptr)); break;
+        }
+
+        return *this;
+    }
 
-    template<class TT, class = std::enable_if_t<std::is_convertible_v<TT*, T*>>>
-    AnyPtr &operator=(std::unique_ptr<TT> p) { ptr = std::move(p); return *this; }
+    template<class TT, class = SimilarPtrOnly<TT>>
+    AnyPtr &operator=(TT *p) noexcept
+    {
+        ptr = static_cast<T *>(p);
+        return *this;
+    }
 
-    template<class TT, class = std::enable_if_t<std::is_convertible_v<TT*, T*>>>
-    AnyPtr &operator=(std::shared_ptr<TT> p) { ptr = p; return *this; }
+    template<class TT, class = SimilarPtrOnly<TT>>
+    AnyPtr &operator=(std::unique_ptr<TT> p) noexcept
+    {
+        ptr = std::unique_ptr<T>(std::move(p));
+        return *this;
+    }
 
-    template<class TT, class = std::enable_if_t<std::is_convertible_v<TT*, T*>>>
-    AnyPtr &operator=(std::weak_ptr<TT> p) { ptr = std::move(p); return *this; }
+    template<class TT, class = SimilarPtrOnly<TT>>
+    AnyPtr &operator=(std::shared_ptr<TT> p) noexcept
+    {
+        ptr = std::shared_ptr<T>(std::move(p));
+        return *this;
+    }
 
-    const T &operator*() const { return *get_ptr(*this); }
-    T &operator*() { return *get_ptr(*this); }
+    const T &operator*() const noexcept { return *get_ptr(*this); }
+    T &operator*() noexcept { return *get_ptr(*this); }
 
-    T *operator->() { return get_ptr(*this); }
-    const T *operator->() const { return get_ptr(*this); }
+    T *operator->() noexcept { return get_ptr(*this); }
+    const T *operator->() const noexcept { return get_ptr(*this); }
 
-    T *get() { return get_ptr(*this); }
-    const T *get() const { return get_ptr(*this); }
+    T *get() noexcept { return get_ptr(*this); }
+    const T *get() const noexcept { return get_ptr(*this); }
 
-    operator bool() const
+    operator bool() const noexcept
     {
         switch (ptr.which()) {
         case RawPtr: return bool(boost::get<T *>(ptr));
         case UPtr: return bool(boost::get<std::unique_ptr<T>>(ptr));
         case ShPtr: return bool(boost::get<std::shared_ptr<T>>(ptr));
-        case WkPtr: {
-            auto shptr = boost::get<std::weak_ptr<T>>(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<T> get_shared_cpy() const
+    std::shared_ptr<T> get_shared_cpy() const noexcept
     {
         std::shared_ptr<T> ret;
 
-        switch (ptr.which()) {
-        case ShPtr: ret = boost::get<std::shared_ptr<T>>(ptr); break;
-        case WkPtr: ret = boost::get<std::weak_ptr<T>>(ptr).lock(); break;
-        default:
-            ;
-        }
+        if (ptr.which() == ShPtr)
+            ret = boost::get<std::shared_ptr<T>>(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<T>{std::move(boost::get<std::unique_ptr<T>>(ptr))};
@@ -125,6 +160,7 @@ public:
     }
 };
 
+
 } // namespace Slic3r
 
 #endif // ANYPTR_HPP

+ 2 - 1
src/libslic3r/Arrange.cpp

@@ -12,6 +12,7 @@
 #include <ClipperUtils.hpp>
 
 #include <boost/geometry/index/rtree.hpp>
+#include <boost/container/small_vector.hpp>
 
 #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<SpatElement> result;
+            boost::container::small_vector<SpatElement, 100> result;
             result.reserve(index.size());
 
             index.query(query, std::back_inserter(result));

+ 260 - 0
src/libslic3r/Arrange/Arrange.hpp

@@ -0,0 +1,260 @@
+#ifndef ARRANGE2_HPP
+#define ARRANGE2_HPP
+
+#include "Scene.hpp"
+#include "Items/MutableItemTraits.hpp"
+#include "Core/NFP/NFPArrangeItemTraits.hpp"
+
+#include "libslic3r/MinAreaBoundingBox.hpp"
+
+namespace Slic3r { namespace arr2 {
+
+template<class ArrItem> class Arranger
+{
+public:
+    class Ctl : public ArrangeTaskCtl {
+    public:
+        virtual void on_packed(ArrItem &item) {};
+    };
+
+    virtual ~Arranger() = default;
+
+    virtual void arrange(std::vector<ArrItem> &items,
+                         const std::vector<ArrItem> &fixed,
+                         const ExtendedBed &bed,
+                         Ctl &ctl) = 0;
+
+    void arrange(std::vector<ArrItem> &items,
+                 const std::vector<ArrItem> &fixed,
+                 const ExtendedBed &bed,
+                 ArrangeTaskCtl &ctl);
+
+    void arrange(std::vector<ArrItem> &items,
+                 const std::vector<ArrItem> &fixed,
+                 const ExtendedBed &bed,
+                 Ctl &&ctl)
+    {
+        arrange(items, fixed, bed, ctl);
+    }
+
+    void arrange(std::vector<ArrItem> &items,
+                 const std::vector<ArrItem> &fixed,
+                 const ExtendedBed &bed,
+                 ArrangeTaskCtl &&ctl)
+    {
+        arrange(items, fixed, bed, ctl);
+    }
+
+    static std::unique_ptr<Arranger> create(const ArrangeSettingsView &settings);
+};
+
+template<class ArrItem> using ArrangerCtl = typename Arranger<ArrItem>::Ctl;
+
+template<class ArrItem>
+class DefaultArrangerCtl : public Arranger<ArrItem>::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<class ArrItem>
+void Arranger<ArrItem>::arrange(std::vector<ArrItem> &items,
+             const std::vector<ArrItem> &fixed,
+             const ExtendedBed &bed,
+             ArrangeTaskCtl &ctl)
+{
+    arrange(items, fixed, bed, DefaultArrangerCtl<ArrItem>{ctl});
+}
+
+template<class ArrItem> class ArrangeableToItemConverter
+{
+public:
+    virtual ~ArrangeableToItemConverter() = default;
+
+    virtual ArrItem convert(const Arrangeable &arrbl, coord_t offs = 0) const = 0;
+
+    // Returns the extent of simplification that the converter utilizes when
+    // creating arrange items. Zero shall mean no simplification at all.
+    virtual coord_t simplification_tolerance() const { return 0; }
+
+    static std::unique_ptr<ArrangeableToItemConverter> create(
+        ArrangeSettingsView::GeometryHandling geometry_handling,
+        coord_t                               safety_d);
+
+    static std::unique_ptr<ArrangeableToItemConverter> create(
+        const Scene &sc)
+    {
+        return create(sc.settings().get_geometry_handling(),
+                      scaled(sc.settings().get_distance_from_objects()));
+    }
+};
+
+template<class DStore, class = WritableDataStoreOnly<DStore>>
+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 ArrItem>
+class BasicItemConverter : public ArrangeableToItemConverter<ArrItem>
+{
+    coord_t m_safety_d;
+    coord_t m_simplify_tol;
+
+public:
+    BasicItemConverter(coord_t safety_d = 0, coord_t simpl_tol = 0)
+        : m_safety_d{safety_d}, m_simplify_tol{simpl_tol}
+    {}
+
+    coord_t safety_dist() const noexcept { return m_safety_d; }
+
+    coord_t simplification_tolerance() const override
+    {
+        return m_simplify_tol;
+    }
+};
+
+template<class ArrItem>
+class ConvexItemConverter : public BasicItemConverter<ArrItem>
+{
+public:
+    using BasicItemConverter<ArrItem>::BasicItemConverter;
+
+    ArrItem convert(const Arrangeable &arrbl, coord_t offs) const override;
+};
+
+template<class ArrItem>
+class AdvancedItemConverter : public BasicItemConverter<ArrItem>
+{
+protected:
+    virtual ArrItem get_arritem(const Arrangeable &arrbl, coord_t eps) const;
+
+public:
+    using BasicItemConverter<ArrItem>::BasicItemConverter;
+
+    ArrItem convert(const Arrangeable &arrbl, coord_t offs) const override;
+};
+
+template<class ArrItem>
+class BalancedItemConverter : public AdvancedItemConverter<ArrItem>
+{
+protected:
+    ArrItem get_arritem(const Arrangeable &arrbl, coord_t offs) const override;
+
+public:
+    using AdvancedItemConverter<ArrItem>::AdvancedItemConverter;
+};
+
+template<class ArrItem, class En = void> 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<ObjectID> retrieve_id(const ArrItem &itm)
+    {
+        std::optional<ObjectID> ret;
+        auto                    idptr = get_data<const ObjectID>(itm, Key);
+        if (idptr)
+            ret = *idptr;
+
+        return ret;
+    }
+};
+
+template<class ArrItem>
+using ImbueableItemTraits = ImbueableItemTraits_<StripCVRef<ArrItem>>;
+
+template<class ArrItem>
+void imbue_id(ArrItem &itm, const ObjectID &id)
+{
+    ImbueableItemTraits<ArrItem>::imbue_id(itm, id);
+}
+
+template<class ArrItem>
+std::optional<ObjectID> retrieve_id(const ArrItem &itm)
+{
+    return ImbueableItemTraits<ArrItem>::retrieve_id(itm);
+}
+
+template<class ArrItem>
+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<class ArrItem>
+double get_min_area_bounding_box_rotation(const ArrItem &itm)
+{
+    return MinAreaBoundigBox{envelope_convex_hull(itm),
+                             MinAreaBoundigBox::pcConvex}
+        .angle_to_X();
+}
+
+template<class ArrItem>
+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;
+}
+
+template<class ArrItem>
+auto get_corrected_bed(const ExtendedBed &bed,
+                       const ArrangeableToItemConverter<ArrItem> &converter)
+{
+    auto bedcpy = bed;
+    visit_bed([tol = -converter.simplification_tolerance()](auto &rawbed) {
+        rawbed = offset(rawbed, tol);
+    }, bedcpy);
+
+    return bedcpy;
+}
+
+}} // namespace Slic3r::arr2
+
+#endif // ARRANGE2_HPP

+ 485 - 0
src/libslic3r/Arrange/ArrangeImpl.hpp

@@ -0,0 +1,485 @@
+#ifndef ARRANGEIMPL_HPP
+#define ARRANGEIMPL_HPP
+
+#include <random>
+
+#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/MutableItemTraits.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<class It,
+         class ConstIt,
+         class SelectionStrategy,
+         class PackStrategy, class...SBedArgs>
+void arrange(SelectionStrategy &&selstrategy,
+             PackStrategy &&packingstrategy,
+             const Range<It> &items,
+             const Range<ConstIt> &fixed,
+             const SegmentedRectangleBed<SBedArgs...> &bed)
+{
+    // Dispatch:
+    arrange(std::forward<SelectionStrategy>(selstrategy),
+            std::forward<PackStrategy>(packingstrategy), items, fixed,
+            RectangleBed{bed.bb}, SelStrategyTag<SelectionStrategy>{});
+
+    size_t beds = get_bed_count(crange(items));
+    size_t fixed_beds = std::max(beds, get_bed_count(fixed));
+    std::vector<bool> fixed_is_empty(fixed_beds, true);
+
+    std::vector<BoundingBox> 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 (get_bed_index(itm) == static_cast<int>(bedidx) && !is_wipe_tower(itm))
+                translate(itm, d);
+    }
+}
+
+
+using VariantKernel =
+    boost::variant<TMArrangeKernel, GravityKernel>;
+
+template<> struct KernelTraits_<VariantKernel> {
+    template<class ArrItem>
+    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<class ArrItem, class Bed, class Ctx, class RemIt>
+    static bool on_start_packing(VariantKernel &kernel,
+                                 ArrItem &itm,
+                                 const Bed &bed,
+                                 const Ctx &packing_context,
+                                 const Range<RemIt> &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<class ArrItem>
+    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<class ArrItem>
+struct firstfit::ItemArrangedVisitor<ArrItem, DataStoreOnly<ArrItem>> {
+    template<class Bed, class PIt, class RIt>
+    static void on_arranged(ArrItem &itm,
+                            const Bed &bed,
+                            const Range<PIt> &packed,
+                            const Range<RIt> &remaining)
+    {
+        using OnArrangeCb = std::function<void(StripCVRef<ArrItem> &)>;
+
+        auto cb = get_data<OnArrangeCb>(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<std::mt19937::result_type>
+            dist(0, arr2::ArrangeSettingsView::xlpRandom - 1);
+        xlpivot = static_cast<ArrangeSettingsView::XLPivots>(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;
+}
+
+template<class It, class Bed>
+void fill_rotations(const Range<It>           &items,
+                    const Bed                 &bed,
+                    const ArrangeSettingsView &settings)
+{
+    if (!settings.is_rotation_enabled())
+        return;
+
+    for (auto &itm : items) {
+        if (is_wipe_tower(itm)) // Rotating the wipe tower is currently problematic
+            continue;
+
+        // Use the minimum bounding box rotation as a starting point.
+        auto minbbr = get_min_area_bounding_box_rotation(itm);
+        std::vector<double> rotations =
+            {minbbr,
+             minbbr + PI / 4., minbbr + PI / 2.,
+             minbbr + PI,      minbbr + 3 * PI / 4.};
+
+        // Add the original rotation of the item if minbbr
+        // is not already the original rotation (zero)
+        if (std::abs(minbbr) > 0.)
+            rotations.emplace_back(0.);
+
+        // Also try to find the rotation that fits the item
+        // into a rectangular bed, given that it cannot fit,
+        // and there exists a rotation which can fit.
+        if constexpr (std::is_convertible_v<Bed, RectangleBed>) {
+            double fitbrot = get_fit_into_bed_rotation(itm, bed);
+            if (std::abs(fitbrot) > 0.)
+                rotations.emplace_back(fitbrot);
+        }
+
+        set_allowed_rotations(itm, rotations);
+    }
+}
+
+// An arranger put together to fulfill all the requirements of PrusaSlicer based
+// on the supplied ArrangeSettings
+template<class ArrItem>
+class DefaultArranger: public Arranger<ArrItem> {
+    ArrangeSettings m_settings;
+
+    static constexpr auto Accuracy = 1.;
+
+    template<class It, class FixIt, class Bed>
+    void arrange_(
+        const Range<It>     &items,
+        const Range<FixIt>  &fixed,
+        const Bed &bed,
+        ArrangerCtl<ArrItem> &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<VariantKernel> kernel{bounding_box(bed), basekernel};
+#else
+        auto & kernel = basekernel;
+#endif
+
+        fill_rotations(items, bed, m_settings);
+
+        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<Bed, RectangleBed>) {
+            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<ArrItem> &items,
+        const std::vector<ArrItem> &fixed,
+        const ExtendedBed &bed,
+        ArrangerCtl<ArrItem> &ctl) override
+    {
+        visit_bed([this, &items, &fixed, &ctl](auto rawbed) {
+
+            if constexpr (IsSegmentedBed<decltype(rawbed)>)
+                rawbed.pivot = xlpivots_to_rect_pivots(
+                    m_settings.get_xl_alignment());
+
+            arrange_(range(items), crange(fixed), rawbed, ctl);
+        }, bed);
+    }
+};
+
+template<class ArrItem>
+std::unique_ptr<Arranger<ArrItem>> Arranger<ArrItem>::create(
+    const ArrangeSettingsView &settings)
+{
+    // Currently all that is needed is handled by DefaultArranger
+    return std::make_unique<DefaultArranger<ArrItem>>(settings);
+}
+
+template<class ArrItem>
+ArrItem ConvexItemConverter<ArrItem>::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());
+    if constexpr (IsWritableDataStore<ArrItem>)
+        arrbl.imbue_data(AnyWritableDataStore{ret});
+
+    return ret;
+}
+
+template<class ArrItem>
+ArrItem AdvancedItemConverter<ArrItem>::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());
+    if constexpr (IsWritableDataStore<ArrItem>)
+        arrbl.imbue_data(AnyWritableDataStore{ret});
+
+    return ret;
+}
+
+template<class ArrItem>
+ArrItem AdvancedItemConverter<ArrItem>::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(envelope, infl);
+    }
+
+    auto simpl_tol = static_cast<double>(this->simplification_tolerance());
+
+    if (simpl_tol > 0)
+    {
+        outline = expolygons_simplify(outline, simpl_tol);
+        if (!envelope.empty())
+            envelope = expolygons_simplify(envelope, simpl_tol);
+    }
+
+    ArrItem ret;
+    set_shape(ret, outline);
+    if (! envelope.empty())
+        set_envelope(ret, envelope);
+
+    return ret;
+}
+
+template<class ArrItem>
+ArrItem BalancedItemConverter<ArrItem>::get_arritem(const Arrangeable &arrbl,
+                                                    coord_t    offs) const
+{
+    ArrItem ret = AdvancedItemConverter<ArrItem>::get_arritem(arrbl, offs);
+    set_convex_envelope(ret, envelope_convex_hull(ret));
+
+    return ret;
+}
+
+template<class ArrItem>
+std::unique_ptr<ArrangeableToItemConverter<ArrItem>>
+ArrangeableToItemConverter<ArrItem>::create(
+    ArrangeSettingsView::GeometryHandling gh,
+    coord_t safety_d)
+{
+    std::unique_ptr<ArrangeableToItemConverter<ArrItem>> ret;
+
+    constexpr coord_t SimplifyTol = scaled(.2);
+
+    switch(gh) {
+    case arr2::ArrangeSettingsView::ghConvex:
+        ret = std::make_unique<ConvexItemConverter<ArrItem>>(safety_d);
+        break;
+    case arr2::ArrangeSettingsView::ghBalanced:
+        ret = std::make_unique<BalancedItemConverter<ArrItem>>(safety_d, SimplifyTol);
+        break;
+    case arr2::ArrangeSettingsView::ghAdvanced:
+        ret = std::make_unique<AdvancedItemConverter<ArrItem>>(safety_d, SimplifyTol);
+        break;
+    default:
+        ;
+    }
+
+    return ret;
+}
+
+}} // namespace Slic3r::arr2
+
+#endif // ARRANGEIMPL_HPP

Some files were not shown because too many files changed in this diff