mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-12 10:39:04 +08:00
Fix faulty virtual bed handling
Changes to firstfit selection to use a map of bed contexts to avoid memory overuse when a fixed item has a large bed index number. fixes SPE-1844
This commit is contained in:
parent
aea278ab55
commit
1d4594ad66
@ -2,6 +2,7 @@
|
|||||||
#define ARRANGEIMPL_HPP
|
#define ARRANGEIMPL_HPP
|
||||||
|
|
||||||
#include <random>
|
#include <random>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
#include "Arrange.hpp"
|
#include "Arrange.hpp"
|
||||||
|
|
||||||
@ -43,25 +44,36 @@ void arrange(SelectionStrategy &&selstrategy,
|
|||||||
std::forward<PackStrategy>(packingstrategy), items, fixed,
|
std::forward<PackStrategy>(packingstrategy), items, fixed,
|
||||||
RectangleBed{bed.bb}, SelStrategyTag<SelectionStrategy>{});
|
RectangleBed{bed.bb}, SelStrategyTag<SelectionStrategy>{});
|
||||||
|
|
||||||
size_t beds = get_bed_count(crange(items));
|
std::vector<int> bed_indices = get_bed_indices(items, fixed);
|
||||||
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);
|
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<BoundingBox> pilebb(bed_indices.size());
|
||||||
|
|
||||||
for (auto &itm : items) {
|
for (auto &itm : items) {
|
||||||
auto bedidx = get_bed_index(itm);
|
auto bedidx = get_bed_index(itm);
|
||||||
if (bedidx >= 0) {
|
if (bedidx >= 0) {
|
||||||
pilebb[bedidx].merge(fixed_bounding_box(itm));
|
pilebb[bedidx].merge(fixed_bounding_box(itm));
|
||||||
if (is_wipe_tower(itm))
|
if (is_wipe_tower(itm))
|
||||||
fixed_is_empty[bedidx] = false;
|
set_bed_as_empty(bedidx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &fxitm : fixed) {
|
for (auto &fxitm : fixed) {
|
||||||
auto bedidx = get_bed_index(fxitm);
|
auto bedidx = get_bed_index(fxitm);
|
||||||
if (bedidx >= 0 || is_wipe_tower(fxitm))
|
if (bedidx >= 0 || is_wipe_tower(fxitm))
|
||||||
fixed_is_empty[bedidx] = false;
|
set_bed_as_empty(bedidx);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto bedbb = bounding_box(bed);
|
auto bedbb = bounding_box(bed);
|
||||||
@ -74,7 +86,7 @@ void arrange(SelectionStrategy &&selstrategy,
|
|||||||
Pivots pivot = bed.alignment();
|
Pivots pivot = bed.alignment();
|
||||||
|
|
||||||
for (size_t bedidx = 0; bedidx < beds; ++bedidx) {
|
for (size_t bedidx = 0; bedidx < beds; ++bedidx) {
|
||||||
if (! fixed_is_empty[bedidx])
|
if (! fixed_is_empty(bedidx))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
BoundingBox bb;
|
BoundingBox bb;
|
||||||
|
@ -233,8 +233,44 @@ void arrange(SelectionStrategy &&selstrategy,
|
|||||||
"Arrange unimplemented for this selection strategy");
|
"Arrange unimplemented for this selection strategy");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class It>
|
||||||
|
std::vector<int> get_bed_indices(const Range<It> &items)
|
||||||
|
{
|
||||||
|
auto bed_indices = reserve_vector<int>(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<class It, class CIt>
|
||||||
|
std::vector<int> get_bed_indices(const Range<It> &items, const Range<CIt> &fixed)
|
||||||
|
{
|
||||||
|
std::vector<int> 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<class It>
|
template<class It>
|
||||||
size_t get_bed_count(const Range<It> &items)
|
size_t get_bed_count(const Range<It> &items)
|
||||||
|
{
|
||||||
|
return get_bed_indices(items).size();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class It> int get_max_bed_index(const Range<It> &items)
|
||||||
{
|
{
|
||||||
auto it = std::max_element(items.begin(),
|
auto it = std::max_element(items.begin(),
|
||||||
items.end(),
|
items.end(),
|
||||||
@ -242,11 +278,11 @@ size_t get_bed_count(const Range<It> &items)
|
|||||||
return get_bed_index(i1) < get_bed_index(i2);
|
return get_bed_index(i1) < get_bed_index(i2);
|
||||||
});
|
});
|
||||||
|
|
||||||
size_t beds = 0;
|
int ret = Unarranged;
|
||||||
if (it != items.end())
|
if (it != items.end())
|
||||||
beds = get_bed_index(*it) + 1;
|
ret = get_bed_index(*it);
|
||||||
|
|
||||||
return beds;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DefaultStopCondition {
|
struct DefaultStopCondition {
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#define ARRANGEFIRSTFIT_HPP
|
#define ARRANGEFIRSTFIT_HPP
|
||||||
|
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
#include <libslic3r/Arrange/Core/ArrangeBase.hpp>
|
#include <libslic3r/Arrange/Core/ArrangeBase.hpp>
|
||||||
|
|
||||||
@ -88,21 +89,28 @@ void arrange(
|
|||||||
sorted_items.emplace_back(itm);
|
sorted_items.emplace_back(itm);
|
||||||
}
|
}
|
||||||
|
|
||||||
int max_bed_idx = get_bed_count(fixed);
|
|
||||||
|
|
||||||
using Context = PackStrategyContext<PackStrategy, ArrItem>;
|
using Context = PackStrategyContext<PackStrategy, ArrItem>;
|
||||||
|
|
||||||
auto bed_contexts = reserve_vector<Context>(max_bed_idx + 1);
|
std::map<int, Context> 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<ArrItem>(ps, bed, bedidx));
|
||||||
|
|
||||||
|
assert(res.second);
|
||||||
|
|
||||||
|
ctx_it = res.first;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx_it->second;
|
||||||
|
};
|
||||||
|
|
||||||
for (auto &itm : fixed) {
|
for (auto &itm : fixed) {
|
||||||
if (get_bed_index(itm) >= 0) {
|
auto bedidx = get_bed_index(itm);
|
||||||
auto bedidx = static_cast<size_t>(get_bed_index(itm));
|
if (bedidx >= 0) {
|
||||||
|
Context &ctx = get_or_init_context(bedidx);
|
||||||
while (bed_contexts.size() <= bedidx)
|
add_fixed_item(ctx, itm);
|
||||||
bed_contexts.emplace_back(
|
|
||||||
create_context<ArrItem>(ps, bed, bedidx));
|
|
||||||
|
|
||||||
add_fixed_item(bed_contexts[bedidx], itm);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,18 +132,20 @@ void arrange(
|
|||||||
|
|
||||||
while (it != sorted_items.end() && !is_cancelled()) {
|
while (it != sorted_items.end() && !is_cancelled()) {
|
||||||
bool was_packed = false;
|
bool was_packed = false;
|
||||||
size_t j = 0;
|
int bedidx = 0;
|
||||||
while (!was_packed && !is_cancelled()) {
|
while (!was_packed && !is_cancelled()) {
|
||||||
for (; j < bed_contexts.size() && !was_packed && !is_cancelled(); j++) {
|
for (; !was_packed && !is_cancelled(); bedidx++) {
|
||||||
set_bed_index(*it, int(j));
|
set_bed_index(*it, bedidx);
|
||||||
|
|
||||||
auto remaining = Range{std::next(static_cast<SConstIt>(it)),
|
auto remaining = Range{std::next(static_cast<SConstIt>(it)),
|
||||||
sorted_items.cend()};
|
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) {
|
if(was_packed) {
|
||||||
add_packed_item(bed_contexts[j], *it);
|
add_packed_item(ctx, *it);
|
||||||
|
|
||||||
auto packed_range = Range{sorted_items.cbegin(),
|
auto packed_range = Range{sorted_items.cbegin(),
|
||||||
static_cast<SConstIt>(it)};
|
static_cast<SConstIt>(it)};
|
||||||
@ -145,12 +155,6 @@ void arrange(
|
|||||||
set_bed_index(*it, Unarranged);
|
set_bed_index(*it, Unarranged);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!was_packed) {
|
|
||||||
bed_contexts.emplace_back(
|
|
||||||
create_context<ArrItem>(ps, bed, bed_contexts.size()));
|
|
||||||
j = bed_contexts.size() - 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
|
@ -304,22 +304,28 @@ Transform3d YStriderVBedHandler::get_physical_bed_trafo(int bed_index) const
|
|||||||
return tr;
|
return tr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const int GridStriderVBedHandler::ColsOutside =
|
const int GridStriderVBedHandler::Cols =
|
||||||
static_cast<int>(std::sqrt(std::numeric_limits<int>::max()));
|
2 * static_cast<int>(std::sqrt(std::numeric_limits<int>::max()) / 2);
|
||||||
|
|
||||||
|
const int GridStriderVBedHandler::HalfCols = Cols / 2;
|
||||||
|
const int GridStriderVBedHandler::Offset = HalfCols + Cols * HalfCols;
|
||||||
|
|
||||||
Vec2i GridStriderVBedHandler::raw2grid(int bed_idx) const
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int GridStriderVBedHandler::grid2raw(const Vec2i &crd) const
|
int GridStriderVBedHandler::grid2raw(const Vec2i &crd) const
|
||||||
{
|
{
|
||||||
assert(std::abs(crd.x()) < ColsOutside - 1 &&
|
// Overlapping virtual beds will happen if the crd values exceed limits
|
||||||
std::abs(crd.y()) < ColsOutside - 1);
|
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
|
int GridStriderVBedHandler::get_bed_index(const VBedPlaceable &obj) const
|
||||||
|
@ -338,7 +338,9 @@ class GridStriderVBedHandler: public VirtualBedHandler
|
|||||||
// not representable with scaled coordinates. Combining XStrider with
|
// not representable with scaled coordinates. Combining XStrider with
|
||||||
// YStrider takes care of the X and Y axis to be mapped into the physical
|
// 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)
|
// 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;
|
XStriderVBedHandler m_xstrider;
|
||||||
YStriderVBedHandler m_ystrider;
|
YStriderVBedHandler m_ystrider;
|
||||||
|
@ -75,6 +75,21 @@ void prepare_fixed_unselected(ItemCont &items, int shift)
|
|||||||
items.end());
|
items.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline int find_first_empty_bed(const std::vector<int>& 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<class ArrItem>
|
template<class ArrItem>
|
||||||
std::unique_ptr<ArrangeTaskResult>
|
std::unique_ptr<ArrangeTaskResult>
|
||||||
ArrangeTask<ArrItem>::process_native(Ctl &ctl)
|
ArrangeTask<ArrItem>::process_native(Ctl &ctl)
|
||||||
@ -109,15 +124,17 @@ ArrangeTask<ArrItem>::process_native(Ctl &ctl)
|
|||||||
|
|
||||||
arranger->arrange(printable.selected, fixed_items, bed, subctl);
|
arranger->arrange(printable.selected, fixed_items, bed, subctl);
|
||||||
|
|
||||||
// Unprintable items should go to the first bed not containing any printable
|
std::vector<int> printable_bed_indices =
|
||||||
// items
|
get_bed_indices(crange(printable.selected), crange(printable.unselected));
|
||||||
auto beds = std::max(get_bed_count(crange(printable.selected)),
|
|
||||||
get_bed_count(crange(printable.unselected)));
|
|
||||||
|
|
||||||
// If there are no printables, leave the physical bed empty
|
// 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);
|
arranger->arrange(unprintable.selected, unprintable.unselected, bed, ctl);
|
||||||
|
|
||||||
@ -125,7 +142,7 @@ ArrangeTask<ArrItem>::process_native(Ctl &ctl)
|
|||||||
|
|
||||||
for (auto &itm : unprintable.selected) {
|
for (auto &itm : unprintable.selected) {
|
||||||
if (is_arranged(itm)) {
|
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);
|
arr2::set_bed_index(itm, bedidx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user