mirror of
https://github.com/QIDITECH/QIDISlicer.git
synced 2026-02-02 08:58:43 +03:00
Merge prusa 2.6.1
This commit is contained in:
82
src/libslic3r/Arrange/Tasks/ArrangeTask.hpp
Normal file
82
src/libslic3r/Arrange/Tasks/ArrangeTask.hpp
Normal file
@@ -0,0 +1,82 @@
|
||||
|
||||
#ifndef ARRANGETASK_HPP
|
||||
#define ARRANGETASK_HPP
|
||||
|
||||
#include "libslic3r/Arrange/Arrange.hpp"
|
||||
#include "libslic3r/Arrange/Items/TrafoOnlyArrangeItem.hpp"
|
||||
|
||||
namespace Slic3r { namespace arr2 {
|
||||
|
||||
struct ArrangeTaskResult : public ArrangeResult
|
||||
{
|
||||
std::vector<TrafoOnlyArrangeItem> items;
|
||||
|
||||
bool apply_on(ArrangeableModel &mdl) override
|
||||
{
|
||||
bool ret = true;
|
||||
for (auto &itm : items) {
|
||||
if (is_arranged(itm))
|
||||
ret = ret && apply_arrangeitem(itm, mdl);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<class ArrItem>
|
||||
void add_item(const ArrItem &itm)
|
||||
{
|
||||
items.emplace_back(itm);
|
||||
if (auto id = retrieve_id(itm))
|
||||
imbue_id(items.back(), *id);
|
||||
}
|
||||
|
||||
template<class It>
|
||||
void add_items(const Range<It> &items_range)
|
||||
{
|
||||
for (auto &itm : items_range)
|
||||
add_item(itm);
|
||||
}
|
||||
};
|
||||
|
||||
template<class ArrItem> struct ArrangeTask : public ArrangeTaskBase
|
||||
{
|
||||
struct ArrangeSet
|
||||
{
|
||||
std::vector<ArrItem> selected, unselected;
|
||||
} printable, unprintable;
|
||||
|
||||
ExtendedBed bed;
|
||||
ArrangeSettings settings;
|
||||
|
||||
static std::unique_ptr<ArrangeTask> create(
|
||||
const Scene &sc,
|
||||
const ArrangeableToItemConverter<ArrItem> &converter);
|
||||
|
||||
static std::unique_ptr<ArrangeTask> create(const Scene &sc)
|
||||
{
|
||||
auto conv = ArrangeableToItemConverter<ArrItem>::create(sc);
|
||||
return create(sc, *conv);
|
||||
}
|
||||
|
||||
std::unique_ptr<ArrangeResult> process(Ctl &ctl) override
|
||||
{
|
||||
return process_native(ctl);
|
||||
}
|
||||
|
||||
std::unique_ptr<ArrangeTaskResult> process_native(Ctl &ctl);
|
||||
std::unique_ptr<ArrangeTaskResult> process_native(Ctl &&ctl)
|
||||
{
|
||||
return process_native(ctl);
|
||||
}
|
||||
|
||||
int item_count_to_process() const override
|
||||
{
|
||||
return static_cast<int>(printable.selected.size() +
|
||||
unprintable.selected.size());
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace arr2
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // ARRANGETASK_HPP
|
||||
159
src/libslic3r/Arrange/Tasks/ArrangeTaskImpl.hpp
Normal file
159
src/libslic3r/Arrange/Tasks/ArrangeTaskImpl.hpp
Normal file
@@ -0,0 +1,159 @@
|
||||
|
||||
#ifndef ARRANGETASK_IMPL_HPP
|
||||
#define ARRANGETASK_IMPL_HPP
|
||||
|
||||
#include <random>
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include "ArrangeTask.hpp"
|
||||
|
||||
namespace Slic3r { namespace arr2 {
|
||||
|
||||
// Prepare the selected and unselected items separately. If nothing is
|
||||
// selected, behaves as if everything would be selected.
|
||||
template<class ArrItem>
|
||||
void extract_selected(ArrangeTask<ArrItem> &task,
|
||||
const ArrangeableModel &mdl,
|
||||
const ArrangeableToItemConverter<ArrItem> &itm_conv)
|
||||
{
|
||||
// Go through the objects and check if inside the selection
|
||||
mdl.for_each_arrangeable(
|
||||
[&task, &itm_conv](const Arrangeable &arrbl) {
|
||||
bool selected = arrbl.is_selected();
|
||||
bool printable = arrbl.is_printable();
|
||||
|
||||
try {
|
||||
auto itm = itm_conv.convert(arrbl, selected ? 0 : -SCALED_EPSILON);
|
||||
|
||||
auto &container_parent = printable ? task.printable :
|
||||
task.unprintable;
|
||||
|
||||
auto &container = selected ?
|
||||
container_parent.selected :
|
||||
container_parent.unselected;
|
||||
|
||||
container.emplace_back(std::move(itm));
|
||||
} catch (const EmptyItemOutlineError &ex) {
|
||||
BOOST_LOG_TRIVIAL(error)
|
||||
<< "ObjectID " << std::to_string(arrbl.id().id) << ": " << ex.what();
|
||||
}
|
||||
});
|
||||
|
||||
// If the selection was empty arrange everything
|
||||
if (task.printable.selected.empty() && task.unprintable.selected.empty()) {
|
||||
task.printable.selected.swap(task.printable.unselected);
|
||||
task.unprintable.selected.swap(task.unprintable.unselected);
|
||||
}
|
||||
}
|
||||
|
||||
template<class ArrItem>
|
||||
std::unique_ptr<ArrangeTask<ArrItem>> ArrangeTask<ArrItem>::create(
|
||||
const Scene &sc, const ArrangeableToItemConverter<ArrItem> &converter)
|
||||
{
|
||||
auto task = std::make_unique<ArrangeTask<ArrItem>>();
|
||||
|
||||
task->settings.set_from(sc.settings());
|
||||
|
||||
task->bed = get_corrected_bed(sc.bed(), converter);
|
||||
|
||||
extract_selected(*task, sc.model(), converter);
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
// Remove all items on the physical bed (not occupyable for unprintable items)
|
||||
// and shift all items to the next lower bed index, so that arrange will think
|
||||
// that logical bed no. 1 is the physical one
|
||||
template<class ItemCont>
|
||||
void prepare_fixed_unselected(ItemCont &items, int shift)
|
||||
{
|
||||
for (auto &itm : items)
|
||||
set_bed_index(itm, get_bed_index(itm) - shift);
|
||||
|
||||
items.erase(std::remove_if(items.begin(), items.end(),
|
||||
[](auto &itm) { return !is_arranged(itm); }),
|
||||
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>
|
||||
std::unique_ptr<ArrangeTaskResult>
|
||||
ArrangeTask<ArrItem>::process_native(Ctl &ctl)
|
||||
{
|
||||
auto result = std::make_unique<ArrangeTaskResult>();
|
||||
|
||||
auto arranger = Arranger<ArrItem>::create(settings);
|
||||
|
||||
class TwoStepArrangeCtl: public Ctl
|
||||
{
|
||||
Ctl &parent;
|
||||
ArrangeTask &self;
|
||||
public:
|
||||
TwoStepArrangeCtl(Ctl &p, ArrangeTask &slf) : parent{p}, self{slf} {}
|
||||
|
||||
void update_status(int remaining) override
|
||||
{
|
||||
parent.update_status(remaining + self.unprintable.selected.size());
|
||||
}
|
||||
|
||||
bool was_canceled() const override { return parent.was_canceled(); }
|
||||
|
||||
} subctl{ctl, *this};
|
||||
|
||||
auto fixed_items = printable.unselected;
|
||||
|
||||
// static (unselected) unprintable objects should not be overlapped by
|
||||
// movable and printable objects
|
||||
std::copy(unprintable.unselected.begin(),
|
||||
unprintable.unselected.end(),
|
||||
std::back_inserter(fixed_items));
|
||||
|
||||
arranger->arrange(printable.selected, fixed_items, bed, subctl);
|
||||
|
||||
std::vector<int> printable_bed_indices =
|
||||
get_bed_indices(crange(printable.selected), crange(printable.unselected));
|
||||
|
||||
// If there are no printables, leave the physical bed empty
|
||||
constexpr int SearchFrom = 1;
|
||||
|
||||
// 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);
|
||||
|
||||
result->add_items(crange(printable.selected));
|
||||
|
||||
for (auto &itm : unprintable.selected) {
|
||||
if (is_arranged(itm)) {
|
||||
int bedidx = get_bed_index(itm) + first_empty_bed;
|
||||
arr2::set_bed_index(itm, bedidx);
|
||||
}
|
||||
|
||||
result->add_item(itm);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace arr2
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif //ARRANGETASK_IMPL_HPP
|
||||
54
src/libslic3r/Arrange/Tasks/FillBedTask.hpp
Normal file
54
src/libslic3r/Arrange/Tasks/FillBedTask.hpp
Normal file
@@ -0,0 +1,54 @@
|
||||
|
||||
#ifndef FILLBEDTASK_HPP
|
||||
#define FILLBEDTASK_HPP
|
||||
|
||||
#include "MultiplySelectionTask.hpp"
|
||||
|
||||
#include "libslic3r/Arrange/Arrange.hpp"
|
||||
|
||||
namespace Slic3r { namespace arr2 {
|
||||
|
||||
struct FillBedTaskResult: public MultiplySelectionTaskResult {};
|
||||
|
||||
template<class ArrItem>
|
||||
struct FillBedTask: public ArrangeTaskBase
|
||||
{
|
||||
std::optional<ArrItem> prototype_item;
|
||||
|
||||
std::vector<ArrItem> selected, unselected;
|
||||
|
||||
ArrangeSettings settings;
|
||||
ExtendedBed bed;
|
||||
size_t selected_existing_count = 0;
|
||||
|
||||
std::unique_ptr<FillBedTaskResult> process_native(Ctl &ctl);
|
||||
std::unique_ptr<FillBedTaskResult> process_native(Ctl &&ctl)
|
||||
{
|
||||
return process_native(ctl);
|
||||
}
|
||||
|
||||
std::unique_ptr<ArrangeResult> process(Ctl &ctl) override
|
||||
{
|
||||
return process_native(ctl);
|
||||
}
|
||||
|
||||
int item_count_to_process() const override
|
||||
{
|
||||
return selected.size();
|
||||
}
|
||||
|
||||
static std::unique_ptr<FillBedTask> create(
|
||||
const Scene &sc,
|
||||
const ArrangeableToItemConverter<ArrItem> &converter);
|
||||
|
||||
static std::unique_ptr<FillBedTask> create(const Scene &sc)
|
||||
{
|
||||
auto conv = ArrangeableToItemConverter<ArrItem>::create(sc);
|
||||
return create(sc, *conv);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace arr2
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // FILLBEDTASK_HPP
|
||||
202
src/libslic3r/Arrange/Tasks/FillBedTaskImpl.hpp
Normal file
202
src/libslic3r/Arrange/Tasks/FillBedTaskImpl.hpp
Normal file
@@ -0,0 +1,202 @@
|
||||
|
||||
#ifndef FILLBEDTASKIMPL_HPP
|
||||
#define FILLBEDTASKIMPL_HPP
|
||||
|
||||
#include "FillBedTask.hpp"
|
||||
|
||||
#include "Arrange/Core/NFP/NFPArrangeItemTraits.hpp"
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
namespace Slic3r { namespace arr2 {
|
||||
|
||||
template<class ArrItem>
|
||||
int calculate_items_needed_to_fill_bed(const ExtendedBed &bed,
|
||||
const ArrItem &prototype_item,
|
||||
size_t prototype_count,
|
||||
const std::vector<ArrItem> &fixed)
|
||||
{
|
||||
double poly_area = fixed_area(prototype_item);
|
||||
|
||||
auto area_sum_fn = [](double s, const auto &itm) {
|
||||
return s + (get_bed_index(itm) == 0) * fixed_area(itm);
|
||||
};
|
||||
|
||||
double unsel_area = std::accumulate(fixed.begin(),
|
||||
fixed.end(),
|
||||
0.,
|
||||
area_sum_fn);
|
||||
|
||||
double fixed_area = unsel_area + prototype_count * poly_area;
|
||||
double bed_area = 0.;
|
||||
|
||||
visit_bed([&bed_area] (auto &realbed) { bed_area = area(realbed); }, bed);
|
||||
|
||||
// This is the maximum number of items,
|
||||
// the real number will always be close but less.
|
||||
auto needed_items = static_cast<int>(
|
||||
std::ceil((bed_area - fixed_area) / poly_area));
|
||||
|
||||
return needed_items;
|
||||
}
|
||||
|
||||
template<class ArrItem>
|
||||
void extract(FillBedTask<ArrItem> &task,
|
||||
const Scene &scene,
|
||||
const ArrangeableToItemConverter<ArrItem> &itm_conv)
|
||||
{
|
||||
task.prototype_item = {};
|
||||
|
||||
auto selected_ids = scene.selected_ids();
|
||||
|
||||
if (selected_ids.empty())
|
||||
return;
|
||||
|
||||
std::set<ObjectID> selected_objects = selected_geometry_ids(scene);
|
||||
|
||||
if (selected_objects.size() != 1)
|
||||
return;
|
||||
|
||||
ObjectID prototype_geometry_id = *(selected_objects.begin());
|
||||
|
||||
auto set_prototype_item = [&task, &itm_conv](const Arrangeable &arrbl) {
|
||||
if (arrbl.is_printable())
|
||||
task.prototype_item = itm_conv.convert(arrbl);
|
||||
};
|
||||
|
||||
scene.model().visit_arrangeable(selected_ids.front(), set_prototype_item);
|
||||
|
||||
if (!task.prototype_item)
|
||||
return;
|
||||
|
||||
// Workaround for missing items when arranging the same geometry only:
|
||||
// Injecting a number of items but with slightly shrinked shape, so that
|
||||
// they can fill the emerging holes. Priority is set to lowest so that
|
||||
// these filler items will only be inserted as the last ones.
|
||||
ArrItem prototype_item_shrinked;
|
||||
scene.model().visit_arrangeable(selected_ids.front(),
|
||||
[&prototype_item_shrinked, &itm_conv](const Arrangeable &arrbl) {
|
||||
if (arrbl.is_printable())
|
||||
prototype_item_shrinked = itm_conv.convert(arrbl, -SCALED_EPSILON);
|
||||
});
|
||||
|
||||
set_bed_index(*task.prototype_item, Unarranged);
|
||||
|
||||
auto collect_task_items = [&prototype_geometry_id, &task,
|
||||
&itm_conv](const Arrangeable &arrbl) {
|
||||
try {
|
||||
if (arrbl.geometry_id() == prototype_geometry_id) {
|
||||
if (arrbl.is_printable()) {
|
||||
auto itm = itm_conv.convert(arrbl);
|
||||
raise_priority(itm);
|
||||
task.selected.emplace_back(std::move(itm));
|
||||
}
|
||||
} else {
|
||||
auto itm = itm_conv.convert(arrbl, -SCALED_EPSILON);
|
||||
task.unselected.emplace_back(std::move(itm));
|
||||
}
|
||||
} catch (const EmptyItemOutlineError &ex) {
|
||||
BOOST_LOG_TRIVIAL(error)
|
||||
<< "ObjectID " << std::to_string(arrbl.id().id) << ": " << ex.what();
|
||||
}
|
||||
};
|
||||
|
||||
// Set the lowest priority to the shrinked prototype (hole filler) item
|
||||
set_priority(prototype_item_shrinked,
|
||||
lowest_priority(range(task.selected)) - 1);
|
||||
|
||||
scene.model().for_each_arrangeable(collect_task_items);
|
||||
|
||||
int needed_items = calculate_items_needed_to_fill_bed(task.bed,
|
||||
*task.prototype_item,
|
||||
task.selected.size(),
|
||||
task.unselected);
|
||||
|
||||
task.selected_existing_count = task.selected.size();
|
||||
task.selected.reserve(task.selected.size() + needed_items);
|
||||
std::fill_n(std::back_inserter(task.selected), needed_items,
|
||||
*task.prototype_item);
|
||||
|
||||
// Add as many filler items as there are needed items. Most of them will
|
||||
// be discarded anyways.
|
||||
std::fill_n(std::back_inserter(task.selected), needed_items,
|
||||
prototype_item_shrinked);
|
||||
}
|
||||
|
||||
|
||||
template<class ArrItem>
|
||||
std::unique_ptr<FillBedTask<ArrItem>> FillBedTask<ArrItem>::create(
|
||||
const Scene &sc, const ArrangeableToItemConverter<ArrItem> &converter)
|
||||
{
|
||||
auto task = std::make_unique<FillBedTask<ArrItem>>();
|
||||
|
||||
task->settings.set_from(sc.settings());
|
||||
|
||||
task->bed = get_corrected_bed(sc.bed(), converter);
|
||||
|
||||
extract(*task, sc, converter);
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
template<class ArrItem>
|
||||
std::unique_ptr<FillBedTaskResult> FillBedTask<ArrItem>::process_native(
|
||||
Ctl &ctl)
|
||||
{
|
||||
auto result = std::make_unique<FillBedTaskResult>();
|
||||
|
||||
if (!prototype_item)
|
||||
return result;
|
||||
|
||||
result->prototype_id = retrieve_id(*prototype_item).value_or(ObjectID{});
|
||||
|
||||
class FillBedCtl: public ArrangerCtl<ArrItem>
|
||||
{
|
||||
ArrangeTaskCtl &parent;
|
||||
FillBedTask &self;
|
||||
bool do_stop = false;
|
||||
|
||||
public:
|
||||
FillBedCtl(ArrangeTaskCtl &p, FillBedTask &slf) : parent{p}, self{slf} {}
|
||||
|
||||
void update_status(int remaining) override
|
||||
{
|
||||
parent.update_status(remaining);
|
||||
}
|
||||
|
||||
bool was_canceled() const override
|
||||
{
|
||||
return parent.was_canceled() || do_stop;
|
||||
}
|
||||
|
||||
void on_packed(ArrItem &itm) override
|
||||
{
|
||||
// Stop at the first filler that is not on the physical bed
|
||||
do_stop = get_bed_index(itm) > PhysicalBedId && get_priority(itm) < 0;
|
||||
}
|
||||
|
||||
} subctl(ctl, *this);
|
||||
|
||||
auto arranger = Arranger<ArrItem>::create(settings);
|
||||
|
||||
arranger->arrange(selected, unselected, bed, subctl);
|
||||
|
||||
auto arranged_range = Range{selected.begin(),
|
||||
selected.begin() + selected_existing_count};
|
||||
|
||||
result->add_arranged_items(arranged_range);
|
||||
|
||||
auto to_add_range = Range{selected.begin() + selected_existing_count,
|
||||
selected.end()};
|
||||
|
||||
for (auto &itm : to_add_range)
|
||||
if (get_bed_index(itm) == PhysicalBedId)
|
||||
result->add_new_item(itm);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace arr2
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // FILLBEDTASKIMPL_HPP
|
||||
109
src/libslic3r/Arrange/Tasks/MultiplySelectionTask.hpp
Normal file
109
src/libslic3r/Arrange/Tasks/MultiplySelectionTask.hpp
Normal file
@@ -0,0 +1,109 @@
|
||||
|
||||
#ifndef MULTIPLYSELECTIONTASK_HPP
|
||||
#define MULTIPLYSELECTIONTASK_HPP
|
||||
|
||||
#include "libslic3r/Arrange/Arrange.hpp"
|
||||
#include "libslic3r/Arrange/Items/TrafoOnlyArrangeItem.hpp"
|
||||
|
||||
namespace Slic3r { namespace arr2 {
|
||||
|
||||
struct MultiplySelectionTaskResult: public ArrangeResult {
|
||||
ObjectID prototype_id;
|
||||
|
||||
std::vector<TrafoOnlyArrangeItem> arranged_items;
|
||||
std::vector<TrafoOnlyArrangeItem> to_add;
|
||||
|
||||
bool apply_on(ArrangeableModel &mdl) override
|
||||
{
|
||||
bool ret = prototype_id.valid();
|
||||
|
||||
if (!ret)
|
||||
return ret;
|
||||
|
||||
for (auto &itm : to_add) {
|
||||
auto id = mdl.add_arrangeable(prototype_id);
|
||||
imbue_id(itm, id);
|
||||
ret = ret && apply_arrangeitem(itm, mdl);
|
||||
}
|
||||
|
||||
for (auto &itm : arranged_items) {
|
||||
if (is_arranged(itm))
|
||||
ret = ret && apply_arrangeitem(itm, mdl);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<class ArrItem>
|
||||
void add_arranged_item(const ArrItem &itm)
|
||||
{
|
||||
arranged_items.emplace_back(itm);
|
||||
if (auto id = retrieve_id(itm))
|
||||
imbue_id(arranged_items.back(), *id);
|
||||
}
|
||||
|
||||
template<class It>
|
||||
void add_arranged_items(const Range<It> &items_range)
|
||||
{
|
||||
arranged_items.reserve(items_range.size());
|
||||
for (auto &itm : items_range)
|
||||
add_arranged_item(itm);
|
||||
}
|
||||
|
||||
template<class ArrItem> void add_new_item(const ArrItem &itm)
|
||||
{
|
||||
to_add.emplace_back(itm);
|
||||
}
|
||||
|
||||
template<class It> void add_new_items(const Range<It> &items_range)
|
||||
{
|
||||
to_add.reserve(items_range.size());
|
||||
for (auto &itm : items_range) {
|
||||
to_add.emplace_back(itm);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<class ArrItem>
|
||||
struct MultiplySelectionTask: public ArrangeTaskBase
|
||||
{
|
||||
std::optional<ArrItem> prototype_item;
|
||||
|
||||
std::vector<ArrItem> selected, unselected;
|
||||
|
||||
ArrangeSettings settings;
|
||||
ExtendedBed bed;
|
||||
size_t selected_existing_count = 0;
|
||||
|
||||
std::unique_ptr<MultiplySelectionTaskResult> process_native(Ctl &ctl);
|
||||
std::unique_ptr<MultiplySelectionTaskResult> process_native(Ctl &&ctl)
|
||||
{
|
||||
return process_native(ctl);
|
||||
}
|
||||
|
||||
std::unique_ptr<ArrangeResult> process(Ctl &ctl) override
|
||||
{
|
||||
return process_native(ctl);
|
||||
}
|
||||
|
||||
int item_count_to_process() const override
|
||||
{
|
||||
return selected.size();
|
||||
}
|
||||
|
||||
static std::unique_ptr<MultiplySelectionTask> create(
|
||||
const Scene &sc,
|
||||
size_t multiply_count,
|
||||
const ArrangeableToItemConverter<ArrItem> &converter);
|
||||
|
||||
static std::unique_ptr<MultiplySelectionTask> create(const Scene &sc,
|
||||
size_t multiply_count)
|
||||
{
|
||||
auto conv = ArrangeableToItemConverter<ArrItem>::create(sc);
|
||||
return create(sc, multiply_count, *conv);
|
||||
}
|
||||
};
|
||||
|
||||
}} // namespace Slic3r::arr2
|
||||
|
||||
#endif // MULTIPLYSELECTIONTASK_HPP
|
||||
128
src/libslic3r/Arrange/Tasks/MultiplySelectionTaskImpl.hpp
Normal file
128
src/libslic3r/Arrange/Tasks/MultiplySelectionTaskImpl.hpp
Normal file
@@ -0,0 +1,128 @@
|
||||
|
||||
#ifndef MULTIPLYSELECTIONTASKIMPL_HPP
|
||||
#define MULTIPLYSELECTIONTASKIMPL_HPP
|
||||
|
||||
#include "MultiplySelectionTask.hpp"
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
namespace Slic3r { namespace arr2 {
|
||||
|
||||
template<class ArrItem>
|
||||
std::unique_ptr<MultiplySelectionTask<ArrItem>> MultiplySelectionTask<ArrItem>::create(
|
||||
const Scene &scene, size_t count, const ArrangeableToItemConverter<ArrItem> &itm_conv)
|
||||
{
|
||||
auto task_ptr = std::make_unique<MultiplySelectionTask<ArrItem>>();
|
||||
|
||||
auto &task = *task_ptr;
|
||||
|
||||
task.settings.set_from(scene.settings());
|
||||
|
||||
task.bed = get_corrected_bed(scene.bed(), itm_conv);
|
||||
|
||||
task.prototype_item = {};
|
||||
|
||||
auto selected_ids = scene.selected_ids();
|
||||
|
||||
if (selected_ids.empty())
|
||||
return task_ptr;
|
||||
|
||||
std::set<ObjectID> selected_objects = selected_geometry_ids(scene);
|
||||
|
||||
if (selected_objects.size() != 1)
|
||||
return task_ptr;
|
||||
|
||||
ObjectID prototype_geometry_id = *(selected_objects.begin());
|
||||
|
||||
auto set_prototype_item = [&task, &itm_conv](const Arrangeable &arrbl) {
|
||||
if (arrbl.is_printable())
|
||||
task.prototype_item = itm_conv.convert(arrbl);
|
||||
};
|
||||
|
||||
scene.model().visit_arrangeable(selected_ids.front(), set_prototype_item);
|
||||
|
||||
if (!task.prototype_item)
|
||||
return task_ptr;
|
||||
|
||||
set_bed_index(*task.prototype_item, Unarranged);
|
||||
|
||||
auto collect_task_items = [&prototype_geometry_id, &task,
|
||||
&itm_conv](const Arrangeable &arrbl) {
|
||||
try {
|
||||
if (arrbl.geometry_id() == prototype_geometry_id) {
|
||||
if (arrbl.is_printable()) {
|
||||
auto itm = itm_conv.convert(arrbl);
|
||||
raise_priority(itm);
|
||||
task.selected.emplace_back(std::move(itm));
|
||||
}
|
||||
} else {
|
||||
auto itm = itm_conv.convert(arrbl, -SCALED_EPSILON);
|
||||
task.unselected.emplace_back(std::move(itm));
|
||||
}
|
||||
} catch (const EmptyItemOutlineError &ex) {
|
||||
BOOST_LOG_TRIVIAL(error)
|
||||
<< "ObjectID " << std::to_string(arrbl.id().id) << ": " << ex.what();
|
||||
}
|
||||
};
|
||||
|
||||
scene.model().for_each_arrangeable(collect_task_items);
|
||||
|
||||
task.selected_existing_count = task.selected.size();
|
||||
task.selected.reserve(task.selected.size() + count);
|
||||
std::fill_n(std::back_inserter(task.selected), count, *task.prototype_item);
|
||||
|
||||
return task_ptr;
|
||||
}
|
||||
|
||||
template<class ArrItem>
|
||||
std::unique_ptr<MultiplySelectionTaskResult>
|
||||
MultiplySelectionTask<ArrItem>::process_native(Ctl &ctl)
|
||||
{
|
||||
auto result = std::make_unique<MultiplySelectionTaskResult>();
|
||||
|
||||
if (!prototype_item)
|
||||
return result;
|
||||
|
||||
result->prototype_id = retrieve_id(*prototype_item).value_or(ObjectID{});
|
||||
|
||||
class MultiplySelectionCtl: public ArrangerCtl<ArrItem>
|
||||
{
|
||||
ArrangeTaskCtl &parent;
|
||||
MultiplySelectionTask<ArrItem> &self;
|
||||
|
||||
public:
|
||||
MultiplySelectionCtl(ArrangeTaskCtl &p, MultiplySelectionTask<ArrItem> &slf)
|
||||
: parent{p}, self{slf} {}
|
||||
|
||||
void update_status(int remaining) override
|
||||
{
|
||||
parent.update_status(remaining);
|
||||
}
|
||||
|
||||
bool was_canceled() const override
|
||||
{
|
||||
return parent.was_canceled();
|
||||
}
|
||||
|
||||
} subctl(ctl, *this);
|
||||
|
||||
auto arranger = Arranger<ArrItem>::create(settings);
|
||||
|
||||
arranger->arrange(selected, unselected, bed, subctl);
|
||||
|
||||
auto arranged_range = Range{selected.begin(),
|
||||
selected.begin() + selected_existing_count};
|
||||
|
||||
result->add_arranged_items(arranged_range);
|
||||
|
||||
auto to_add_range = Range{selected.begin() + selected_existing_count,
|
||||
selected.end()};
|
||||
|
||||
result->add_new_items(to_add_range);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}} // namespace Slic3r::arr2
|
||||
|
||||
#endif // MULTIPLYSELECTIONTASKIMPL_HPP
|
||||
Reference in New Issue
Block a user