Arrange onto segmented beds for XL

Backport to 2.5.1
This commit is contained in:
tamasmeszaros 2023-03-02 19:59:39 +01:00
parent 201ecd65eb
commit 651070cc0d
7 changed files with 176 additions and 27 deletions

View File

@ -611,11 +611,11 @@ template<class Fn> auto call_with_bed(const Points &bed, Fn &&fn)
auto parea = poly_area(bed);
if ((1.0 - parea / area(bb)) < 1e-3)
return fn(bb);
return fn(RectangleBed{bb});
else if (!std::isnan(circ.radius()))
return fn(circ);
else
return fn(Polygon(bed));
return fn(IrregularBed{ExPolygon(bed)});
}
}
@ -631,9 +631,7 @@ void arrange(ArrangePolygons & items,
const Points & bed,
const ArrangeParams & params)
{
call_with_bed(bed, [&](const auto &bin) {
arrange(items, excludes, bin, params);
});
arrange(items, excludes, to_arrange_bed(bed), params);
}
template<class BedT>
@ -673,5 +671,84 @@ template void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, c
template void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const Polygon &bed, const ArrangeParams &params);
template void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const InfiniteBed &bed, const ArrangeParams &params);
ArrangeBed to_arrange_bed(const Points &bedpts)
{
ArrangeBed ret;
call_with_bed(bedpts, [&](const auto &bed) {
ret = bed;
});
return ret;
}
void arrange(ArrangePolygons &items,
const ArrangePolygons &excludes,
const SegmentedRectangleBed &bed,
const ArrangeParams &params)
{
arrange(items, excludes, bed.bb, params);
if (! excludes.empty())
return;
auto it = std::max_element(items.begin(), items.end(),
[](auto &i1, auto &i2) {
return i1.bed_idx < i1.bed_idx;
});
size_t beds = 0;
if (it != items.end())
beds = it->bed_idx + 1;
std::vector<BoundingBox> pilebb(beds);
for (auto &itm : items) {
if (itm.bed_idx >= 0)
pilebb[itm.bed_idx].merge(get_extents(itm.transformed_poly()));
}
auto piecesz = unscaled(bed.bb).size();
piecesz.x() /= bed.segments.x();
piecesz.y() /= bed.segments.y();
for (size_t bedidx = 0; bedidx < beds; ++bedidx) {
BoundingBox bb;
auto pilesz = unscaled(pilebb[bedidx]).size();
bb.max.x() = scaled(std::ceil(pilesz.x() / piecesz.x()) * piecesz.x());
bb.max.y() = scaled(std::ceil(pilesz.y() / piecesz.y()) * piecesz.y());
switch (params.alignment) {
case Pivots::BottomLeft:
bb.translate((bed.bb.min - bb.min));
break;
case Pivots::TopRight:
bb.translate((bed.bb.max - bb.max));
break;
case Pivots::BottomRight: {
Point bedref{bed.bb.max.x(), bed.bb.min.y()};
Point bbref {bb.max.x(), bb.min.y()};
bb.translate((bedref - bbref));
break;
}
case Pivots::TopLeft: {
Point bedref{bed.bb.min.x(), bed.bb.max.y()};
Point bbref {bb.min.x(), bb.max.y()};
bb.translate((bedref - bbref));
break;
}
case Pivots::Center: {
bb.translate(bed.bb.center() - bb.center());
break;
}
}
Vec2crd d = bb.center() - pilebb[bedidx].center();
for (auto &itm : items)
if (itm.bed_idx == bedidx)
itm.translation += d;
}
}
} // namespace arr
} // namespace Slic3r

View File

