diff --git a/src/libslic3r/Arrange/ArrangeImpl.hpp b/src/libslic3r/Arrange/ArrangeImpl.hpp index d827be3df6..2bbe9ab29c 100644 --- a/src/libslic3r/Arrange/ArrangeImpl.hpp +++ b/src/libslic3r/Arrange/ArrangeImpl.hpp @@ -2,6 +2,7 @@ #define ARRANGEIMPL_HPP #include +#include #include "Arrange.hpp" @@ -43,25 +44,36 @@ void arrange(SelectionStrategy &&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 bed_indices = get_bed_indices(items, fixed); - std::vector pilebb(beds); + size_t beds = bed_indices.size(); + + auto fixed_is_empty = [&bed_indices](int bidx) { + auto it = std::lower_bound(bed_indices.begin(), bed_indices.end(), bidx); + return it == bed_indices.end() || *it != bidx; + }; + + auto set_bed_as_empty = [&bed_indices](int bidx) { + auto it = std::lower_bound(bed_indices.begin(), bed_indices.end(), bidx); + if (it != bed_indices.end()) + bed_indices.erase(it); + }; + + std::vector pilebb(bed_indices.size()); 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; + set_bed_as_empty(bedidx); } } for (auto &fxitm : fixed) { auto bedidx = get_bed_index(fxitm); if (bedidx >= 0 || is_wipe_tower(fxitm)) - fixed_is_empty[bedidx] = false; + set_bed_as_empty(bedidx); } auto bedbb = bounding_box(bed); @@ -74,7 +86,7 @@ void arrange(SelectionStrategy &&selstrategy, Pivots pivot = bed.alignment(); for (size_t bedidx = 0; bedidx < beds; ++bedidx) { - if (! fixed_is_empty[bedidx]) + if (! fixed_is_empty(bedidx)) continue; BoundingBox bb; diff --git a/src/libslic3r/Arrange/Core/ArrangeBase.hpp b/src/libslic3r/Arrange/Core/ArrangeBase.hpp index 97177d4b3c..d341903d72 100644 --- a/src/libslic3r/Arrange/Core/ArrangeBase.hpp +++ b/src/libslic3r/Arrange/Core/ArrangeBase.hpp @@ -233,8 +233,44 @@ void arrange(SelectionStrategy &&selstrategy, "Arrange unimplemented for this selection strategy"); } +template +std::vector get_bed_indices(const Range &items) +{ + auto bed_indices = reserve_vector(items.size()); + + for (auto &itm : items) + bed_indices.emplace_back(get_bed_index(itm)); + + std::sort(bed_indices.begin(), bed_indices.end()); + auto endit = std::unique(bed_indices.begin(), bed_indices.end()); + + bed_indices.erase(endit, bed_indices.end()); + + return bed_indices; +} + +template +std::vector get_bed_indices(const Range &items, const Range &fixed) +{ + std::vector ret; + + auto iitems = get_bed_indices(items); + auto ifixed = get_bed_indices(fixed); + ret.reserve(std::max(iitems.size(), ifixed.size())); + std::set_union(iitems.begin(), iitems.end(), + ifixed.begin(), ifixed.end(), + std::back_inserter(ret)); + + return ret; +} + template size_t get_bed_count(const Range &items) +{ + return get_bed_indices(items).size(); +} + +template int get_max_bed_index(const Range &items) { auto it = std::max_element(items.begin(), items.end(), @@ -242,11 +278,11 @@ size_t get_bed_count(const Range &items) return get_bed_index(i1) < get_bed_index(i2); }); - size_t beds = 0; + int ret = Unarranged; if (it != items.end()) - beds = get_bed_index(*it) + 1; + ret = get_bed_index(*it); - return beds; + return ret; } struct DefaultStopCondition { diff --git a/src/libslic3r/Arrange/Core/ArrangeFirstFit.hpp b/src/libslic3r/Arrange/Core/ArrangeFirstFit.hpp index ee6940b770..cf8d3e457a 100644 --- a/src/libslic3r/Arrange/Core/ArrangeFirstFit.hpp +++ b/src/libslic3r/Arrange/Core/ArrangeFirstFit.hpp @@ -2,6 +2,7 @@ #define ARRANGEFIRSTFIT_HPP #include +#include #include @@ -88,21 +89,28 @@ void arrange( 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); + std::map bed_contexts; + auto get_or_init_context = [&ps, &bed, &bed_contexts](int bedidx) -> Context& { + auto ctx_it = bed_contexts.find(bedidx); + if (ctx_it == bed_contexts.end()) { + auto res = bed_contexts.emplace( + bedidx, create_context(ps, bed, bedidx)); + + assert(res.second); + + ctx_it = res.first; + } + + return ctx_it->second; + }; 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); + auto bedidx = get_bed_index(itm); + if (bedidx >= 0) { + Context &ctx = get_or_init_context(bedidx); + add_fixed_item(ctx, itm); } } @@ -124,18 +132,20 @@ void arrange( while (it != sorted_items.end() && !is_cancelled()) { bool was_packed = false; - size_t j = 0; + int bedidx = 0; while (!was_packed && !is_cancelled()) { - for (; j < bed_contexts.size() && !was_packed && !is_cancelled(); j++) { - set_bed_index(*it, int(j)); + for (; !was_packed && !is_cancelled(); bedidx++) { + set_bed_index(*it, bedidx); auto remaining = Range{std::next(static_cast(it)), sorted_items.cend()}; - was_packed = pack(ps, bed, *it, bed_contexts[j], remaining); + Context &ctx = get_or_init_context(bedidx); + + was_packed = pack(ps, bed, *it, ctx, remaining); if(was_packed) { - add_packed_item(bed_contexts[j], *it); + add_packed_item(ctx, *it); auto packed_range = Range{sorted_items.cbegin(), static_cast(it)}; @@ -145,12 +155,6 @@ void arrange( 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; } diff --git a/src/libslic3r/Arrange/SceneBuilder.cpp b/src/libslic3r/Arrange/SceneBuilder.cpp index 75990e2f84..12c4556670 100644 --- a/src/libslic3r/Arrange/SceneBuilder.cpp +++ b/src/libslic3r/Arrange/SceneBuilder.cpp @@ -304,22 +304,28 @@ Transform3d YStriderVBedHandler::get_physical_bed_trafo(int bed_index) const return tr; } -const int GridStriderVBedHandler::ColsOutside = - static_cast(std::sqrt(std::numeric_limits::max())); +const int GridStriderVBedHandler::Cols = + 2 * static_cast(std::sqrt(std::numeric_limits::max()) / 2); + +const int GridStriderVBedHandler::HalfCols = Cols / 2; +const int GridStriderVBedHandler::Offset = HalfCols + Cols * HalfCols; Vec2i GridStriderVBedHandler::raw2grid(int bed_idx) const { - Vec2i ret{bed_idx % ColsOutside, bed_idx / ColsOutside}; + bed_idx += Offset; + + Vec2i ret{bed_idx % Cols - HalfCols, bed_idx / Cols - HalfCols}; return ret; } int GridStriderVBedHandler::grid2raw(const Vec2i &crd) const { - assert(std::abs(crd.x()) < ColsOutside - 1 && - std::abs(crd.y()) < ColsOutside - 1); + // Overlapping virtual beds will happen if the crd values exceed limits + assert((crd.x() < HalfCols - 1 && crd.x() >= -HalfCols) && + (crd.y() < HalfCols - 1 && crd.y() >= -HalfCols)); - return crd.y() * ColsOutside + crd.x(); + return (crd.x() + HalfCols) + Cols * (crd.y() + HalfCols) - Offset; } int GridStriderVBedHandler::get_bed_index(const VBedPlaceable &obj) const diff --git a/src/libslic3r/Arrange/SceneBuilder.hpp b/src/libslic3r/Arrange/SceneBuilder.hpp index d159669f51..54146cd3cc 100644 --- a/src/libslic3r/Arrange/SceneBuilder.hpp +++ b/src/libslic3r/Arrange/SceneBuilder.hpp @@ -338,7 +338,9 @@ class GridStriderVBedHandler: public VirtualBedHandler // not representable with scaled coordinates. Combining XStrider with // YStrider takes care of the X and Y axis to be mapped into the physical // bed's coordinate region (which is representable in scaled coords) - static const int ColsOutside; + static const int Cols; + static const int HalfCols; + static const int Offset; XStriderVBedHandler m_xstrider; YStriderVBedHandler m_ystrider; diff --git a/src/libslic3r/Arrange/Tasks/ArrangeTaskImpl.hpp b/src/libslic3r/Arrange/Tasks/ArrangeTaskImpl.hpp index 8925ea4b95..a46152ed98 100644 --- a/src/libslic3r/Arrange/Tasks/ArrangeTaskImpl.hpp +++ b/src/libslic3r/Arrange/Tasks/ArrangeTaskImpl.hpp @@ -75,6 +75,21 @@ void prepare_fixed_unselected(ItemCont &items, int shift) items.end()); } +inline int find_first_empty_bed(const std::vector& bed_indices, + int starting_from = 0) { + int ret = starting_from; + + for (int idx : bed_indices) { + if (idx == ret) { + ret++; + } else if (idx > ret) { + break; + } + } + + return ret; +} + template std::unique_ptr ArrangeTask::process_native(Ctl &ctl) @@ -109,15 +124,17 @@ ArrangeTask::process_native(Ctl &ctl) arranger->arrange(printable.selected, fixed_items, bed, subctl); - // 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))); + std::vector printable_bed_indices = + get_bed_indices(crange(printable.selected), crange(printable.unselected)); // If there are no printables, leave the physical bed empty - beds = std::max(beds, size_t{1}); + constexpr int SearchFrom = 1; - prepare_fixed_unselected(unprintable.unselected, beds); + // Unprintable items should go to the first logical (!) bed not containing + // any printable items + int first_empty_bed = find_first_empty_bed(printable_bed_indices, SearchFrom); + + prepare_fixed_unselected(unprintable.unselected, first_empty_bed); arranger->arrange(unprintable.selected, unprintable.unselected, bed, ctl); @@ -125,7 +142,7 @@ ArrangeTask::process_native(Ctl &ctl) for (auto &itm : unprintable.selected) { if (is_arranged(itm)) { - int bedidx = get_bed_index(itm) + beds; + int bedidx = get_bed_index(itm) + first_empty_bed; arr2::set_bed_index(itm, bedidx); }