diff --git a/src/libnest2d/include/libnest2d.h b/src/libnest2d/include/libnest2d.h index f1d2506f4..5f7a29dfb 100644 --- a/src/libnest2d/include/libnest2d.h +++ b/src/libnest2d/include/libnest2d.h @@ -59,20 +59,20 @@ extern template PackGroup Nester::execute( template::iterator> -PackGroup nest(Iterator from, Iterator to, +void nest(Iterator from, Iterator to, const typename Placer::BinType& bin, Coord dist = 0, const typename Placer::Config& pconf = {}, const typename Selector::Config& sconf = {}) { Nester nester(bin, dist, pconf, sconf); - return nester.execute(from, to); + nester.execute(from, to); } template::iterator> -PackGroup nest(Iterator from, Iterator to, +void nest(Iterator from, Iterator to, const typename Placer::BinType& bin, ProgressFunction prg, StopCondition scond = []() { return false; }, @@ -83,7 +83,7 @@ PackGroup nest(Iterator from, Iterator to, Nester nester(bin, dist, pconf, sconf); if(prg) nester.progressIndicator(prg); if(scond) nester.stopCondition(scond); - return nester.execute(from, to); + nester.execute(from, to); } #ifdef LIBNEST2D_STATIC @@ -91,14 +91,14 @@ PackGroup nest(Iterator from, Iterator to, extern template class Nester; extern template class Nester; -extern template PackGroup nest(std::vector::iterator from, +extern template void nest(std::vector::iterator from, std::vector::iterator to, const Box& bin, Coord dist = 0, const NfpPlacer::Config& pconf, const FirstFitSelection::Config& sconf); -extern template PackGroup nest(std::vector::iterator from, +extern template void nest(std::vector::iterator from, std::vector::iterator to, const Box& bin, ProgressFunction prg, @@ -112,20 +112,19 @@ extern template PackGroup nest(std::vector::iterator from, template> -PackGroup nest(Container&& cont, +void nest(Container&& cont, const typename Placer::BinType& bin, Coord dist = 0, const typename Placer::Config& pconf = {}, const typename Selector::Config& sconf = {}) { - return nest(cont.begin(), cont.end(), - bin, dist, pconf, sconf); + nest(cont.begin(), cont.end(), bin, dist, pconf, sconf); } template> -PackGroup nest(Container&& cont, +void nest(Container&& cont, const typename Placer::BinType& bin, ProgressFunction prg, StopCondition scond = []() { return false; }, @@ -133,8 +132,8 @@ PackGroup nest(Container&& cont, const typename Placer::Config& pconf = {}, const typename Selector::Config& sconf = {}) { - return nest(cont.begin(), cont.end(), - bin, prg, scond, dist, pconf, sconf); + nest(cont.begin(), cont.end(), bin, prg, scond, dist, + pconf, sconf); } } diff --git a/src/libnest2d/include/libnest2d/libnest2d.hpp b/src/libnest2d/include/libnest2d/libnest2d.hpp index 99c8c90c1..a83a16ecf 100644 --- a/src/libnest2d/include/libnest2d/libnest2d.hpp +++ b/src/libnest2d/include/libnest2d/libnest2d.hpp @@ -12,6 +12,8 @@ namespace libnest2d { +static const constexpr int BIN_ID_UNSET = -1; + /** * \brief An item to be placed on a bin. * @@ -34,9 +36,9 @@ class _Item { RawShape sh_; // Transformation data - Vertex translation_; - Radians rotation_; - Coord inflation_; + Vertex translation_{0, 0}; + Radians rotation_{0.0}; + Coord inflation_{0}; // Info about whether the transformations will have to take place // This is needed because if floating point is used, it is hard to say @@ -66,9 +68,7 @@ class _Item { BBCache(): valid(false) {} } bb_cache_; - static const size_t ID_UNSET = size_t(-1); - - size_t id_{ID_UNSET}; + int binid_{BIN_ID_UNSET}; bool fixed_{false}; public: @@ -149,8 +149,8 @@ public: 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_; } + inline void binId(int idx) { binid_ = idx; } + inline int binId() const noexcept { return binid_; } /** * @brief Convert the polygon to string representation. The format depends @@ -766,25 +766,6 @@ public: void clear() { impl_.clear(); } }; -using BinIdx = unsigned; -template using _NestResult = - std::vector< - std::tuple, // Translation calculated by nesting - Radians, // Rotation calculated by nesting - BinIdx> // Logical bin index, first is zero - >; - -template struct Indexed { - using ShapeType = T; - static T& get(T& obj) { return obj; } -}; - -template struct Indexed> { - using ShapeType = S; - static S& get(std::pair& 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 @@ -805,7 +786,6 @@ public: using Coord = TCoord>; using PackGroup = _PackGroup; using ResultType = PackGroup; - template using NestResult = _NestResult; private: BinType bin_; @@ -816,8 +796,13 @@ private: using TPItem = remove_cvref_t; using TSItem = remove_cvref_t; - std::vector item_cache_; StopCondition stopfn_; + + template using TVal = remove_cvref_t; + + template + using ConvertibleOnly = + enable_if_t< std::is_convertible, TPItem>::value, void>; public: @@ -864,12 +849,20 @@ public: * The number of groups in the pack group is the number of bins opened by * the selection algorithm. */ - template - inline const NestResult execute(It from, It to, - std::function keyfn = nullptr) + template + inline ConvertibleOnly execute(It from, It to) { - if (!keyfn) keyfn = [to](It it) { return to - it; }; - return _execute(from, to, keyfn); + auto infl = static_cast(std::ceil(min_obj_distance_/2.0)); + if(infl > 0) std::for_each(from, to, [this, infl](Item& item) { + item.inflate(infl); + }); + + selector_.template packItems( + from, to, bin_, pconfig_); + + if(min_obj_distance_ > 0) std::for_each(from, to, [infl](Item& item) { + item.inflate(-infl); + }); } /// Set a progress indicator function object for the selector. @@ -890,74 +883,32 @@ public: } private: - - template using TVal = remove_cvref_t; - - template - using ConvertibleOnly = - enable_if_t< std::is_convertible, TPItem>::value, void>; - - template - using NotConvertibleOnly = - enable_if_t< ! std::is_convertible, 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 - inline ConvertibleOnly> _execute( - It from, It to, std::function keyfn) - { - { - 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 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); - - ++binidx; - } - - return result; - } +// template +// inline ConvertibleOnly _execute(It from, It to) +// { +// __execute(from, to); +// } - template - inline NotConvertibleOnly> _execute( - It from, It to, std::function keyfn) - { - item_cache_.reserve(to - from); - for(auto it = from; it != to; ++it) - item_cache_.emplace_back(Indexed::get(*it)); +// template inline void _execute(It from, It to) +// { +// auto infl = static_cast(std::ceil(min_obj_distance_/2.0)); +// if(infl > 0) std::for_each(from, to, [this](Item& item) { +// item.inflate(infl); +// }); - return _execute(item_cache_.begin(), item_cache_.end(), keyfn); - } - - template inline void __execute(It from, It to) - { - auto infl = static_cast(std::ceil(min_obj_distance_/2.0)); - if(infl > 0) std::for_each(from, to, [this](Item& item) { - item.inflate(infl); - }); - - selector_.template packItems( - from, to, bin_, pconfig_); +// selector_.template packItems( +// from, to, bin_, pconfig_); - if(min_obj_distance_ > 0) std::for_each(from, to, [](Item& item) { - item.inflate(-infl); - }); - } +// if(min_obj_distance_ > 0) std::for_each(from, to, [](Item& item) { +// item.inflate(-infl); +// }); +// } }; } diff --git a/src/libnest2d/include/libnest2d/selections/djd_heuristic.hpp b/src/libnest2d/include/libnest2d/selections/djd_heuristic.hpp index 25007e580..f904210aa 100644 --- a/src/libnest2d/include/libnest2d/selections/djd_heuristic.hpp +++ b/src/libnest2d/include/libnest2d/selections/djd_heuristic.hpp @@ -711,7 +711,12 @@ public: addBin(); packjob(placers[idx], remaining, idx); idx++; } - + + int binid = 0; + for(auto &bin : packed_bins_) { + for(Item& itm : bin) itm.binId(binid); + binid++; + } } }; diff --git a/src/libnest2d/include/libnest2d/selections/firstfit.hpp b/src/libnest2d/include/libnest2d/selections/firstfit.hpp index 287204c08..6a3e2e61b 100644 --- a/src/libnest2d/include/libnest2d/selections/firstfit.hpp +++ b/src/libnest2d/include/libnest2d/selections/firstfit.hpp @@ -90,8 +90,10 @@ public: size_t j = 0; while(!was_packed && !cancelled()) { for(; j < placers.size() && !was_packed && !cancelled(); j++) { - if((was_packed = placers[j].pack(*it, rem(it, store_) ))) - makeProgress(placers[j], j); + if((was_packed = placers[j].pack(*it, rem(it, store_) ))) { + it->get().binId(int(j)); + makeProgress(placers[j], j); + } } if(!was_packed) { diff --git a/src/libnest2d/tests/test.cpp b/src/libnest2d/tests/test.cpp index e5bd87182..6891b16e1 100644 --- a/src/libnest2d/tests/test.cpp +++ b/src/libnest2d/tests/test.cpp @@ -372,27 +372,34 @@ TEST(GeometryAlgorithms, ArrangeRectanglesTight) Nester arrange(bin); - auto groups = arrange.execute(rects.begin(), rects.end()); - - ASSERT_EQ(groups.size(), 1u); - ASSERT_EQ(groups[0].size(), rects.size()); - + arrange.execute(rects.begin(), rects.end()); + + auto max_group = std::max_element(rects.begin(), rects.end(), + [](const Item &i1, const Item &i2) { + return i1.binId() < i2.binId(); + }); + + int groups = max_group == rects.end() ? 0 : max_group->binId() + 1; + + ASSERT_EQ(groups, 1u); + ASSERT_TRUE( + std::all_of(rects.begin(), rects.end(), [](const Rectangle &itm) { + return itm.binId() != BIN_ID_UNSET; + })); + // check for no intersections, no containment: - for(auto result : groups) { - bool valid = true; - for(Item& r1 : result) { - for(Item& r2 : result) { - if(&r1 != &r2 ) { - valid = !Item::intersects(r1, r2) || Item::touches(r1, r2); - ASSERT_TRUE(valid); - valid = (valid && !r1.isInside(r2) && !r2.isInside(r1)); - ASSERT_TRUE(valid); - } + bool valid = true; + for(Item& r1 : rects) { + for(Item& r2 : rects) { + if(&r1 != &r2 ) { + valid = !Item::intersects(r1, r2) || Item::touches(r1, r2); + ASSERT_TRUE(valid); + valid = (valid && !r1.isInside(r2) && !r2.isInside(r1)); + ASSERT_TRUE(valid); } } } - } TEST(GeometryAlgorithms, ArrangeRectanglesLoose) @@ -433,16 +440,25 @@ TEST(GeometryAlgorithms, ArrangeRectanglesLoose) Nester arrange(bin, min_obj_distance); - auto groups = arrange.execute(rects.begin(), rects.end()); + arrange.execute(rects.begin(), rects.end()); - ASSERT_EQ(groups.size(), 1u); - ASSERT_EQ(groups[0].size(), rects.size()); + auto max_group = std::max_element(rects.begin(), rects.end(), + [](const Item &i1, const Item &i2) { + return i1.binId() < i2.binId(); + }); + + size_t groups = max_group == rects.end() ? 0 : max_group->binId() + 1; + + ASSERT_EQ(groups, 1u); + ASSERT_TRUE( + std::all_of(rects.begin(), rects.end(), [](const Rectangle &itm) { + return itm.binId() != BIN_ID_UNSET; + })); // check for no intersections, no containment: - auto result = groups[0]; bool valid = true; - for(Item& r1 : result) { - for(Item& r2 : result) { + for(Item& r1 : rects) { + for(Item& r2 : rects) { if(&r1 != &r2 ) { valid = !Item::intersects(r1, r2); valid = (valid && !r1.isInside(r2) && !r2.isInside(r1)); @@ -554,27 +570,24 @@ TEST(GeometryAlgorithms, convexHull) { TEST(GeometryAlgorithms, NestTest) { std::vector input = prusaParts(); + + libnest2d::nest(input, Box(250000000, 210000000), [](unsigned cnt) { + std::cout << "parts left: " << cnt << std::endl; + }); + + auto max_binid_it = std::max_element(input.begin(), input.end(), + [](const Item &i1, const Item &i2) { + return i1.binId() < i2.binId(); + }); + + size_t bins = max_binid_it == input.end() ? 0 : max_binid_it->binId() + 1; - PackGroup result = libnest2d::nest(input, - Box(250000000, 210000000), - [](unsigned cnt) { - std::cout - << "parts left: " << cnt - << std::endl; - }); - - ASSERT_LE(result.size(), 2); - - size_t partsum = std::accumulate(result.begin(), - result.end(), - size_t(0), - [](size_t s, - const decltype( - result)::value_type &bin) { - return s += bin.size(); - }); - - ASSERT_EQ(input.size(), partsum); + ASSERT_EQ(bins, 2u); + + ASSERT_TRUE( + std::all_of(input.begin(), input.end(), [](const Item &itm) { + return itm.binId() != BIN_ID_UNSET; + })); } namespace { diff --git a/src/libslic3r/Arrange.cpp b/src/libslic3r/Arrange.cpp index 6bbcc9577..c49c9fb97 100644 --- a/src/libslic3r/Arrange.cpp +++ b/src/libslic3r/Arrange.cpp @@ -341,9 +341,9 @@ public: m_pck.configure(m_pconf); } - template inline PackGroup operator()(Args&&...args) { + template inline void operator()(Args&&...args) { m_rtree.clear(); - return m_pck.execute(std::forward(args)...); + m_pck.execute(std::forward(args)...); } inline void preload(std::vector& fixeditems) { @@ -513,7 +513,7 @@ BedShapeHint bedShape(const Polyline &bed) { } template // Arrange for arbitrary bin type -_NestResult _arrange( +void _arrange( std::vector & shapes, std::vector & excludes, const BinT & bin, @@ -553,40 +553,30 @@ _NestResult _arrange( for (auto &itm : shapes ) inp.emplace_back(itm); for (auto &itm : excludes) inp.emplace_back(itm); - return arranger(inp.begin(), inp.end()); -} - -inline SLIC3R_CONSTEXPR coord_t stride_padding(coord_t w) -{ - return w + w / 5; + arranger(inp.begin(), inp.end()); } // The final client function for arrangement. A progress indicator and // a stop predicate can be also be passed to control the process. -bool arrange(ArrangeablePtrs & arrangables, - const ArrangeablePtrs & excludes, +void arrange(ArrangePolygons & arrangables, + const ArrangePolygons & excludes, coord_t min_obj_dist, const BedShapeHint & bedhint, std::function progressind, std::function stopcondition) { - bool ret = true; namespace clppr = ClipperLib; std::vector items, fixeditems; items.reserve(arrangables.size()); - coord_t binwidth = 0; - + + // Create Item from Arrangeable auto process_arrangeable = - [](const Arrangeable *arrangeable, std::vector &outp) + [](const ArrangePolygon &arrpoly, std::vector &outp) { - assert(arrangeable); - - auto arrangeitem = arrangeable->get_arrange_polygon(); - - Polygon & p = std::get<0>(arrangeitem); - const Vec2crd &offs = std::get<1>(arrangeitem); - double rotation = std::get<2>(arrangeitem); + Polygon p = arrpoly.poly.contour; + const Vec2crd & offs = arrpoly.translation; + double rotation = arrpoly.rotation; if (p.is_counter_clockwise()) p.reverse(); @@ -600,10 +590,10 @@ bool arrange(ArrangeablePtrs & arrangables, outp.back().translation({offs.x(), offs.y()}); }; - for (Arrangeable *arrangeable : arrangables) + for (ArrangePolygon &arrangeable : arrangables) process_arrangeable(arrangeable, items); - for (const Arrangeable * fixed: excludes) + for (const ArrangePolygon &fixed: excludes) process_arrangeable(fixed, fixeditems); // Integer ceiling the min distance from the bed perimeters @@ -619,7 +609,6 @@ bool arrange(ArrangeablePtrs & arrangables, BoundingBox bbb = bedhint.shape.box; bbb.min -= Point{md, md}, bbb.max += Point{md, md}; Box binbb{{bbb.min(X), bbb.min(Y)}, {bbb.max(X), bbb.max(Y)}}; - binwidth = coord_t(binbb.width()); _arrange(items, fixeditems, binbb, min_obj_dist, pri, cfn); break; @@ -627,7 +616,6 @@ bool arrange(ArrangeablePtrs & arrangables, case BedShapeType::CIRCLE: { auto c = bedhint.shape.circ; auto cc = to_lnCircle(c); - binwidth = scaled(c.radius()); _arrange(items, fixeditems, cc, min_obj_dist, pri, cfn); break; @@ -636,7 +624,6 @@ bool arrange(ArrangeablePtrs & arrangables, auto ctour = Slic3rMultiPoint_to_ClipperPath(bedhint.shape.polygon); auto irrbed = sl::create(std::move(ctour)); BoundingBox polybb(bedhint.shape.polygon); - binwidth = (polybb.max(X) - polybb.min(X)); _arrange(items, fixeditems, irrbed, min_obj_dist, pri, cfn); break; @@ -655,19 +642,22 @@ bool arrange(ArrangeablePtrs & arrangables, } }; - if(stopcondition && stopcondition()) return false; - - return ret; + for(size_t i = 0; i < items.size(); ++i) { + clppr::IntPoint tr = items[i].translation(); + arrangables[i].translation = {coord_t(tr.X), coord_t(tr.Y)}; + arrangables[i].rotation = items[i].rotation(); + arrangables[i].bed_idx = items[i].binId(); + } } // Arrange, without the fixed items (excludes) -bool arrange(ArrangeablePtrs & inp, - coord_t min_d, - const BedShapeHint & bedhint, - std::function prfn, - std::function stopfn) +void arrange(ArrangePolygons & inp, + coord_t min_d, + const BedShapeHint & bedhint, + std::function prfn, + std::function stopfn) { - return arrange(inp, {}, min_d, bedhint, prfn, stopfn); + arrange(inp, {}, min_d, bedhint, prfn, stopfn); } } // namespace arr diff --git a/src/libslic3r/Arrange.hpp b/src/libslic3r/Arrange.hpp index 337a7d959..bc23108cd 100644 --- a/src/libslic3r/Arrange.hpp +++ b/src/libslic3r/Arrange.hpp @@ -1,7 +1,7 @@ #ifndef MODELARRANGE_HPP #define MODELARRANGE_HPP -#include "Polygon.hpp" +#include "ExPolygon.hpp" #include "BoundingBox.hpp" namespace Slic3r { @@ -37,34 +37,57 @@ enum class BedShapeType { /// Info about the print bed for the arrange() function. struct BedShapeHint { BedShapeType type = BedShapeType::INFINITE; - /*union*/ struct { // I know but who cares... TODO: use variant from cpp17? + union BedShape_u { // I know but who cares... TODO: use variant from cpp17? CircleBed circ; BoundingBox box; Polyline polygon; - InfiniteBed infinite; + InfiniteBed infinite{}; + ~BedShape_u() {} + BedShape_u() {}; } shape; + + BedShapeHint() {}; + + ~BedShapeHint() { + if (type == BedShapeType::IRREGULAR) + shape.polygon.Slic3r::Polyline::~Polyline(); + }; + + BedShapeHint(const BedShapeHint &cpy) { + *this = cpy; + } + + BedShapeHint& operator=(const BedShapeHint &cpy) { + type = cpy.type; + switch(type) { + case BedShapeType::BOX: shape.box = cpy.shape.box; break; + case BedShapeType::CIRCLE: shape.circ = cpy.shape.circ; break; + case BedShapeType::IRREGULAR: shape.polygon = cpy.shape.polygon; break; + case BedShapeType::INFINITE: shape.infinite = cpy.shape.infinite; break; + case BedShapeType::UNKNOWN: break; + } + + return *this; + } }; /// Get a bed shape hint for arrange() from a naked Polyline. BedShapeHint bedShape(const Polyline& bed); -/** - * @brief Classes implementing the Arrangeable interface can be used as input - * to the arrange function. - */ -class Arrangeable { -public: +static const constexpr long UNARRANGED = -1; + +struct ArrangePolygon { + const ExPolygon poly; + Vec2crd translation{0, 0}; + double rotation{0.0}; + long bed_idx{UNARRANGED}; - virtual ~Arrangeable() = default; - - /// Apply the result transformation calculated by the arrangement. - virtual void apply_arrange_result(Vec2d offset, double rotation_rads, unsigned bed_num) = 0; - - /// Get the 2D silhouette to arrange and an initial offset and rotation - virtual std::tuple get_arrange_polygon() const = 0; + ArrangePolygon(const ExPolygon &p, const Vec2crd &tr = {}, double rot = 0.0) + : poly{p}, translation{tr}, rotation{rot} + {} }; -using ArrangeablePtrs = std::vector; +using ArrangePolygons = std::vector; /** * \brief Arranges the model objects on the screen. @@ -97,20 +120,20 @@ using ArrangeablePtrs = std::vector; * * \param stopcondition A predicate returning true if abort is needed. */ -bool arrange(ArrangeablePtrs &items, - coord_t min_obj_distance, - const BedShapeHint& bedhint, - std::function progressind = nullptr, - std::function stopcondition = nullptr); +void arrange(ArrangePolygons & items, + coord_t min_obj_distance, + const BedShapeHint & bedhint, + std::function progressind = nullptr, + std::function stopcondition = nullptr); /// Same as the previous, only that it takes unmovable items as an /// additional argument. -bool arrange(ArrangeablePtrs &items, - const ArrangeablePtrs &excludes, - coord_t min_obj_distance, - const BedShapeHint& bedhint, - std::function progressind = nullptr, - std::function stopcondition = nullptr); +void arrange(ArrangePolygons & items, + const ArrangePolygons & excludes, + coord_t min_obj_distance, + const BedShapeHint & bedhint, + std::function progressind = nullptr, + std::function stopcondition = nullptr); } // arr } // Slic3r diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 2af4e4c27..966be4c88 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -404,11 +404,16 @@ bool Model::arrange_objects(coordf_t dist, const BoundingBoxf* bb) size_t count = 0; for (auto obj : objects) count += obj->instances.size(); - arrangement::ArrangeablePtrs input; + arrangement::ArrangePolygons input; + ModelInstancePtrs instances; input.reserve(count); + instances.reserve(count); for (ModelObject *mo : objects) - for (ModelInstance *minst : mo->instances) - input.emplace_back(minst); + for (ModelInstance *minst : mo->instances) { + input.emplace_back(minst->get_arrange_polygon()); + instances.emplace_back(minst); + } + arrangement::BedShapeHint bedhint; @@ -417,7 +422,22 @@ bool Model::arrange_objects(coordf_t dist, const BoundingBoxf* bb) bedhint.shape.box = BoundingBox(scaled(bb->min), scaled(bb->max)); } - return arrangement::arrange(input, scaled(dist), bedhint); + arrangement::arrange(input, scaled(dist), bedhint); + + bool ret = true; + + for(size_t i = 0; i < input.size(); ++i) { + auto inst = instances[i]; + inst->set_rotation(Z, input[i].rotation); + auto tr = unscaled(input[i].translation); + inst->set_offset(X, tr.x()); + inst->set_offset(Y, tr.y()); + + if (input[i].bed_idx != 0) ret = false; // no logical beds are allowed + } + + + return ret; } // Duplicate the entire model preserving instance relative positions. @@ -1819,7 +1839,7 @@ void ModelInstance::transform_polygon(Polygon* polygon) const polygon->scale(get_scaling_factor(X), get_scaling_factor(Y)); // scale around polygon origin } -std::tuple ModelInstance::get_arrange_polygon() const +arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const { static const double SIMPLIFY_TOLERANCE_MM = 0.1; @@ -1835,15 +1855,15 @@ std::tuple ModelInstance::get_arrange_polygon() const // this may happen for malformed models, see: // https://github.com/prusa3d/PrusaSlicer/issues/2209 - if (p.points.empty()) return {}; + if (p.points.empty()) return {{}}; Polygons pp{p}; pp = p.simplify(scaled(SIMPLIFY_TOLERANCE_MM)); if (!pp.empty()) p = pp.front(); - - return std::make_tuple(p, - Vec2crd{scaled(get_offset(X)), scaled(get_offset(Y))}, - get_rotation(Z)); + + ExPolygon ep; ep.contour = std::move(p); + + return {ep, Vec2crd{scaled(get_offset(X)), scaled(get_offset(Y))}, get_rotation(Z)}; } // Test whether the two models contain the same number of ModelObjects with the same set of IDs diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index d0ed0bc88..2fd696d14 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -491,7 +491,7 @@ private: // A single instance of a ModelObject. // Knows the affine transformation of an object. -class ModelInstance : public ModelBase, public arrangement::Arrangeable +class ModelInstance : public ModelBase { public: enum EPrintVolumeState : unsigned char @@ -555,19 +555,19 @@ public: bool is_printable() const { return print_volume_state == PVS_Inside; } // ///////////////////////////////////////////////////////////////////////// - // Implement arr::Arrangeable interface + // Implement arrangement::Arrangeable interface // ///////////////////////////////////////////////////////////////////////// // Getting the input polygon for arrange - virtual std::tuple get_arrange_polygon() const override; + arrangement::ArrangePolygon get_arrange_polygon() const; // Apply the arrange result on the ModelInstance - virtual void apply_arrange_result(Vec2d offs, double rot_rads, unsigned /*bed_num*/) override + void apply_arrange_result(Vec2crd offs, double rot_rads) { // write the transformation data into the model instance set_rotation(Z, rot_rads); - set_offset(X, offs(X)); - set_offset(Y, offs(Y)); + set_offset(X, unscale(offs(X))); + set_offset(Y, unscale(offs(Y))); } protected: diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index e5b2b38f8..4e3093489 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -5739,7 +5739,7 @@ const SLAPrint* GLCanvas3D::sla_print() const return (m_process == nullptr) ? nullptr : m_process->sla_print(); } -void GLCanvas3D::WipeTowerInfo::apply_arrange_result(Vec2d offset, double rotation_rads, unsigned /*bed_num*/) +void GLCanvas3D::WipeTowerInfo::apply_arrange_result(Vec2d offset, double rotation_rads) { m_pos = offset; m_rotation = rotation_rads; diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 8f419a16d..2316637d8 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -612,7 +612,7 @@ public: int get_move_volume_id() const { return m_mouse.drag.move_volume_idx; } int get_first_hover_volume_idx() const { return m_hover_volume_idxs.empty() ? -1 : m_hover_volume_idxs.front(); } - class WipeTowerInfo: public arrangement::Arrangeable { + class WipeTowerInfo { Vec2d m_pos = {std::nan(""), std::nan("")}; Vec2d m_bb_size; double m_rotation; @@ -624,9 +624,9 @@ public: return !std::isnan(m_pos.x()) && !std::isnan(m_pos.y()); } - virtual void apply_arrange_result(Vec2d offset, double rotation_rads, unsigned /*bed_num*/) override; - - virtual std::tuple get_arrange_polygon() const override + void apply_arrange_result(Vec2d offset, double rotation_rads); + + arrangement::ArrangePolygon get_arrange_polygon() const { Polygon p({ {coord_t(0), coord_t(0)}, @@ -635,8 +635,9 @@ public: {coord_t(0), scaled(m_bb_size(Y))}, {coord_t(0), coord_t(0)}, }); - - return std::make_tuple(p, scaled(m_pos), m_rotation); + + ExPolygon ep; ep.contour = std::move(p); + return {ep, scaled(m_pos), m_rotation}; } }; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index a49b541b5..3adfc0f05 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1217,28 +1217,6 @@ bool PlaterDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &fi return true; } -namespace { -arrangement::ArrangeablePtrs get_arrange_input(Model &model, const Selection &sel) { - auto selmap = sel.get_content(); - - size_t count = 0; - for (auto obj : model.objects) count += obj->instances.size(); - - arrangement::ArrangeablePtrs ret; ret.reserve(count); - - if (selmap.empty()) - for (ModelObject *mo : model.objects) - for (ModelInstance *minst : mo->instances) - ret.emplace_back(minst); - else - for (auto &s : selmap) - for (auto &instid : s.second) - ret.emplace_back(model.objects[s.first]->instances[instid]); - - return ret; -} -} - // Plater / private struct Plater::priv { @@ -1447,17 +1425,18 @@ struct Plater::priv class ArrangeJob : public Job { GLCanvas3D::WipeTowerInfo m_wti; - arrangement::ArrangeablePtrs m_selected, m_unselected; - - static std::array collect( + arrangement::ArrangePolygons m_selected, m_unselected; + + static std::array collect( Model &model, const Selection &sel) { - auto selmap = sel.get_content(); - + const Selection::ObjectIdxsToInstanceIdxsMap &selmap = + sel.get_content(); + size_t count = 0; for (auto obj : model.objects) count += obj->instances.size(); - arrangement::ArrangeablePtrs selected, unselected; + arrangement::ArrangePolygons selected, unselected; selected.reserve(count + 1 /* for optional wti */); unselected.reserve(count + 1 /* for optional wti */); @@ -1475,12 +1454,12 @@ struct Plater::priv ModelInstance *inst = model.objects[oidx] ->instances[iidx]; instit == iids.end() ? - unselected.emplace_back(inst) : - selected.emplace_back(inst); + unselected.emplace_back(inst->get_arrange_polygon()) : + selected.emplace_back(inst->get_arrange_polygon()); } } else // object not selected, all instances are unselected for (auto inst : model.objects[oidx]->instances) - unselected.emplace_back(inst); + unselected.emplace_back(inst->get_arrange_polygon()); } if (selected.empty()) selected.swap(unselected); @@ -1495,14 +1474,15 @@ struct Plater::priv m_wti = plater().view3D->get_canvas3d()->get_wipe_tower_info(); const Selection& sel = plater().get_selection(); + BoundingBoxf bedbb(plater().bed.get_shape()); auto arrinput = collect(plater().model, sel); m_selected.swap(arrinput[0]); m_unselected.swap(arrinput[1]); if (m_wti) sel.is_wipe_tower() ? - m_selected.emplace_back(&m_wti) : - m_unselected.emplace_back(&m_wti); + m_selected.emplace_back(m_wti.get_arrange_polygon()) : + m_unselected.emplace_back(m_wti.get_arrange_polygon()); } public: