From 9372f1c6ad15d6d4f938f167625ff9e8e5905cb1 Mon Sep 17 00:00:00 2001
From: tamasmeszaros <meszaros.q@gmail.com>
Date: Thu, 4 Jul 2019 13:58:18 +0200
Subject: [PATCH] Wip in Nester interface

---
 src/libnest2d/include/libnest2d/libnest2d.hpp | 216 +++++++++++-------
 src/libslic3r/Arrange.cpp                     |  49 ++--
 2 files changed, 141 insertions(+), 124 deletions(-)

diff --git a/src/libnest2d/include/libnest2d/libnest2d.hpp b/src/libnest2d/include/libnest2d/libnest2d.hpp
index f106ad3d8..99c8c90c1 100644
--- a/src/libnest2d/include/libnest2d/libnest2d.hpp
+++ b/src/libnest2d/include/libnest2d/libnest2d.hpp
@@ -36,20 +36,20 @@ class _Item {
     // Transformation data
     Vertex translation_;
     Radians rotation_;
-    Coord offset_distance_;
+    Coord inflation_;
 
     // Info about whether the transformations will have to take place
     // This is needed because if floating point is used, it is hard to say
     // that a zero angle is not a rotation because of testing for equality.
-    bool has_rotation_ = false, has_translation_ = false, has_offset_ = false;
+    bool has_rotation_ = false, has_translation_ = false, has_inflation_ = false;
 
     // For caching the calculations as they can get pretty expensive.
     mutable RawShape tr_cache_;
     mutable bool tr_cache_valid_ = false;
     mutable double area_cache_ = 0;
     mutable bool area_cache_valid_ = false;
-    mutable RawShape offset_cache_;
-    mutable bool offset_cache_valid_ = false;
+    mutable RawShape inflate_cache_;
+    mutable bool inflate_cache_valid_ = false;
 
     enum class Convexity: char {
         UNCHECKED,
@@ -66,7 +66,9 @@ class _Item {
         BBCache(): valid(false) {}
     } bb_cache_;
     
-    std::function<void(const _Item&, unsigned)> applyfn_;
+    static const size_t ID_UNSET = size_t(-1);
+    
+    size_t id_{ID_UNSET};
     bool fixed_{false};
 
 public:
@@ -126,12 +128,12 @@ public:
                  THolesContainer<RawShape>&& holes):
         sh_(sl::create<RawShape>(std::move(contour), std::move(holes))) {}
 
-    template<class... Args>
-    _Item(std::function<void(const _Item&, unsigned)> applyfn, Args &&... args):
-        _Item(std::forward<Args>(args)...)
-    {
-        applyfn_ = std::move(applyfn);
-    }
+//    template<class... Args>
+//    _Item(std::function<void(const _Item&, unsigned)> applyfn, Args &&... args):
+//        _Item(std::forward<Args>(args)...)
+//    {
+//        applyfn_ = std::move(applyfn);
+//    }
 
     // Call the apply callback set in constructor. Within the callback, the
     // original caller can apply the stored transformation to the original
@@ -140,13 +142,15 @@ public:
     // client uses a simplified or processed polygon for nesting)
     // This callback, if present, will be called for each item after the nesting
     // is finished.
-    inline void callApplyFunction(unsigned binidx) const
-    {
-        if (applyfn_) applyfn_(*this, binidx);
-    }
+//    inline void callApplyFunction(unsigned binidx) const
+//    {
+//        if (applyfn_) applyfn_(*this, binidx);
+//    }
     
     inline bool isFixed() const noexcept { return fixed_; }
     inline void markAsFixed(bool fixed = true) { fixed_ = fixed; }
+    inline void id(size_t idx) { id_ = idx; }
+    inline long id() const noexcept { return id_; }
 
     /**
      * @brief Convert the polygon to string representation. The format depends
@@ -224,7 +228,7 @@ public:
         double ret ;
         if(area_cache_valid_) ret = area_cache_;
         else {
-            ret = sl::area(offsettedShape());
+            ret = sl::area(infaltedShape());
             area_cache_ = ret;
             area_cache_valid_ = true;
         }
@@ -295,17 +299,21 @@ public:
     {
         rotation(rotation() + rads);
     }
-
-    inline void addOffset(Coord distance) BP2D_NOEXCEPT
+    
+    inline void inflation(Coord distance) BP2D_NOEXCEPT
     {
-        offset_distance_ = distance;
-        has_offset_ = true;
+        inflation_ = distance;
+        has_inflation_ = true;
         invalidateCache();
     }
-
-    inline void removeOffset() BP2D_NOEXCEPT {
-        has_offset_ = false;
-        invalidateCache();
+    
+    inline Coord inflation() const BP2D_NOEXCEPT {
+        return inflation_;
+    }
+    
+    inline void inflate(Coord distance) BP2D_NOEXCEPT
+    {
+        inflation(inflation() + distance);
     }
 
     inline Radians rotation() const BP2D_NOEXCEPT
@@ -339,7 +347,7 @@ public:
     {
         if(tr_cache_valid_) return tr_cache_;
 
-        RawShape cpy = offsettedShape();
+        RawShape cpy = infaltedShape();
         if(has_rotation_) sl::rotate(cpy, rotation_);
         if(has_translation_) sl::translate(cpy, translation_);
         tr_cache_ = cpy; tr_cache_valid_ = true;
@@ -360,17 +368,17 @@ public:
 
     inline void resetTransformation() BP2D_NOEXCEPT
     {
-        has_translation_ = false; has_rotation_ = false; has_offset_ = false;
+        has_translation_ = false; has_rotation_ = false; has_inflation_ = false;
         invalidateCache();
     }
 
     inline Box boundingBox() const {
         if(!bb_cache_.valid) {
             if(!has_rotation_)
-                bb_cache_.bb = sl::boundingBox(offsettedShape());
+                bb_cache_.bb = sl::boundingBox(infaltedShape());
             else {
                 // TODO make sure this works
-                auto rotsh = offsettedShape();
+                auto rotsh = infaltedShape();
                 sl::rotate(rotsh, rotation_);
                 bb_cache_.bb = sl::boundingBox(rotsh);
             }
@@ -419,14 +427,14 @@ public:
 
 private:
 
-    inline const RawShape& offsettedShape() const {
-        if(has_offset_ ) {
-            if(offset_cache_valid_) return offset_cache_;
+    inline const RawShape& infaltedShape() const {
+        if(has_inflation_ ) {
+            if(inflate_cache_valid_) return inflate_cache_;
 
-            offset_cache_ = sh_;
-            sl::offset(offset_cache_, offset_distance_);
-            offset_cache_valid_ = true;
-            return offset_cache_;
+            inflate_cache_ = sh_;
+            sl::offset(inflate_cache_, inflation_);
+            inflate_cache_valid_ = true;
+            return inflate_cache_;
         }
         return sh_;
     }
@@ -436,7 +444,7 @@ private:
         tr_cache_valid_ = false;
         lmb_valid_ = false; rmt_valid_ = false;
         area_cache_valid_ = false;
-        offset_cache_valid_ = false;
+        inflate_cache_valid_ = false;
         bb_cache_.valid = false;
         convexity_ = Convexity::UNCHECKED;
     }
@@ -758,6 +766,25 @@ public:
     void clear() { impl_.clear(); }
 };
 
+using BinIdx = unsigned;
+template<class S, class Key = size_t> using _NestResult =
+    std::vector<
+        std::tuple<Key,          // Identifier of the original shape
+                   TPoint<S>,    // Translation calculated by nesting
+                   Radians,      // Rotation calculated by nesting
+                   BinIdx>       // Logical bin index, first is zero
+        >;
+
+template<class T> struct Indexed {
+    using ShapeType = T;
+    static T& get(T& obj) { return obj; }
+};
+
+template<class K, class S> struct Indexed<std::pair<K, S>> {
+    using ShapeType = S;
+    static S& get(std::pair<K, S>& obj) { return obj.second; }
+};
+
 /**
  * The Arranger is the front-end class for the libnest2d library. It takes the
  * input items and outputs the items with the proper transformations to be
@@ -769,6 +796,7 @@ class Nester {
     TSel selector_;
 public:
     using Item = typename PlacementStrategy::Item;
+    using ShapeType = typename Item::ShapeType;
     using ItemRef = std::reference_wrapper<Item>;
     using TPlacer = PlacementStrategyLike<PlacementStrategy>;
     using BinType = typename TPlacer::BinType;
@@ -777,6 +805,7 @@ public:
     using Coord = TCoord<TPoint<typename Item::ShapeType>>;
     using PackGroup = _PackGroup<typename Item::ShapeType>;
     using ResultType = PackGroup;
+    template<class K> using NestResult = _NestResult<ShapeType, K>;
 
 private:
     BinType bin_;
@@ -835,10 +864,12 @@ public:
      * The number of groups in the pack group is the number of bins opened by
      * the selection algorithm.
      */
-    template<class TIterator>
-    inline PackGroup execute(TIterator from, TIterator to)
+    template<class It, class Key = size_t>
+    inline const NestResult<Key> execute(It from, It to,
+                                         std::function<Key(It)> keyfn = nullptr)
     {
-        return _execute(from, to);
+        if (!keyfn) keyfn = [to](It it) { return to - it; };
+        return _execute(from, to, keyfn);
     }
 
     /// Set a progress indicator function object for the selector.
@@ -858,65 +889,74 @@ public:
         return selector_.getResult();
     }
 