@ -3,12 +3,25 @@
#include "ExPolygon.hpp"
#include <boost/variant.hpp>
#include <libslic3r/BoundingBox.hpp>
namespace Slic3r {
class BoundingBox;
namespace arrangement {
/// Representing an unbounded bed.
struct InfiniteBed {
Point center;
explicit InfiniteBed(const Point &p = {0, 0}): center{p} {}
};
struct RectangleBed {
BoundingBox bb;
};
/// A geometry abstraction for a circular print bed. Similarly to BoundingBox.
class CircleBed {
Point center_;
@ -22,12 +35,28 @@ public:
inline const Point& center() const { return center_; }
};
/// Representing an unbounded bed.
struct InfiniteBed {
Point center;
explicit InfiniteBed(const Point &p = {0, 0}): center{p} {}
struct SegmentedRectangleBed {
Vec<2, size_t> segments;
BoundingBox bb;
SegmentedRectangleBed (const BoundingBox &bb,
size_t segments_x,
size_t segments_y)
: segments{segments_x, segments_y}
, bb{bb}
{}
};
struct IrregularBed {
ExPolygon poly;
};
//enum BedType { Infinite, Rectangle, Circle, SegmentedRectangle, Irregular };
using ArrangeBed = boost::variant<InfiniteBed, RectangleBed, CircleBed, SegmentedRectangleBed, IrregularBed>;
ArrangeBed to_arrange_bed(const Points &bedpts);
/// A logical bed representing an object not being arranged. Either the arrange
/// has not yet successfully run on this ArrangePolygon or it could not fit the
/// object due to overly large size or invalid geometry.
@ -134,6 +163,24 @@ extern template void arrange(ArrangePolygons &items, const ArrangePolygons &excl
extern template void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const Polygon &bed, const ArrangeParams &params);
extern template void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const InfiniteBed &bed, const ArrangeParams &params);
inline void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const RectangleBed &bed, const ArrangeParams &params)
{
arrange(items, excludes, bed.bb, params);
}
inline void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const IrregularBed &bed, const ArrangeParams &params)
{
arrange(items, excludes, bed.poly.contour, params);
}
void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const SegmentedRectangleBed &bed, const ArrangeParams &params);
inline void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const ArrangeBed &bed, const ArrangeParams &params)
{
auto call_arrange = [&](const auto &realbed) { arrange(items, excludes, realbed, params); };
boost::apply_visitor(call_arrange, bed);
}
inline void arrange(ArrangePolygons &items, const Points &bed, const ArrangeParams &params = {}) { arrange(items, {}, bed, params); }
inline void arrange(ArrangePolygons &items, const BoundingBox &bed, const ArrangeParams &params = {}) { arrange(items, {}, bed, params); }
inline void arrange(ArrangePolygons &items, const CircleBed &bed, const ArrangeParams &params = {}) { arrange(items, {}, bed, params); }

View File

@ -53,7 +53,7 @@ public:
PointClass size() const;
double radius() const;
void translate(coordf_t x, coordf_t y) { assert(this->defined); PointClass v(x, y); this->min += v; this->max += v; }
void translate(const Vec2d &v) { this->min += v; this->max += v; }
void translate(const PointClass &v) { this->min += v; this->max += v; }
void offset(coordf_t delta);
BoundingBoxBase<PointClass> inflated(coordf_t delta) const throw() { BoundingBoxBase<PointClass> out(*this); out.offset(delta); return out; }
PointClass center() const;
@ -221,10 +221,17 @@ inline bool empty(const BoundingBox3Base<VT> &bb)
return ! bb.defined || bb.min(0) >= bb.max(0) || bb.min(1) >= bb.max(1) || bb.min(2) >= bb.max(2);
}
inline BoundingBox scaled(const BoundingBoxf &bb) { return {scaled(bb.min), scaled(bb.max)}; }
inline BoundingBox3 scaled(const BoundingBoxf3 &bb) { return {scaled(bb.min), scaled(bb.max)}; }
inline BoundingBoxf unscaled(const BoundingBox &bb) { return {unscaled(bb.min), unscaled(bb.max)}; }
inline BoundingBoxf3 unscaled(const BoundingBox3 &bb) { return {unscaled(bb.min), unscaled(bb.max)}; }
template<class T = coord_t>
BoundingBoxBase<Vec<2, T>> scaled(const BoundingBoxf &bb) { return {scaled<T>(bb.min), scaled<T>(bb.max)}; }
template<class T = coord_t>
BoundingBox3Base<Vec<3, T>> scaled(const BoundingBoxf3 &bb) { return {scaled<T>(bb.min), scaled<T>(bb.max)}; }
template<class T = double>
BoundingBoxBase<Vec<2, T>> unscaled(const BoundingBox &bb) { return {unscaled<T>(bb.min), unscaled<T>(bb.max)}; }
template<class T = double>
BoundingBox3Base<Vec<3, T>> unscaled(const BoundingBox3 &bb) { return {unscaled<T>(bb.min), unscaled<T>(bb.max)}; }
template<class Tout, class Tin>
auto cast(const BoundingBoxBase<Tin> &b)

View File

@ -4660,6 +4660,15 @@ Points get_bed_shape(const DynamicPrintConfig &config)
return to_points(bed_shape_opt->values);
}
void get_bed_shape(const DynamicPrintConfig &cfg, arrangement::ArrangeBed &out)
{
if (is_XL_printer(cfg)) {
out = arrangement::SegmentedRectangleBed{get_extents(get_bed_shape(cfg)), 4, 4};
} else {
out = arrangement::to_arrange_bed(get_bed_shape(cfg));
}
}
Points get_bed_shape(const PrintConfig &cfg)
{
return to_points(cfg.bed_shape.values);
@ -4667,6 +4676,19 @@ Points get_bed_shape(const PrintConfig &cfg)
Points get_bed_shape(const SLAPrinterConfig &cfg) { return to_points(cfg.bed_shape.values); }
bool is_XL_printer(const DynamicPrintConfig &cfg)
{
static constexpr const char *ALIGN_ONLY_FOR = "XL";
bool ret = false;
auto *printer_model = cfg.opt<ConfigOptionString>("printer_model");
if (printer_model)
ret = boost::algorithm::contains(printer_model->value, ALIGN_ONLY_FOR);
return ret;
}
} // namespace Slic3r
#include <cereal/types/polymorphic.hpp>

View File

@ -18,6 +18,7 @@
#include "libslic3r.h"
#include "Config.hpp"
#include "libslic3r/Arrange.hpp"
#include <boost/preprocessor/facilities/empty.hpp>
#include <boost/preprocessor/punctuation/comma_if.hpp>
@ -1084,10 +1085,13 @@ private:
static PrintAndCLIConfigDef s_def;
};
bool is_XL_printer(const DynamicPrintConfig &cfg);
Points get_bed_shape(const DynamicPrintConfig &cfg);
Points get_bed_shape(const PrintConfig &cfg);
Points get_bed_shape(const SLAPrinterConfig &cfg);
void get_bed_shape(const DynamicPrintConfig &cfg, arrangement::ArrangeBed &out);
// ModelConfig is a wrapper around DynamicPrintConfig with an addition of a timestamp.
// Each change of ModelConfig is tracked by assigning a new timestamp from a global counter.
// The counter is used for faster synchronization of the background slicing thread

View File

@ -988,16 +988,7 @@ PrinterTechnology GLCanvas3D::current_printer_technology() const
bool GLCanvas3D::is_arrange_alignment_enabled() const
{
static constexpr const char *ALIGN_ONLY_FOR = "XL";
bool ret = false;
auto *printer_model = m_config->opt<ConfigOptionString>("printer_model");
if (printer_model)
ret = boost::algorithm::contains(printer_model->value, ALIGN_ONLY_FOR);
return ret;
return m_config ? is_XL_printer(*m_config) : false;
}
GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D &bed)

View File

@ -185,7 +185,8 @@ void ArrangeJob::process()
arrangement::ArrangeParams params = get_arrange_params(m_plater);
auto count = unsigned(m_selected.size() + m_unprintable.size());
Points bedpts = get_bed_shape(*m_plater->config());
arrangement::ArrangeBed bed;
get_bed_shape(*m_plater->config(), bed);
params.stopcondition = [this]() { return was_canceled(); };
@ -194,13 +195,13 @@ void ArrangeJob::process()
if (st > 0) update_status(int(count - st), arrangestr);
};
arrangement::arrange(m_selected, m_unselected, bedpts, params);
arrangement::arrange(m_selected, m_unselected, bed, params);
params.progressind = [this, count](unsigned st) {
if (st > 0) update_status(int(count - st), arrangestr);
};
arrangement::arrange(m_unprintable, {}, bedpts, params);
arrangement::arrange(m_unprintable, {}, bed, params);
// finalize just here.
update_status(int(count),