mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-12 21:39:02 +08:00
Merge branch 'tm_arrange_overflows'
This commit is contained in:
commit
c159451efe
@ -83,11 +83,19 @@ void Arranger<ArrItem>::arrange(std::vector<ArrItem> &items,
|
||||
arrange(items, fixed, bed, DefaultArrangerCtl<ArrItem>{ctl});
|
||||
}
|
||||
|
||||
class EmptyItemOutlineError: public std::exception {
|
||||
static constexpr const char *Msg = "No outline can be derived for object";
|
||||
|
||||
public:
|
||||
const char* what() const noexcept override { return Msg; }
|
||||
};
|
||||
|
||||
template<class ArrItem> class ArrangeableToItemConverter
|
||||
{
|
||||
public:
|
||||
virtual ~ArrangeableToItemConverter() = default;
|
||||
|
||||
// May throw EmptyItemOutlineError
|
||||
virtual ArrItem convert(const Arrangeable &arrbl, coord_t offs = 0) const = 0;
|
||||
|
||||
// Returns the extent of simplification that the converter utilizes when
|
||||
|
@ -2,6 +2,7 @@
|
||||
#define ARRANGEIMPL_HPP
|
||||
|
||||
#include <random>
|
||||
#include <map>
|
||||
|
||||
#include "Arrange.hpp"
|
||||
|
||||
@ -43,25 +44,23 @@ void arrange(SelectionStrategy &&selstrategy,
|
||||
std::forward<PackStrategy>(packingstrategy), items, fixed,
|
||||
RectangleBed{bed.bb}, SelStrategyTag<SelectionStrategy>{});
|
||||
|
||||
size_t beds = get_bed_count(crange(items));
|
||||
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);
|
||||
std::vector<int> bed_indices = get_bed_indices(items, fixed);
|
||||
std::map<int, BoundingBox> pilebb;
|
||||
std::map<int, bool> bed_occupied;
|
||||
|
||||
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;
|
||||
bed_occupied[bedidx] = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &fxitm : fixed) {
|
||||
auto bedidx = get_bed_index(fxitm);
|
||||
if (bedidx >= 0 || is_wipe_tower(fxitm))
|
||||
fixed_is_empty[bedidx] = false;
|
||||
if (bedidx >= 0)
|
||||
bed_occupied[bedidx] = true;
|
||||
}
|
||||
|
||||
auto bedbb = bounding_box(bed);
|
||||
@ -73,8 +72,9 @@ void arrange(SelectionStrategy &&selstrategy,
|
||||
|
||||
Pivots pivot = bed.alignment();
|
||||
|
||||
for (size_t bedidx = 0; bedidx < beds; ++bedidx) {
|
||||
if (! fixed_is_empty[bedidx])
|
||||
for (int bedidx : bed_indices) {
|
||||
if (auto occup_it = bed_occupied.find(bedidx);
|
||||
occup_it != bed_occupied.end() && occup_it->second)
|
||||
continue;
|
||||
|
||||
BoundingBox bb;
|
||||
@ -272,7 +272,7 @@ class DefaultArranger: public Arranger<ArrItem> {
|
||||
int pa = get_priority(itm1);
|
||||
int pb = get_priority(itm2);
|
||||
|
||||
return pa == pb ? envelope_area(itm1) > envelope_area(itm2) :
|
||||
return pa == pb ? area(envelope_convex_hull(itm1)) > area(envelope_convex_hull(itm2)) :
|
||||
pa > pb;
|
||||
};
|
||||
|
||||
@ -295,7 +295,11 @@ class DefaultArranger: public Arranger<ArrItem> {
|
||||
default:
|
||||
[[fallthrough]];
|
||||
case ArrangeSettingsView::asAuto:
|
||||
basekernel = TMArrangeKernel{items.size(), area(bed)};
|
||||
if constexpr (std::is_convertible_v<Bed, CircleBed>){
|
||||
basekernel = GravityKernel{};
|
||||
} else {
|
||||
basekernel = TMArrangeKernel{items.size(), area(bed)};
|
||||
}
|
||||
break;
|
||||
case ArrangeSettingsView::asPullToCenter:
|
||||
basekernel = GravityKernel{};
|
||||
@ -370,6 +374,10 @@ ArrItem ConvexItemConverter<ArrItem>::convert(const Arrangeable &arrbl,
|
||||
{
|
||||
auto bed_index = arrbl.get_bed_index();
|
||||
Polygon outline = arrbl.convex_outline();
|
||||
|
||||
if (outline.empty())
|
||||
throw EmptyItemOutlineError{};
|
||||
|
||||
Polygon envelope = arrbl.convex_envelope();
|
||||
|
||||
coord_t infl = offs + coord_t(std::ceil(this->safety_dist() / 2.));
|
||||
@ -418,6 +426,10 @@ ArrItem AdvancedItemConverter<ArrItem>::get_arritem(const Arrangeable &arrbl,
|
||||
coord_t infl = offs + coord_t(std::ceil(this->safety_dist() / 2.));
|
||||
|
||||
auto outline = arrbl.full_outline();
|
||||
|
||||
if (outline.empty())
|
||||
throw EmptyItemOutlineError{};
|
||||
|
||||
auto envelope = arrbl.full_envelope();
|
||||
|
||||
if (infl != 0) {
|
||||
|
@ -233,8 +233,44 @@ void arrange(SelectionStrategy &&selstrategy,
|
||||
"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>
|
||||
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(),
|
||||
items.end(),
|
||||
@ -242,11 +278,11 @@ size_t get_bed_count(const Range<It> &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 {
|
||||
|
@ -2,6 +2,7 @@
|
||||
#define ARRANGEFIRSTFIT_HPP
|
||||
|
||||
#include <iterator>
|
||||
#include <map>
|
||||
|
||||
#include <libslic3r/Arrange/Core/ArrangeBase.hpp>
|
||||
|
||||
@ -88,21 +89,28 @@ void arrange(
|
||||
sorted_items.emplace_back(itm);
|
||||
}
|
||||
|
||||
int max_bed_idx = get_bed_count(fixed);
|
||||
|
||||
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) {
|
||||
if (get_bed_index(itm) >= 0) {
|
||||
auto bedidx = static_cast<size_t>(get_bed_index(itm));
|
||||
|
||||
while (bed_contexts.size() <= bedidx)
|
||||
bed_contexts.emplace_back(
|
||||
create_context<ArrItem>(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<SConstIt>(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<SConstIt>(it)};
|
||||
@ -145,12 +155,6 @@ void arrange(
|
||||
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;
|
||||
}
|
||||
|
@ -110,7 +110,7 @@ template<class Fn> auto call_with_bed(const Points &bed, Fn &&fn)
|
||||
|
||||
if ((1.0 - parea / area(bb)) < 1e-3) {
|
||||
return fn(RectangleBed{bb});
|
||||
} else if (!std::isnan(circ.radius()))
|
||||
} else if (!std::isnan(circ.radius()) && (1.0 - parea / area(circ)) < 1e-2)
|
||||
return fn(circ);
|
||||
else
|
||||
return fn(IrregularBed{{ExPolygon(bed)}});
|
||||
|
@ -54,11 +54,11 @@ std::set<ObjectID> selected_geometry_ids(const Scene &sc)
|
||||
return result;
|
||||
}
|
||||
|
||||
void arrange(Scene &scene, ArrangeTaskCtl &ctl)
|
||||
bool arrange(Scene &scene, ArrangeTaskCtl &ctl)
|
||||
{
|
||||
auto task = ArrangeTaskBase::create(Tasks::Arrange, scene);
|
||||
auto result = task->process(ctl);
|
||||
result->apply_on(scene.model());
|
||||
return result->apply_on(scene.model());
|
||||
}
|
||||
|
||||
}} // namespace Slic3r::arr2
|
||||
|
@ -161,6 +161,14 @@ template<class BedFn> void visit_bed(BedFn &&fn, ExtendedBed &bed)
|
||||
boost::apply_visitor(fn, bed);
|
||||
}
|
||||
|
||||
inline BoundingBox bounding_box(const ExtendedBed &bed)
|
||||
{
|
||||
BoundingBox bedbb;
|
||||
visit_bed([&bedbb](auto &rawbed) { bedbb = bounding_box(rawbed); }, bed);
|
||||
|
||||
return bedbb;
|
||||
}
|
||||
|
||||
class Scene;
|
||||
|
||||
// SceneBuilderBase is intended for Scene construction. A simple constructor
|
||||
@ -365,10 +373,26 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
void arrange(Scene &scene, ArrangeTaskCtl &ctl);
|
||||
inline void arrange(Scene &scene, ArrangeTaskCtl &&ctl = DummyCtl{})
|
||||
bool arrange(Scene &scene, ArrangeTaskCtl &ctl);
|
||||
inline bool arrange(Scene &scene, ArrangeTaskCtl &&ctl = DummyCtl{})
|
||||
{
|
||||
arrange(scene, ctl);
|
||||
return arrange(scene, ctl);
|
||||
}
|
||||
|
||||
inline bool arrange(Scene &&scene, ArrangeTaskCtl &ctl)
|
||||
{
|
||||
return arrange(scene, ctl);
|
||||
}
|
||||
|
||||
inline bool arrange(Scene &&scene, ArrangeTaskCtl &&ctl = DummyCtl{})
|
||||
{
|
||||
return arrange(scene, ctl);
|
||||
}
|
||||
|
||||
template<class Builder, class Ctl = DummyCtl>
|
||||
bool arrange(SceneBuilderBase<Builder> &&builder, Ctl &&ctl = {})
|
||||
{
|
||||
return arrange(Scene{std::move(builder)}, ctl);
|
||||
}
|
||||
|
||||
} // namespace arr2
|
||||
|
@ -64,16 +64,18 @@ void transform_instance(ModelInstance &mi,
|
||||
mi.invalidate_object_bounding_box();
|
||||
}
|
||||
|
||||
BoundingBoxf3 instance_bounding_box(const ModelInstance &mi, bool dont_translate)
|
||||
BoundingBoxf3 instance_bounding_box(const ModelInstance &mi,
|
||||
const Transform3d &tr,
|
||||
bool dont_translate)
|
||||
{
|
||||
BoundingBoxf3 bb;
|
||||
const Transform3d inst_matrix
|
||||
= dont_translate ? mi.get_transformation().get_matrix_no_offset()
|
||||
: mi.get_transformation().get_matrix();
|
||||
: mi.get_transformation().get_matrix();
|
||||
|
||||
for (ModelVolume *v : mi.get_object()->volumes) {
|
||||
if (v->is_model_part()) {
|
||||
bb.merge(v->mesh().transformed_bounding_box(inst_matrix
|
||||
bb.merge(v->mesh().transformed_bounding_box(tr * inst_matrix
|
||||
* v->get_matrix()));
|
||||
}
|
||||
}
|
||||
@ -81,22 +83,39 @@ BoundingBoxf3 instance_bounding_box(const ModelInstance &mi, bool dont_translate
|
||||
return bb;
|
||||
}
|
||||
|
||||
BoundingBoxf3 instance_bounding_box(const ModelInstance &mi, bool dont_translate)
|
||||
{
|
||||
return instance_bounding_box(mi, Transform3d::Identity(), dont_translate);
|
||||
}
|
||||
|
||||
bool check_coord_bounds(const BoundingBoxf &bb)
|
||||
{
|
||||
return std::abs(bb.min.x()) < UnscaledCoordLimit &&
|
||||
std::abs(bb.min.y()) < UnscaledCoordLimit &&
|
||||
std::abs(bb.max.x()) < UnscaledCoordLimit &&
|
||||
std::abs(bb.max.y()) < UnscaledCoordLimit;
|
||||
}
|
||||
|
||||
ExPolygons extract_full_outline(const ModelInstance &inst, const Transform3d &tr)
|
||||
{
|
||||
ExPolygons outline;
|
||||
for (const ModelVolume *v : inst.get_object()->volumes) {
|
||||
Polygons vol_outline;
|
||||
vol_outline = project_mesh(v->mesh().its,
|
||||
tr * inst.get_matrix() * v->get_matrix(),
|
||||
[] {});
|
||||
switch (v->type()) {
|
||||
case ModelVolumeType::MODEL_PART:
|
||||
outline = union_ex(outline, vol_outline);
|
||||
break;
|
||||
case ModelVolumeType::NEGATIVE_VOLUME:
|
||||
outline = diff_ex(outline, vol_outline);
|
||||
break;
|
||||
default:;
|
||||
|
||||
if (check_coord_bounds(to_2d(instance_bounding_box(inst, tr)))) {
|
||||
for (const ModelVolume *v : inst.get_object()->volumes) {
|
||||
Polygons vol_outline;
|
||||
|
||||
vol_outline = project_mesh(v->mesh().its,
|
||||
tr * inst.get_matrix() * v->get_matrix(),
|
||||
[] {});
|
||||
switch (v->type()) {
|
||||
case ModelVolumeType::MODEL_PART:
|
||||
outline = union_ex(outline, vol_outline);
|
||||
break;
|
||||
case ModelVolumeType::NEGATIVE_VOLUME:
|
||||
outline = diff_ex(outline, vol_outline);
|
||||
break;
|
||||
default:;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -105,7 +124,14 @@ ExPolygons extract_full_outline(const ModelInstance &inst, const Transform3d &tr
|
||||
|
||||
Polygon extract_convex_outline(const ModelInstance &inst, const Transform3d &tr)
|
||||
{
|
||||
return inst.get_object()->convex_hull_2d(tr * inst.get_matrix());
|
||||
auto bb = to_2d(instance_bounding_box(inst, tr));
|
||||
Polygon ret;
|
||||
|
||||
if (check_coord_bounds(bb)) {
|
||||
ret = inst.get_object()->convex_hull_2d(tr * inst.get_matrix());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline static bool is_infinite_bed(const ExtendedBed &ebed) noexcept
|
||||
@ -171,6 +197,17 @@ void SceneBuilder::build_arrangeable_slicer_model(ArrangeableSlicerModel &amodel
|
||||
m_wipetower_handler = std::make_unique<MissingWipeTowerHandler>();
|
||||
}
|
||||
|
||||
if (m_fff_print && !m_xl_printer)
|
||||
m_xl_printer = is_XL_printer(m_fff_print->config());
|
||||
|
||||
bool has_wipe_tower = false;
|
||||
m_wipetower_handler->visit(
|
||||
[&has_wipe_tower](const Arrangeable &arrbl) { has_wipe_tower = true; });
|
||||
|
||||
if (m_xl_printer && !has_wipe_tower) {
|
||||
m_bed = XLBed{bounding_box(m_bed)};
|
||||
}
|
||||
|
||||
amodel.m_vbed_handler = std::move(m_vbed_handler);
|
||||
amodel.m_model = std::move(m_model);
|
||||
amodel.m_selmask = std::move(m_selection);
|
||||
@ -190,7 +227,14 @@ int XStriderVBedHandler::get_bed_index(const VBedPlaceable &obj) const
|
||||
auto reference_pos_x = (instance_bb.min.x() - bedx);
|
||||
auto stride = unscaled(stride_s);
|
||||
|
||||
bedidx = static_cast<int>(std::floor(reference_pos_x / stride));
|
||||
auto bedidx_d = std::floor(reference_pos_x / stride);
|
||||
|
||||
if (bedidx_d < std::numeric_limits<int>::min())
|
||||
bedidx = std::numeric_limits<int>::min();
|
||||
else if (bedidx_d > std::numeric_limits<int>::max())
|
||||
bedidx = std::numeric_limits<int>::max();
|
||||
else
|
||||
bedidx = static_cast<int>(bedidx_d);
|
||||
}
|
||||
|
||||
return bedidx;
|
||||
@ -231,7 +275,14 @@ int YStriderVBedHandler::get_bed_index(const VBedPlaceable &obj) const
|
||||
auto reference_pos_y = (instance_bb.min.y() - ystart);
|
||||
auto stride = unscaled(stride_s);
|
||||
|
||||
bedidx = static_cast<int>(std::floor(reference_pos_y / stride));
|
||||
auto bedidx_d = std::floor(reference_pos_y / stride);
|
||||
|
||||
if (bedidx_d < std::numeric_limits<int>::min())
|
||||
bedidx = std::numeric_limits<int>::min();
|
||||
else if (bedidx_d > std::numeric_limits<int>::max())
|
||||
bedidx = std::numeric_limits<int>::max();
|
||||
else
|
||||
bedidx = static_cast<int>(bedidx_d);
|
||||
}
|
||||
|
||||
return bedidx;
|
||||
@ -262,21 +313,28 @@ Transform3d YStriderVBedHandler::get_physical_bed_trafo(int bed_index) const
|
||||
return tr;
|
||||
}
|
||||
|
||||
const int GridStriderVBedHandler::ColsOutside =
|
||||
static_cast<int>(std::sqrt(std::numeric_limits<int>::max()));
|
||||
const int GridStriderVBedHandler::Cols =
|
||||
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 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(crd.x() < ColsOutside - 1 && 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
|
||||
@ -394,11 +452,11 @@ SceneBuilder &&SceneBuilder::set_bed(const DynamicPrintConfig &cfg)
|
||||
Points bedpts = get_bed_shape(cfg);
|
||||
|
||||
if (is_XL_printer(cfg)) {
|
||||
m_bed = XLBed{get_extents(bedpts)};
|
||||
} else {
|
||||
m_bed = arr2::to_arrange_bed(bedpts);
|
||||
m_xl_printer = true;
|
||||
}
|
||||
|
||||
m_bed = arr2::to_arrange_bed(bedpts);
|
||||
|
||||
return std::move(*this);
|
||||
}
|
||||
|
||||
|
@ -205,6 +205,7 @@ protected:
|
||||
|
||||
AnyPtr<const SLAPrint> m_sla_print;
|
||||
AnyPtr<const Print> m_fff_print;
|
||||
bool m_xl_printer = false;
|
||||
|
||||
void set_brim_and_skirt();
|
||||
|
||||
@ -338,7 +339,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;
|
||||
@ -377,6 +380,11 @@ void transform_instance(ModelInstance &mi,
|
||||
BoundingBoxf3 instance_bounding_box(const ModelInstance &mi,
|
||||
bool dont_translate = false);
|
||||
|
||||
BoundingBoxf3 instance_bounding_box(const ModelInstance &mi,
|
||||
const Transform3d &tr,
|
||||
bool dont_translate = false);
|
||||
|
||||
constexpr double UnscaledCoordLimit = 1000.;
|
||||
|
||||
ExPolygons extract_full_outline(const ModelInstance &inst,
|
||||
const Transform3d &tr = Transform3d::Identity());
|
||||
@ -491,9 +499,9 @@ class ArrangeableSLAPrint : public ArrangeableSlicerModel {
|
||||
static void visit_arrangeable_(Self &&self, const ObjectID &id, Fn &&fn);
|
||||
|
||||
public:
|
||||
explicit ArrangeableSLAPrint(const SLAPrint *slaprint,
|
||||
SceneBuilder &builder)
|
||||
: m_slaprint{slaprint}, ArrangeableSlicerModel{builder}
|
||||
explicit ArrangeableSLAPrint(const SLAPrint *slaprint, SceneBuilder &builder)
|
||||
: m_slaprint{slaprint}
|
||||
, ArrangeableSlicerModel{builder}
|
||||
{
|
||||
assert(slaprint != nullptr);
|
||||
}
|
||||
|
@ -3,6 +3,8 @@
|
||||
|
||||
#include <random>
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include "ArrangeTask.hpp"
|
||||
|
||||
namespace Slic3r { namespace arr2 {
|
||||
@ -20,16 +22,21 @@ void extract_selected(ArrangeTask<ArrItem> &task,
|
||||
bool selected = arrbl.is_selected();
|
||||
bool printable = arrbl.is_printable();
|
||||
|
||||
auto itm = itm_conv.convert(arrbl, selected ? 0 : -SCALED_EPSILON);
|
||||
try {
|
||||
auto itm = itm_conv.convert(arrbl, selected ? 0 : -SCALED_EPSILON);
|
||||
|
||||
auto &container_parent = printable ? task.printable :
|
||||
auto &container_parent = printable ? task.printable :
|
||||
task.unprintable;
|
||||
|
||||
auto &container = selected ?
|
||||
auto &container = selected ?
|
||||
container_parent.selected :
|
||||
container_parent.unselected;
|
||||
|
||||
container.emplace_back(std::move(itm));
|
||||
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
|
||||
@ -68,6 +75,21 @@ void prepare_fixed_unselected(ItemCont &items, int shift)
|
||||
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)
|
||||
@ -102,15 +124,17 @@ ArrangeTask<ArrItem>::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<int> 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);
|
||||
|
||||
@ -118,7 +142,7 @@ ArrangeTask<ArrItem>::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);
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,8 @@
|
||||
|
||||
#include "Arrange/Core/NFP/NFPArrangeItemTraits.hpp"
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
namespace Slic3r { namespace arr2 {
|
||||
|
||||
template<class ArrItem>
|
||||
@ -81,15 +83,20 @@ void extract(FillBedTask<ArrItem> &task,
|
||||
|
||||
auto collect_task_items = [&prototype_geometry_id, &task,
|
||||
&itm_conv](const Arrangeable &arrbl) {
|
||||
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));
|
||||
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));
|
||||
}
|
||||
} 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();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -3,6 +3,8 @@
|
||||
|
||||
#include "MultiplySelectionTask.hpp"
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
namespace Slic3r { namespace arr2 {
|
||||
|
||||
template<class ArrItem>
|
||||
@ -45,15 +47,20 @@ std::unique_ptr<MultiplySelectionTask<ArrItem>> MultiplySelectionTask<ArrItem>::
|
||||
|
||||
auto collect_task_items = [&prototype_geometry_id, &task,
|
||||
&itm_conv](const Arrangeable &arrbl) {
|
||||
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));
|
||||
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));
|
||||
}
|
||||
} 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();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,14 +1,11 @@
|
||||
#include "ModelArrange.hpp"
|
||||
|
||||
|
||||
#include <libslic3r/Arrange/SceneBuilder.hpp>
|
||||
#include <libslic3r/Arrange/Items/ArrangeItem.hpp>
|
||||
#include <libslic3r/Arrange/Tasks/MultiplySelectionTask.hpp>
|
||||
|
||||
#include <libslic3r/Model.hpp>
|
||||
#include <libslic3r/Geometry/ConvexHull.hpp>
|
||||
#include "Arrange/Core/ArrangeItemTraits.hpp"
|
||||
#include "Arrange/Items/ArrangeItem.hpp"
|
||||
|
||||
#include "MTUtils.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
@ -27,14 +24,10 @@ bool arrange_objects(Model &model,
|
||||
const arr2::ArrangeBed &bed,
|
||||
const arr2::ArrangeSettingsView &settings)
|
||||
{
|
||||
arr2::Scene scene{arr2::SceneBuilder{}
|
||||
.set_bed(bed)
|
||||
.set_arrange_settings(settings)
|
||||
.set_model(model)};
|
||||
|
||||
auto task = arr2::ArrangeTaskBase::create(arr2::Tasks::Arrange, scene);
|
||||
auto result = task->process();
|
||||
return result->apply_on(scene.model());
|
||||
return arrange(arr2::SceneBuilder{}
|
||||
.set_bed(bed)
|
||||
.set_arrange_settings(settings)
|
||||
.set_model(model));
|
||||
}
|
||||
|
||||
void duplicate_objects(Model &model,
|
||||
|
@ -760,7 +760,7 @@ TEST_CASE("First fit selection strategy", "[arrange2]")
|
||||
|
||||
THEN("The item should be left unpacked")
|
||||
{
|
||||
REQUIRE(std::all_of(items.begin(), items.end(), [](auto &itm) {
|
||||
REQUIRE(std::all_of(items.begin(), items.end(), [](const ArrItem &itm) {
|
||||
return !Slic3r::arr2::is_arranged(itm);
|
||||
}));
|
||||
}
|
||||
@ -878,7 +878,7 @@ TEST_CASE("First fit selection strategy", "[arrange2]")
|
||||
|
||||
THEN("all items should fit onto the beds from index 0 to 4")
|
||||
{
|
||||
REQUIRE(std::all_of(items.begin(), items.end(), [](auto &itm) {
|
||||
REQUIRE(std::all_of(items.begin(), items.end(), [](const ArrItem &itm) {
|
||||
auto bed_idx = Slic3r::arr2::get_bed_index(itm);
|
||||
return bed_idx >= 0 && bed_idx < Count / Capacity;
|
||||
}));
|
||||
|
@ -49,8 +49,8 @@ static Slic3r::Model get_example_model_with_random_cube_objects(size_t N = 0)
|
||||
for (size_t i = 0; i < cube_count; ++i) {
|
||||
ModelInstance *inst = new_object->add_instance();
|
||||
arr2::transform_instance(*inst,
|
||||
Vec2d{random_value(-200., 200.),
|
||||
random_value(-200., 200.)},
|
||||
Vec2d{random_value(-arr2::UnscaledCoordLimit / 10., arr2::UnscaledCoordLimit / 10.),
|
||||
random_value(-arr2::UnscaledCoordLimit / 10., arr2::UnscaledCoordLimit / 10.)},
|
||||
random_value(0., 2 * PI));
|
||||
}
|
||||
|
||||
@ -168,7 +168,8 @@ TEMPLATE_TEST_CASE("Writing arrange transformations into ModelInstance should be
|
||||
{
|
||||
auto [tx, ty, rot] = GENERATE(map(
|
||||
[](int i) {
|
||||
return std::make_tuple(-500. + i * 20., -500. + i * 20.,
|
||||
return std::make_tuple(-Slic3r::arr2::UnscaledCoordLimit / 2. + i * Slic3r::arr2::UnscaledCoordLimit / 100.,
|
||||
-Slic3r::arr2::UnscaledCoordLimit / 2. + i * Slic3r::arr2::UnscaledCoordLimit / 100.,
|
||||
-PI + i * (2 * PI / 100.));
|
||||
},
|
||||
range(0, 100)));
|
||||
|
@ -228,7 +228,7 @@ void init_print(std::vector<TriangleMesh> &&meshes, Slic3r::Print &print, Slic3r
|
||||
object->add_volume(std::move(t));
|
||||
object->add_instance();
|
||||
}
|
||||
arrange_objects(model, arr2::InfiniteBed{}, arr2::ArrangeSettings{}.set_distance_from_objects(min_object_distance(config)));
|
||||
arrange_objects(model, arr2::to_arrange_bed(get_bed_shape(config)), arr2::ArrangeSettings{}.set_distance_from_objects(min_object_distance(config)));
|
||||
model.center_instances_around_point({100, 100});
|
||||
for (ModelObject *mo : model.objects) {
|
||||
mo->ensure_on_bed();
|
||||
|
@ -42,8 +42,12 @@ SCENARIO("Model construction", "[Model]") {
|
||||
}
|
||||
}
|
||||
model_object->add_instance();
|
||||
arrange_objects(model, arr2::InfiniteBed{scaled(Vec2d(100, 100))}, arr2::ArrangeSettings{}.set_distance_from_objects(min_object_distance(config)));
|
||||
model_object->ensure_on_bed();
|
||||
arrange_objects(model,
|
||||
arr2::to_arrange_bed(get_bed_shape(config)),
|
||||
arr2::ArrangeSettings{}.set_distance_from_objects(
|
||||
min_object_distance(config)));
|
||||
|
||||
model_object->ensure_on_bed();
|
||||
print.auto_assign_extruders(model_object);
|
||||
THEN("Print works?") {
|
||||
print.set_status_silent();
|
||||
|
Loading…
x
Reference in New Issue
Block a user