-    inline void preload(const PackGroup& pgrp)
-    {
-        selector_.preload(pgrp);
-    }
-
 private:
-
-    template<class TIterator,
-             class IT = remove_cvref_t<typename TIterator::value_type>,
-
-             // This function will be used only if the iterators are pointing to
-             // a type compatible with the libnets2d::_Item template.
-             // This way we can use references to input elements as they will
-             // have to exist for the lifetime of this call.
-             class T = enable_if_t< std::is_convertible<IT, TPItem>::value, IT>
-             >
-    inline const PackGroup& _execute(TIterator from, TIterator to, bool = false)
+    
+    template<class It> using TVal = remove_cvref_t<typename It::value_type>;
+    
+    template<class It, class Out>
+    using ConvertibleOnly =
+        enable_if_t< std::is_convertible<TVal<It>, TPItem>::value, void>;
+    
+    template<class It, class Out>
+    using NotConvertibleOnly =
+        enable_if_t< ! std::is_convertible<TVal<It>, TPItem>::value, void>;
+    
+    // This function will be used only if the iterators are pointing to
+    // a type compatible with the libnets2d::_Item template.
+    // This way we can use references to input elements as they will
+    // have to exist for the lifetime of this call.
+    template<class It, class Key>
+    inline ConvertibleOnly<It, const NestResult<Key>> _execute(
+        It from, It to, std::function<Key(It)> keyfn)
     {
-        __execute(from, to);
-        return lastResult();
-    }
-
-    template<class TIterator,
-             class IT = remove_cvref_t<typename TIterator::value_type>,
-             class T = enable_if_t<!std::is_convertible<IT, TPItem>::value, IT>
-             >
-    inline const PackGroup& _execute(TIterator from, TIterator to, int = false)
-    {
-        item_cache_ = {from, to};
-
-        __execute(item_cache_.begin(), item_cache_.end());
-        return lastResult();
-    }
-
-    template<class TIter> inline void __execute(TIter from, TIter to)
-    {
-        if(min_obj_distance_ > 0) std::for_each(from, to, [this](Item& item) {
-            auto offs = min_obj_distance_;
-            if (item.isFixed()) offs *= 0.99;
+        {
+            auto it = from; size_t id = 0;
+            while(it != to) 
+                if (it->id() == Item::ID_UNSET) (it++)->id(id++);
+                else { id = it->id() + 1; ++it; }
+        }
+        
+        NestResult<Key> result(to - from);
+        
+        __execute(from, to, keyfn);
+        
+        BinIdx binidx = 0;
+        for(auto &itmgrp : lastResult()) {
+            for(const Item& itm : itmgrp) 
+                result[itm.id()] =
+                    std::make_tuple(keyfn(from + itm.id()), itm.translation(),
+                                    itm.rotation(), binidx);
             
-            item.addOffset(static_cast<Coord>(std::ceil(offs/2.0)));
+            ++binidx;
+        }
+        
+        return result;
+    }
+    
+    template<class It, class Key = size_t>
+    inline NotConvertibleOnly<It, const NestResult<Key>> _execute(
+        It from, It to, std::function<Key(It)> keyfn)
+    {
+        item_cache_.reserve(to - from);
+        for(auto it = from; it != to; ++it)
+            item_cache_.emplace_back(Indexed<typename It::value_type>::get(*it));
+
+        return _execute(item_cache_.begin(), item_cache_.end(), keyfn);
+    }
+
+    template<class It> inline void __execute(It from, It to)
+    {
+        auto infl = static_cast<Coord>(std::ceil(min_obj_distance_/2.0));
+        if(infl > 0) std::for_each(from, to, [this](Item& item) {
+            item.inflate(infl);
         });
 
         selector_.template packItems<PlacementStrategy>(
                     from, to, bin_, pconfig_);
         
         if(min_obj_distance_ > 0) std::for_each(from, to, [](Item& item) {
-            item.removeOffset();
+            item.inflate(-infl);
         });
-        
-        if(!stopfn_ || (stopfn_ && !stopfn_())) {
-            // Ignore results if nesting was stopped.
-            const PackGroup& bins = lastResult();
-            unsigned binidx = 0;
-            for(auto& bin : bins) {
-                for(const Item& itm : bin) itm.callApplyFunction(binidx);
-                ++binidx;
-            }
-        }
     }
 };
 
diff --git a/src/libslic3r/Arrange.cpp b/src/libslic3r/Arrange.cpp
index f778a7f75..6bbcc9577 100644
--- a/src/libslic3r/Arrange.cpp
+++ b/src/libslic3r/Arrange.cpp
@@ -513,12 +513,13 @@ BedShapeHint bedShape(const Polyline &bed) {
 }
 
 template<class BinT> // Arrange for arbitrary bin type
-PackGroup _arrange(std::vector<Item> &           shapes,
-                   std::vector<Item> &           excludes,
-                   const BinT &                  bin,
-                   coord_t                       minobjd,
-                   std::function<void(unsigned)> prind,
-                   std::function<bool()>         stopfn)
+_NestResult<clppr::Polygon> _arrange(
+    std::vector<Item> &           shapes,
+    std::vector<Item> &           excludes,
+    const BinT &                  bin,
+    coord_t                       minobjd,
+    std::function<void(unsigned)> prind,
+    std::function<bool()>         stopfn)
 {   
     AutoArranger<BinT> arranger{bin, minobjd, prind, stopfn};
     
@@ -535,22 +536,13 @@ PackGroup _arrange(std::vector<Item> &           shapes,
 
             // Try to put the first item to the center, as the arranger
             // will not do this for us.
-            for (auto it = shapes.begin(); it != shapes.end(); ++it) {
-                Item &itm = *it;
+            for (Item &itm : shapes) {
                 auto  ibb = itm.boundingBox();
                 auto  d   = binbb.center() - ibb.center();
                 itm.translate(d);
 
                 if (!arranger.is_colliding(itm)) {
                     itm.markAsFixed();
-
-                    // Write the transformation data into the item. The
-                    // callback was set on the instantiation of Item and
-                    // calls the Arrangeable interface.
-                    it->callApplyFunction(0);
-
-                    // Remove this item, as it is arranged now
-                    it = shapes.erase(it);
                     break;
                 }
             }
@@ -586,9 +578,7 @@ bool arrange(ArrangeablePtrs &             arrangables,
     coord_t binwidth = 0;
 
     auto process_arrangeable =
-        [](const Arrangeable *                         arrangeable,
-           std::vector<Item> &                         outp,
-           std::function<void(const Item &, unsigned)> applyfn)
+        [](const Arrangeable *arrangeable, std::vector<Item> &outp)
     {
         assert(arrangeable);
 
@@ -605,29 +595,16 @@ bool arrange(ArrangeablePtrs &             arrangables,
         auto firstp = clpath.Contour.front();
         clpath.Contour.emplace_back(firstp);
 
-        outp.emplace_back(applyfn, std::move(clpath));
+        outp.emplace_back(std::move(clpath));
         outp.back().rotation(rotation);
         outp.back().translation({offs.x(), offs.y()});
     };
 
-    for (Arrangeable *arrangeable : arrangables) {
-        process_arrangeable(
-            arrangeable,
-            items,
-            // callback called by arrange to apply the result on the arrangeable
-            [arrangeable, &binwidth, &ret](const Item &itm, unsigned binidx) {
-                ret = !binidx;  // Return value false more bed is required
-                clppr::cInt stride = binidx * stride_padding(binwidth);
-
-                clppr::IntPoint offs = itm.translation();
-                arrangeable->apply_arrange_result({unscaled(offs.X + stride),
-                                                   unscaled(offs.Y)},
-                                                  itm.rotation(), binidx);
-            });
-    }
+    for (Arrangeable *arrangeable : arrangables)
+        process_arrangeable(arrangeable, items);
     
     for (const Arrangeable * fixed: excludes)
-        process_arrangeable(fixed, fixeditems, nullptr);
+        process_arrangeable(fixed, fixeditems);
     
     // Integer ceiling the min distance from the bed perimeters
     coord_t md = min_obj_dist - SCALED_EPSILON;