Priority for wipe tower, Increased safety distance from bed edges.
* WipeTowerInfo class extended in plater (WipeTower) instead of GLCanvas3D * Bed origin support in ModelInstance and WipeTower
This commit is contained in:
parent
5446167c11
commit
44801f4429
@ -68,7 +68,7 @@ class _Item {
|
|||||||
BBCache(): valid(false) {}
|
BBCache(): valid(false) {}
|
||||||
} bb_cache_;
|
} bb_cache_;
|
||||||
|
|
||||||
int binid_{BIN_ID_UNSET};
|
int binid_{BIN_ID_UNSET}, priority_{0};
|
||||||
bool fixed_{false};
|
bool fixed_{false};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -130,8 +130,12 @@ public:
|
|||||||
|
|
||||||
inline bool isFixed() const noexcept { return fixed_; }
|
inline bool isFixed() const noexcept { return fixed_; }
|
||||||
inline void markAsFixed(bool fixed = true) { fixed_ = fixed; }
|
inline void markAsFixed(bool fixed = true) { fixed_ = fixed; }
|
||||||
|
|
||||||
inline void binId(int idx) { binid_ = idx; }
|
inline void binId(int idx) { binid_ = idx; }
|
||||||
inline int binId() const noexcept { return binid_; }
|
inline int binId() const noexcept { return binid_; }
|
||||||
|
|
||||||
|
inline void priority(int p) { priority_ = p; }
|
||||||
|
inline int priority() const noexcept { return priority_; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Convert the polygon to string representation. The format depends
|
* @brief Convert the polygon to string representation. The format depends
|
||||||
|
@ -62,9 +62,10 @@ public:
|
|||||||
placers.back().configure(pconfig);
|
placers.back().configure(pconfig);
|
||||||
placers.back().preload(ig);
|
placers.back().preload(ig);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto sortfunc = [](Item& i1, Item& i2) {
|
auto sortfunc = [](Item& i1, Item& i2) {
|
||||||
return i1.area() > i2.area();
|
int p1 = i1.priority(), p2 = i2.priority();
|
||||||
|
return p1 == p2 ? i1.area() > i2.area() : p1 > p2;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::sort(store_.begin(), store_.end(), sortfunc);
|
std::sort(store_.begin(), store_.end(), sortfunc);
|
||||||
|
@ -584,6 +584,7 @@ void arrange(ArrangePolygons & arrangables,
|
|||||||
outp.back().rotation(rotation);
|
outp.back().rotation(rotation);
|
||||||
outp.back().translation({offs.x(), offs.y()});
|
outp.back().translation({offs.x(), offs.y()});
|
||||||
outp.back().binId(arrpoly.bed_idx);
|
outp.back().binId(arrpoly.bed_idx);
|
||||||
|
outp.back().priority(arrpoly.priority);
|
||||||
};
|
};
|
||||||
|
|
||||||
for (ArrangePolygon &arrangeable : arrangables)
|
for (ArrangePolygon &arrangeable : arrangables)
|
||||||
@ -595,7 +596,7 @@ void arrange(ArrangePolygons & arrangables,
|
|||||||
for (Item &itm : fixeditems) itm.inflate(-2 * SCALED_EPSILON);
|
for (Item &itm : fixeditems) itm.inflate(-2 * SCALED_EPSILON);
|
||||||
|
|
||||||
// Integer ceiling the min distance from the bed perimeters
|
// Integer ceiling the min distance from the bed perimeters
|
||||||
coord_t md = min_obj_dist - SCALED_EPSILON;
|
coord_t md = min_obj_dist - 2 * scaled(0.1 + EPSILON);
|
||||||
md = (md % 2) ? md / 2 + 1 : md / 2;
|
md = (md % 2) ? md / 2 + 1 : md / 2;
|
||||||
|
|
||||||
auto &cfn = stopcondition;
|
auto &cfn = stopcondition;
|
||||||
|
@ -140,15 +140,18 @@ static const constexpr int UNARRANGED = -1;
|
|||||||
/// polygon belongs: UNARRANGED means no place for the polygon
|
/// polygon belongs: UNARRANGED means no place for the polygon
|
||||||
/// (also the initial state before arrange), 0..N means the index of the bed.
|
/// (also the initial state before arrange), 0..N means the index of the bed.
|
||||||
/// Zero is the physical bed, larger than zero means a virtual bed.
|
/// Zero is the physical bed, larger than zero means a virtual bed.
|
||||||
struct ArrangePolygon {
|
struct ArrangePolygon {
|
||||||
const ExPolygon poly; /// The 2D silhouette to be arranged
|
ExPolygon poly; /// The 2D silhouette to be arranged
|
||||||
Vec2crd translation{0, 0}; /// The translation of the poly
|
Vec2crd translation{0, 0}; /// The translation of the poly
|
||||||
double rotation{0.0}; /// The rotation of the poly in radians
|
double rotation{0.0}; /// The rotation of the poly in radians
|
||||||
int bed_idx{UNARRANGED}; /// To which logical bed does poly belong...
|
int bed_idx{UNARRANGED}; /// To which logical bed does poly belong...
|
||||||
|
int priority{0};
|
||||||
|
|
||||||
ArrangePolygon(ExPolygon p, const Vec2crd &tr = {}, double rot = 0.0)
|
/// Optional setter function which can store arbitrary data in its closure
|
||||||
: poly{std::move(p)}, translation{tr}, rotation{rot}
|
std::function<void(const ArrangePolygon&)> setter = nullptr;
|
||||||
{}
|
|
||||||
|
/// Helper function to call the setter with the arrange data arguments
|
||||||
|
void apply() const { if (setter) setter(*this); }
|
||||||
};
|
};
|
||||||
|
|
||||||
using ArrangePolygons = std::vector<ArrangePolygon>;
|
using ArrangePolygons = std::vector<ArrangePolygon>;
|
||||||
|
@ -387,20 +387,24 @@ bool Model::arrange_objects(coordf_t dist, const BoundingBoxf* bb)
|
|||||||
}
|
}
|
||||||
|
|
||||||
arrangement::BedShapeHint bedhint;
|
arrangement::BedShapeHint bedhint;
|
||||||
|
coord_t bedwidth = 0;
|
||||||
if (bb)
|
|
||||||
|
if (bb) {
|
||||||
|
bedwidth = scaled(bb->size().x());
|
||||||
bedhint = arrangement::BedShapeHint(
|
bedhint = arrangement::BedShapeHint(
|
||||||
BoundingBox(scaled(bb->min), scaled(bb->max)));
|
BoundingBox(scaled(bb->min), scaled(bb->max)));
|
||||||
|
}
|
||||||
|
|
||||||
arrangement::arrange(input, scaled(dist), bedhint);
|
arrangement::arrange(input, scaled(dist), bedhint);
|
||||||
|
|
||||||
bool ret = true;
|
bool ret = true;
|
||||||
|
coord_t stride = bedwidth + bedwidth / 5;
|
||||||
|
|
||||||
for(size_t i = 0; i < input.size(); ++i) {
|
for(size_t i = 0; i < input.size(); ++i) {
|
||||||
if (input[i].bed_idx == 0) { // no logical beds are allowed
|
if (input[i].bed_idx != 0) ret = false;
|
||||||
instances[i]->apply_arrange_result(input[i].translation,
|
if (input[i].bed_idx >= 0)
|
||||||
input[i].rotation);
|
instances[i]->apply_arrange_result(input[i],
|
||||||
} else ret = false;
|
{input[i].bed_idx * stride, 0});
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -1822,22 +1826,24 @@ arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const
|
|||||||
|
|
||||||
// this may happen for malformed models, see:
|
// this may happen for malformed models, see:
|
||||||
// https://github.com/prusa3d/PrusaSlicer/issues/2209
|
// https://github.com/prusa3d/PrusaSlicer/issues/2209
|
||||||
if (p.points.empty()) return {{}};
|
if (p.points.empty()) return {};
|
||||||
|
|
||||||
Polygons pp{p};
|
Polygons pp{p};
|
||||||
pp = p.simplify(scaled<double>(SIMPLIFY_TOLERANCE_MM));
|
pp = p.simplify(scaled<double>(SIMPLIFY_TOLERANCE_MM));
|
||||||
if (!pp.empty()) p = pp.front();
|
if (!pp.empty()) p = pp.front();
|
||||||
m_arrange_cache.poly.contour = std::move(p);
|
m_arrange_cache.poly.contour = std::move(p);
|
||||||
|
m_arrange_cache.bed_origin = {0, 0};
|
||||||
|
m_arrange_cache.bed_idx = arrangement::UNARRANGED;
|
||||||
m_arrange_cache.valid = true;
|
m_arrange_cache.valid = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
arrangement::ArrangePolygon ret{m_arrange_cache.poly,
|
arrangement::ArrangePolygon ret;
|
||||||
Vec2crd{scaled(get_offset(X)),
|
ret.poly = m_arrange_cache.poly;
|
||||||
scaled(get_offset(Y))},
|
ret.translation = Vec2crd{scaled(get_offset(X)), scaled(get_offset(Y))} -
|
||||||
get_rotation(Z)};
|
m_arrange_cache.bed_origin;
|
||||||
|
ret.rotation = get_rotation(Z);
|
||||||
ret.bed_idx = m_arrange_cache.bed_idx;
|
ret.bed_idx = m_arrange_cache.bed_idx;
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -558,13 +558,15 @@ public:
|
|||||||
arrangement::ArrangePolygon get_arrange_polygon() const;
|
arrangement::ArrangePolygon get_arrange_polygon() const;
|
||||||
|
|
||||||
// Apply the arrange result on the ModelInstance
|
// Apply the arrange result on the ModelInstance
|
||||||
void apply_arrange_result(Vec2crd offs, double rot_rads, int bed_idx = 0)
|
void apply_arrange_result(const arrangement::ArrangePolygon& ap,
|
||||||
|
const Vec2crd& bed_origin = {0, 0})
|
||||||
{
|
{
|
||||||
// write the transformation data into the model instance
|
// write the transformation data into the model instance
|
||||||
set_rotation(Z, rot_rads);
|
set_rotation(Z, ap.rotation);
|
||||||
set_offset(X, unscale<double>(offs(X)));
|
set_offset(X, unscale<double>(ap.translation(X) + bed_origin.x()));
|
||||||
set_offset(Y, unscale<double>(offs(Y)));
|
set_offset(Y, unscale<double>(ap.translation(Y) + bed_origin.y()));
|
||||||
m_arrange_cache.bed_idx = bed_idx;
|
m_arrange_cache.bed_origin = bed_origin;
|
||||||
|
m_arrange_cache.bed_idx = ap.bed_idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@ -599,8 +601,9 @@ private:
|
|||||||
// Warning! This object is not guarded against concurrency.
|
// Warning! This object is not guarded against concurrency.
|
||||||
mutable struct ArrangeCache {
|
mutable struct ArrangeCache {
|
||||||
bool valid = false;
|
bool valid = false;
|
||||||
int bed_idx { arrangement::UNARRANGED };
|
Vec2crd bed_origin {0, 0};
|
||||||
ExPolygon poly;
|
ExPolygon poly;
|
||||||
|
int bed_idx = arrangement::UNARRANGED;
|
||||||
} m_arrange_cache;
|
} m_arrange_cache;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -5739,11 +5739,8 @@ const SLAPrint* GLCanvas3D::sla_print() const
|
|||||||
return (m_process == nullptr) ? nullptr : m_process->sla_print();
|
return (m_process == nullptr) ? nullptr : m_process->sla_print();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLCanvas3D::WipeTowerInfo::apply_arrange_result(Vec2crd off, double rotation_rads)
|
void GLCanvas3D::WipeTowerInfo::apply_wipe_tower() const
|
||||||
{
|
{
|
||||||
Vec2d offset = unscaled(off);
|
|
||||||
m_pos = offset;
|
|
||||||
m_rotation = rotation_rads;
|
|
||||||
DynamicPrintConfig cfg;
|
DynamicPrintConfig cfg;
|
||||||
cfg.opt<ConfigOptionFloat>("wipe_tower_x", true)->value = m_pos(X);
|
cfg.opt<ConfigOptionFloat>("wipe_tower_x", true)->value = m_pos(X);
|
||||||
cfg.opt<ConfigOptionFloat>("wipe_tower_y", true)->value = m_pos(Y);
|
cfg.opt<ConfigOptionFloat>("wipe_tower_y", true)->value = m_pos(Y);
|
||||||
|
@ -613,9 +613,10 @@ public:
|
|||||||
int get_first_hover_volume_idx() const { return m_hover_volume_idxs.empty() ? -1 : m_hover_volume_idxs.front(); }
|
int get_first_hover_volume_idx() const { return m_hover_volume_idxs.empty() ? -1 : m_hover_volume_idxs.front(); }
|
||||||
|
|
||||||
class WipeTowerInfo {
|
class WipeTowerInfo {
|
||||||
|
protected:
|
||||||
Vec2d m_pos = {std::nan(""), std::nan("")};
|
Vec2d m_pos = {std::nan(""), std::nan("")};
|
||||||
Vec2d m_bb_size;
|
Vec2d m_bb_size = {0., 0.};
|
||||||
double m_rotation;
|
double m_rotation = 0.;
|
||||||
friend class GLCanvas3D;
|
friend class GLCanvas3D;
|
||||||
public:
|
public:
|
||||||
|
|
||||||
@ -623,22 +624,12 @@ public:
|
|||||||
{
|
{
|
||||||
return !std::isnan(m_pos.x()) && !std::isnan(m_pos.y());
|
return !std::isnan(m_pos.x()) && !std::isnan(m_pos.y());
|
||||||
}
|
}
|
||||||
|
|
||||||
void apply_arrange_result(Vec2crd offset, double rotation_rads);
|
|
||||||
|
|
||||||
arrangement::ArrangePolygon get_arrange_polygon() const
|
inline const Vec2d& pos() const { return m_pos; }
|
||||||
{
|
inline double rotation() const { return m_rotation; }
|
||||||
Polygon p({
|
inline const Vec2d bb_size() const { return m_bb_size; }
|
||||||
{coord_t(0), coord_t(0)},
|
|
||||||
{scaled(m_bb_size(X)), coord_t(0)},
|
void apply_wipe_tower() const;
|
||||||
{scaled(m_bb_size)},
|
|
||||||
{coord_t(0), scaled(m_bb_size(Y))},
|
|
||||||
{coord_t(0), coord_t(0)},
|
|
||||||
});
|
|
||||||
|
|
||||||
ExPolygon ep; ep.contour = std::move(p);
|
|
||||||
return {ep, scaled(m_pos), m_rotation};
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
WipeTowerInfo get_wipe_tower_info() const;
|
WipeTowerInfo get_wipe_tower_info() const;
|
||||||
|
@ -1262,6 +1262,56 @@ struct Plater::priv
|
|||||||
BackgroundSlicingProcess background_process;
|
BackgroundSlicingProcess background_process;
|
||||||
bool suppressed_backround_processing_update { false };
|
bool suppressed_backround_processing_update { false };
|
||||||
|
|
||||||
|
// Cache the wti info
|
||||||
|
class WipeTower: public GLCanvas3D::WipeTowerInfo {
|
||||||
|
Vec2d m_bed_origin = {0., 0.};
|
||||||
|
int m_bed_idx = arrangement::UNARRANGED;
|
||||||
|
friend priv;
|
||||||
|
public:
|
||||||
|
|
||||||
|
void apply_arrange_result(const arrangement::ArrangePolygon& ap,
|
||||||
|
const Vec2crd& bedc) {
|
||||||
|
m_bed_origin = unscaled(bedc);
|
||||||
|
m_pos = unscaled(ap.translation) + m_bed_origin;
|
||||||
|
m_rotation = ap.rotation;
|
||||||
|
m_bed_idx = ap.bed_idx;
|
||||||
|
apply_wipe_tower();
|
||||||
|
}
|
||||||
|
|
||||||
|
arrangement::ArrangePolygon get_arrange_polygon() const
|
||||||
|
{
|
||||||
|
Polygon p({
|
||||||
|
{coord_t(0), coord_t(0)},
|
||||||
|
{scaled(m_bb_size(X)), coord_t(0)},
|
||||||
|
{scaled(m_bb_size)},
|
||||||
|
{coord_t(0), scaled(m_bb_size(Y))},
|
||||||
|
{coord_t(0), coord_t(0)},
|
||||||
|
});
|
||||||
|
|
||||||
|
arrangement::ArrangePolygon ret;
|
||||||
|
ret.poly.contour = std::move(p);
|
||||||
|
ret.translation = scaled(m_pos) - scaled(m_bed_origin);
|
||||||
|
ret.rotation = m_rotation;
|
||||||
|
ret.bed_idx = m_bed_idx;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For future use
|
||||||
|
int bed_index() const { return m_bed_idx; }
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
WipeTower m_wipetower;
|
||||||
|
|
||||||
|
public:
|
||||||
|
WipeTower& wipe_tower() {
|
||||||
|
auto wti = view3D->get_canvas3d()->get_wipe_tower_info();
|
||||||
|
m_wipetower.m_pos = wti.pos();
|
||||||
|
m_wipetower.m_rotation = wti.rotation();
|
||||||
|
m_wipetower.m_bb_size = wti.bb_size();
|
||||||
|
return m_wipetower;
|
||||||
|
}
|
||||||
|
|
||||||
// A class to handle UI jobs like arranging and optimizing rotation.
|
// A class to handle UI jobs like arranging and optimizing rotation.
|
||||||
// These are not instant jobs, the user has to be informed about their
|
// These are not instant jobs, the user has to be informed about their
|
||||||
// state in the status progress indicator. On the other hand they are
|
// state in the status progress indicator. On the other hand they are
|
||||||
@ -1410,40 +1460,20 @@ struct Plater::priv
|
|||||||
|
|
||||||
class ArrangeJob : public Job
|
class ArrangeJob : public Job
|
||||||
{
|
{
|
||||||
|
using ArrangePolygon = arrangement::ArrangePolygon;
|
||||||
|
using ArrangePolygons = arrangement::ArrangePolygons;
|
||||||
|
|
||||||
// The gap between logical beds in the x axis expressed in ratio of
|
// The gap between logical beds in the x axis expressed in ratio of
|
||||||
// the current bed width.
|
// the current bed width.
|
||||||
static const constexpr double LOGICAL_BED_GAP = 1. / 5.;
|
static const constexpr double LOGICAL_BED_GAP = 1. / 5.;
|
||||||
|
static const constexpr int UNARRANGED = arrangement::UNARRANGED;
|
||||||
|
|
||||||
// Cache the wti info
|
ArrangePolygons m_selected, m_unselected;
|
||||||
GLCanvas3D::WipeTowerInfo m_wti;
|
|
||||||
|
|
||||||
// Cache the selected instances needed to write back the arrange
|
|
||||||
// result. The order of instances is the same as the arrange polys
|
|
||||||
struct IndexedArrangePolys {
|
|
||||||
ModelInstancePtrs insts;
|
|
||||||
arrangement::ArrangePolygons polys;
|
|
||||||
|
|
||||||
void reserve(size_t cap) { insts.reserve(cap); polys.reserve(cap); }
|
|
||||||
void clear() { insts.clear(); polys.clear(); }
|
|
||||||
|
|
||||||
void emplace_back(ModelInstance *inst) {
|
|
||||||
insts.emplace_back(inst);
|
|
||||||
polys.emplace_back(inst->get_arrange_polygon());
|
|
||||||
}
|
|
||||||
|
|
||||||
void swap(IndexedArrangePolys &pp) {
|
|
||||||
insts.swap(pp.insts); polys.swap(pp.polys);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
IndexedArrangePolys m_selected, m_unselected;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
void prepare() override
|
void prepare() override
|
||||||
{
|
{
|
||||||
m_wti = plater().view3D->get_canvas3d()->get_wipe_tower_info();
|
|
||||||
|
|
||||||
// Get the selection map
|
// Get the selection map
|
||||||
Selection& sel = plater().get_selection();
|
Selection& sel = plater().get_selection();
|
||||||
const Selection::ObjectIdxsToInstanceIdxsMap &selmap =
|
const Selection::ObjectIdxsToInstanceIdxsMap &selmap =
|
||||||
@ -1458,59 +1488,57 @@ struct Plater::priv
|
|||||||
m_selected.reserve(count + 1 /* for optional wti */);
|
m_selected.reserve(count + 1 /* for optional wti */);
|
||||||
m_unselected.reserve(count + 1 /* for optional wti */);
|
m_unselected.reserve(count + 1 /* for optional wti */);
|
||||||
|
|
||||||
// Go through the objects and check if inside the selection
|
|
||||||
for (size_t oidx = 0; oidx < model.objects.size(); ++oidx) {
|
|
||||||
auto oit = selmap.find(int(oidx));
|
|
||||||
|
|
||||||
if (oit != selmap.end()) { // Object is selected
|
|
||||||
auto &iids = oit->second;
|
|
||||||
|
|
||||||
// Go through instances and check if inside selection
|
|
||||||
size_t instcnt = model.objects[oidx]->instances.size();
|
|
||||||
for (size_t iidx = 0; iidx < instcnt; ++iidx) {
|
|
||||||
auto instit = iids.find(iidx);
|
|
||||||
ModelInstance *oi = model.objects[oidx]
|
|
||||||
->instances[iidx];
|
|
||||||
|
|
||||||
// Instance is selected
|
|
||||||
instit != iids.end() ?
|
|
||||||
m_selected.emplace_back(oi) :
|
|
||||||
m_unselected.emplace_back(oi);
|
|
||||||
}
|
|
||||||
} else // object not selected, all instances are unselected
|
|
||||||
for (ModelInstance *oi : model.objects[oidx]->instances)
|
|
||||||
m_unselected.emplace_back(oi);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_wti)
|
|
||||||
sel.is_wipe_tower() ?
|
|
||||||
m_selected.polys.emplace_back(m_wti.get_arrange_polygon()) :
|
|
||||||
m_unselected.polys.emplace_back(m_wti.get_arrange_polygon());
|
|
||||||
|
|
||||||
// If the selection is completely empty, consider all items as the
|
|
||||||
// selection
|
|
||||||
if (m_selected.insts.empty() && m_selected.polys.empty())
|
|
||||||
m_selected.swap(m_unselected);
|
|
||||||
|
|
||||||
// Stride between logical beds
|
// Stride between logical beds
|
||||||
double bedwidth = plater().bed_shape_bb().size().x();
|
double bedwidth = plater().bed_shape_bb().size().x();
|
||||||
coord_t stride = scaled((1. + LOGICAL_BED_GAP) * bedwidth);
|
coord_t stride = scaled((1. + LOGICAL_BED_GAP) * bedwidth);
|
||||||
|
|
||||||
for (arrangement::ArrangePolygon &ap : m_selected.polys)
|
// Go through the objects and check if inside the selection
|
||||||
if (ap.bed_idx > 0) ap.translation.x() -= ap.bed_idx * stride;
|
for (size_t oidx = 0; oidx < model.objects.size(); ++oidx) {
|
||||||
|
auto oit = selmap.find(int(oidx));
|
||||||
|
ModelObject *mo = model.objects[oidx];
|
||||||
|
|
||||||
|
std::vector<bool> inst_sel(mo->instances.size(), false);
|
||||||
|
|
||||||
|
if (oit != selmap.end())
|
||||||
|
for (auto inst_id : oit->second) inst_sel[inst_id] = true;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < inst_sel.size(); ++i) {
|
||||||
|
ModelInstance *mi = mo->instances[i];
|
||||||
|
ArrangePolygon ap = mi->get_arrange_polygon();
|
||||||
|
ap.priority = 0;
|
||||||
|
ap.setter = [mi, stride](const ArrangePolygon &p) {
|
||||||
|
if (p.bed_idx != UNARRANGED)
|
||||||
|
mi->apply_arrange_result(p, {p.bed_idx * stride, 0});
|
||||||
|
};
|
||||||
|
|
||||||
|
inst_sel[i] ?
|
||||||
|
m_selected.emplace_back(std::move(ap)) :
|
||||||
|
m_unselected.emplace_back(std::move(ap));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (arrangement::ArrangePolygon &ap : m_unselected.polys)
|
auto& wti = plater().wipe_tower();
|
||||||
if (ap.bed_idx > 0) ap.translation.x() -= ap.bed_idx * stride;
|
if (wti) {
|
||||||
|
ArrangePolygon ap = wti.get_arrange_polygon();
|
||||||
|
ap.setter = [&wti, stride](const ArrangePolygon &p) {
|
||||||
|
if (p.bed_idx != UNARRANGED)
|
||||||
|
wti.apply_arrange_result(p, {p.bed_idx * stride, 0});
|
||||||
|
};
|
||||||
|
|
||||||
|
ap.priority = 1;
|
||||||
|
sel.is_wipe_tower() ?
|
||||||
|
m_selected.emplace_back(std::move(ap)) :
|
||||||
|
m_unselected.emplace_back(std::move(ap));
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the selection was empty arrange everything
|
||||||
|
if (m_selected.empty()) m_selected.swap(m_unselected);
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using Job::Job;
|
using Job::Job;
|
||||||
|
|
||||||
|
int status_range() const override { return int(m_selected.size()); }
|
||||||
int status_range() const override
|
|
||||||
{
|
|
||||||
return int(m_selected.polys.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
void process() override;
|
void process() override;
|
||||||
|
|
||||||
@ -1521,30 +1549,8 @@ struct Plater::priv
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stride between logical beds
|
// Apply the arrange result to all selected objects
|
||||||
double bedwidth = plater().bed_shape_bb().size().x();
|
for (ArrangePolygon &ap : m_selected) ap.apply();
|
||||||
coord_t stride = scaled((1. + LOGICAL_BED_GAP) * bedwidth);
|
|
||||||
|
|
||||||
for(size_t i = 0; i < m_selected.insts.size(); ++i) {
|
|
||||||
if (m_selected.polys[i].bed_idx != arrangement::UNARRANGED) {
|
|
||||||
Vec2crd offs = m_selected.polys[i].translation;
|
|
||||||
double rot = m_selected.polys[i].rotation;
|
|
||||||
int bdidx = m_selected.polys[i].bed_idx;
|
|
||||||
offs.x() += bdidx * stride;
|
|
||||||
m_selected.insts[i]->apply_arrange_result(offs, rot, bdidx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle the wipe tower
|
|
||||||
if (m_wti && m_selected.polys.size() > m_selected.insts.size()) {
|
|
||||||
auto &wtipoly = m_selected.polys.back();
|
|
||||||
if (wtipoly.bed_idx != arrangement::UNARRANGED) {
|
|
||||||
Vec2crd o = wtipoly.translation;
|
|
||||||
double r = wtipoly.rotation;
|
|
||||||
o.x() += wtipoly.bed_idx * stride;
|
|
||||||
m_wti.apply_arrange_result(o, r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call original finalize (will update the scene)
|
// Call original finalize (will update the scene)
|
||||||
Job::finalize();
|
Job::finalize();
|
||||||
@ -2531,7 +2537,8 @@ arrangement::BedShapeHint Plater::priv::get_bed_shape_hint() const {
|
|||||||
return arrangement::BedShapeHint(bedpoly);
|
return arrangement::BedShapeHint(bedpoly);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Plater::priv::find_new_position(const ModelInstancePtrs &instances, coord_t min_d)
|
void Plater::priv::find_new_position(const ModelInstancePtrs &instances,
|
||||||
|
coord_t min_d)
|
||||||
{
|
{
|
||||||
arrangement::ArrangePolygons movable, fixed;
|
arrangement::ArrangePolygons movable, fixed;
|
||||||
|
|
||||||
@ -2546,15 +2553,14 @@ void Plater::priv::find_new_position(const ModelInstancePtrs &instances, coord_t
|
|||||||
movable.emplace_back(std::move(arrpoly));
|
movable.emplace_back(std::move(arrpoly));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto wti = view3D->get_canvas3d()->get_wipe_tower_info();
|
if (wipe_tower())
|
||||||
if (wti) fixed.emplace_back(wti.get_arrange_polygon());
|
fixed.emplace_back(m_wipetower.get_arrange_polygon());
|
||||||
|
|
||||||
arrangement::arrange(movable, fixed, min_d, get_bed_shape_hint());
|
arrangement::arrange(movable, fixed, min_d, get_bed_shape_hint());
|
||||||
|
|
||||||
for (size_t i = 0; i < instances.size(); ++i)
|
for (size_t i = 0; i < instances.size(); ++i)
|
||||||
if (movable[i].bed_idx == 0)
|
if (movable[i].bed_idx == 0)
|
||||||
instances[i]->apply_arrange_result(movable[i].translation,
|
instances[i]->apply_arrange_result(movable[i]);
|
||||||
movable[i].rotation);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Plater::priv::ArrangeJob::process() {
|
void Plater::priv::ArrangeJob::process() {
|
||||||
@ -2567,16 +2573,15 @@ void Plater::priv::ArrangeJob::process() {
|
|||||||
dist = PrintConfig::min_object_distance(plater().config);
|
dist = PrintConfig::min_object_distance(plater().config);
|
||||||
}
|
}
|
||||||
|
|
||||||
coord_t min_obj_distance = scaled(dist);
|
coord_t min_d = scaled(dist);
|
||||||
auto count = unsigned(m_selected.polys.size());
|
auto count = unsigned(m_selected.size());
|
||||||
arrangement::BedShapeHint bedshape = plater().get_bed_shape_hint();
|
arrangement::BedShapeHint bedshape = plater().get_bed_shape_hint();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
arrangement::arrange(m_selected.polys, m_unselected.polys,
|
arrangement::arrange(m_selected, m_unselected, min_d, bedshape,
|
||||||
min_obj_distance,
|
|
||||||
bedshape,
|
|
||||||
[this, count](unsigned st) {
|
[this, count](unsigned st) {
|
||||||
if (st > 0) // will not finalize after last one
|
if (st >
|
||||||
|
0) // will not finalize after last one
|
||||||
update_status(count - st, arrangestr);
|
update_status(count - st, arrangestr);
|
||||||
},
|
},
|
||||||
[this]() { return was_canceled(); });
|
[this]() { return was_canceled(); });
|
||||||
|
Loading…
Reference in New Issue
Block a user