Arrange cache in ModeInstance and logical bed remembered.

This commit is contained in:
tamasmeszaros 2019-07-15 17:30:44 +02:00
parent df7bb94daf
commit 1b0e192046
9 changed files with 488 additions and 412 deletions

View file

@ -434,9 +434,7 @@ inline Circle to_lnCircle(const CircleBed& circ) {
}
// Get the type of bed geometry from a simple vector of points.
BedShapeHint bedShape(const Polyline &bed) {
BedShapeHint ret;
BedShapeHint::BedShapeHint(const Polyline &bed) {
auto x = [](const Point& p) { return p(X); };
auto y = [](const Point& p) { return p(Y); };
@ -497,19 +495,16 @@ BedShapeHint bedShape(const Polyline &bed) {
auto parea = poly_area(bed);
if( (1.0 - parea/area(bb)) < 1e-3 ) {
ret.type = BedShapeType::BOX;
ret.shape.box = bb;
m_type = BedShapes::bsBox;
m_bed.box = bb;
}
else if(auto c = isCircle(bed)) {
ret.type = BedShapeType::CIRCLE;
ret.shape.circ = c;
m_type = BedShapes::bsCircle;
m_bed.circ = c;
} else {
ret.type = BedShapeType::IRREGULAR;
ret.shape.polygon = bed;
m_type = BedShapes::bsIrregular;
m_bed.polygon = bed;
}
// Determine the bed shape by hand
return ret;
}
template<class BinT> // Arrange for arbitrary bin type
@ -588,6 +583,7 @@ void arrange(ArrangePolygons & arrangables,
outp.emplace_back(std::move(clpath));
outp.back().rotation(rotation);
outp.back().translation({offs.x(), offs.y()});
outp.back().binId(arrpoly.bed_idx);
};
for (ArrangePolygon &arrangeable : arrangables)
@ -596,6 +592,8 @@ void arrange(ArrangePolygons & arrangables,
for (const ArrangePolygon &fixed: excludes)
process_arrangeable(fixed, fixeditems);
for (Item &itm : fixeditems) itm.inflate(-2 * SCALED_EPSILON);
// Integer ceiling the min distance from the bed perimeters
coord_t md = min_obj_dist - SCALED_EPSILON;
md = (md % 2) ? md / 2 + 1 : md / 2;
@ -603,39 +601,38 @@ void arrange(ArrangePolygons & arrangables,
auto &cfn = stopcondition;
auto &pri = progressind;
switch (bedhint.type) {
case BedShapeType::BOX: {
switch (bedhint.get_type()) {
case bsBox: {
// Create the arranger for the box shaped bed
BoundingBox bbb = bedhint.shape.box;
BoundingBox bbb = bedhint.get_box();
bbb.min -= Point{md, md}, bbb.max += Point{md, md};
Box binbb{{bbb.min(X), bbb.min(Y)}, {bbb.max(X), bbb.max(Y)}};
_arrange(items, fixeditems, binbb, min_obj_dist, pri, cfn);
break;
}
case BedShapeType::CIRCLE: {
auto c = bedhint.shape.circ;
auto cc = to_lnCircle(c);
case bsCircle: {
auto cc = to_lnCircle(bedhint.get_circle());
_arrange(items, fixeditems, cc, min_obj_dist, pri, cfn);
break;
}
case BedShapeType::IRREGULAR: {
auto ctour = Slic3rMultiPoint_to_ClipperPath(bedhint.shape.polygon);
case bsIrregular: {
auto ctour = Slic3rMultiPoint_to_ClipperPath(bedhint.get_irregular());
auto irrbed = sl::create<clppr::Polygon>(std::move(ctour));
BoundingBox polybb(bedhint.shape.polygon);
BoundingBox polybb(bedhint.get_irregular());
_arrange(items, fixeditems, irrbed, min_obj_dist, pri, cfn);
break;
}
case BedShapeType::INFINITE: {
const InfiniteBed& nobin = bedhint.shape.infinite;
case bsInfinite: {
const InfiniteBed& nobin = bedhint.get_infinite();
auto infbb = Box::infinite({nobin.center.x(), nobin.center.y()});
_arrange(items, fixeditems, infbb, min_obj_dist, pri, cfn);
break;
}
case BedShapeType::UNKNOWN: {
case bsUnknown: {
// We know nothing about the bed, let it be infinite and zero centered
_arrange(items, fixeditems, Box::infinite(), min_obj_dist, pri, cfn);
break;

View file

@ -22,97 +22,152 @@ public:
inline operator bool() { return !std::isnan(radius_); }
};
/// Representing an unbounded bin
/// Representing an unbounded bed.
struct InfiniteBed { Point center; };
/// Types of print bed shapes.
enum class BedShapeType {
BOX,
CIRCLE,
IRREGULAR,
INFINITE,
UNKNOWN
enum BedShapes {
bsBox,
bsCircle,
bsIrregular,
bsInfinite,
bsUnknown
};
/// Info about the print bed for the arrange() function.
struct BedShapeHint {
BedShapeType type = BedShapeType::INFINITE;
union BedShape_u { // I know but who cares... TODO: use variant from cpp17?
/// Info about the print bed for the arrange() function. This is a variant
/// holding one of the four shapes a bed can be.
class BedShapeHint {
BedShapes m_type = BedShapes::bsInfinite;
union BedShape_u { // TODO: use variant from cpp17?
CircleBed circ;
BoundingBox box;
Polyline polygon;
InfiniteBed infinite{};
InfiniteBed infbed{};
~BedShape_u() {}
BedShape_u() {};
} shape;
} m_bed;
BedShapeHint() {};
public:
BedShapeHint(){};
~BedShapeHint() {
if (type == BedShapeType::IRREGULAR)
shape.polygon.Slic3r::Polyline::~Polyline();
};
BedShapeHint(const BedShapeHint &cpy) {
*this = cpy;
/// Get a bed shape hint for arrange() from a naked Polyline.
explicit BedShapeHint(const Polyline &polyl);
explicit BedShapeHint(const BoundingBox &bb)
{
m_type = bsBox; m_bed.box = bb;
}
BedShapeHint& operator=(const BedShapeHint &cpy) {
type = cpy.type;
switch(type) {
case BedShapeType::BOX: shape.box = cpy.shape.box; break;
case BedShapeType::CIRCLE: shape.circ = cpy.shape.circ; break;
case BedShapeType::IRREGULAR: shape.polygon = cpy.shape.polygon; break;
case BedShapeType::INFINITE: shape.infinite = cpy.shape.infinite; break;
case BedShapeType::UNKNOWN: break;
explicit BedShapeHint(const CircleBed &c)
{
m_type = bsCircle; m_bed.circ = c;
}
explicit BedShapeHint(const InfiniteBed &ibed)
{
m_type = bsInfinite; m_bed.infbed = ibed;
}
~BedShapeHint()
{
if (m_type == BedShapes::bsIrregular)
m_bed.polygon.Slic3r::Polyline::~Polyline();
};
BedShapeHint(const BedShapeHint &cpy) { *this = cpy; }
BedShapeHint(BedShapeHint &&cpy) { *this = std::move(cpy); }
BedShapeHint &operator=(const BedShapeHint &cpy)
{
m_type = cpy.m_type;
switch(m_type) {
case bsBox: m_bed.box = cpy.m_bed.box; break;
case bsCircle: m_bed.circ = cpy.m_bed.circ; break;
case bsIrregular: m_bed.polygon = cpy.m_bed.polygon; break;
case bsInfinite: m_bed.infbed = cpy.m_bed.infbed; break;
case bsUnknown: break;
}
return *this;
}
BedShapeHint& operator=(BedShapeHint &&cpy)
{
m_type = cpy.m_type;
switch(m_type) {
case bsBox: m_bed.box = std::move(cpy.m_bed.box); break;
case bsCircle: m_bed.circ = std::move(cpy.m_bed.circ); break;
case bsIrregular: m_bed.polygon = std::move(cpy.m_bed.polygon); break;
case bsInfinite: m_bed.infbed = std::move(cpy.m_bed.infbed); break;
case bsUnknown: break;
}
return *this;
}
BedShapes get_type() const { return m_type; }
const BoundingBox &get_box() const
{
assert(m_type == bsBox); return m_bed.box;
}
const CircleBed &get_circle() const
{
assert(m_type == bsCircle); return m_bed.circ;
}
const Polyline &get_irregular() const
{
assert(m_type == bsIrregular); return m_bed.polygon;
}
const InfiniteBed &get_infinite() const
{
assert(m_type == bsInfinite); return m_bed.infbed;
}
};
/// Get a bed shape hint for arrange() from a naked Polyline.
BedShapeHint bedShape(const Polyline& bed);
static const constexpr long UNARRANGED = -1;
/// A logical bed representing an object not being arranged. Either the arrange
/// has not yet succesfully run on this ArrangePolygon or it could not fit the
/// object due to overly large size or invalid geometry.
static const constexpr int UNARRANGED = -1;
/// Input/Output structure for the arrange() function. The poly field will not
/// be modified during arrangement. Instead, the translation and rotation fields
/// will mark the needed transformation for the polygon to be in the arranged
/// position. These can also be set to an initial offset and rotation.
///
/// The bed_idx field will indicate the logical bed into which the
/// polygon belongs: UNARRANGED means no place for the polygon
/// (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.
struct ArrangePolygon {
const ExPolygon poly;
Vec2crd translation{0, 0};
double rotation{0.0};
long bed_idx{UNARRANGED};
const ExPolygon poly; /// The 2D silhouette to be arranged
Vec2crd translation{0, 0}; /// The translation of the poly
double rotation{0.0}; /// The rotation of the poly in radians
int bed_idx{UNARRANGED}; /// To which logical bed does poly belong...
ArrangePolygon(const ExPolygon &p, const Vec2crd &tr = {}, double rot = 0.0)
: poly{p}, translation{tr}, rotation{rot}
ArrangePolygon(ExPolygon p, const Vec2crd &tr = {}, double rot = 0.0)
: poly{std::move(p)}, translation{tr}, rotation{rot}
{}
};
using ArrangePolygons = std::vector<ArrangePolygon>;
/**
* \brief Arranges the model objects on the screen.
* \brief Arranges the input polygons.
*
* The arrangement considers multiple bins (aka. print beds) for placing
* all the items provided in the model argument. If the items don't fit on
* one print bed, the remaining will be placed onto newly created print
* beds. The first_bin_only parameter, if set to true, disables this
* behavior and makes sure that only one print bed is filled and the
* remaining items will be untouched. When set to false, the items which
* could not fit onto the print bed will be placed next to the print bed so
* the user should see a pile of items on the print bed and some other
* piles outside the print area that can be dragged later onto the print
* bed as a group.
* WARNING: Currently, only convex polygons are supported by the libnest2d
* library which is used to do the arrangement. This might change in the future
* this is why the interface contains a general polygon capable to have holes.
*
* \param items Input which are object pointers implementing the
* Arrangeable interface.
* \param items Input vector of ArrangePolygons. The transformation, rotation
* and bin_idx fields will be changed after the call finished and can be used
* to apply the result on the input polygon.
*
* \param min_obj_distance The minimum distance which is allowed for any
* pair of items on the print bed in any direction.
*
* \param bedhint Info about the shape and type of the
* bed. remaining items which do not fit onto the print area next to the
* print bed or leave them untouched (let the user arrange them by hand or
* remove them).
* \param bedhint Info about the shape and type of the bed.
*
* \param progressind Progress indicator callback called when
* an object gets packed. The unsigned argument is the number of items
@ -127,7 +182,7 @@ void arrange(ArrangePolygons & items,
std::function<bool(void)> stopcondition = nullptr);
/// Same as the previous, only that it takes unmovable items as an
/// additional argument.
/// additional argument. Those will be considered as already arranged objects.
void arrange(ArrangePolygons & items,
const ArrangePolygons & excludes,
coord_t min_obj_distance,

View file

@ -372,35 +372,7 @@ static bool _arrange(const Pointfs &sizes, coordf_t dist, const BoundingBoxf* bb
/* arrange objects preserving their instance count
but altering their instance positions */
bool Model::arrange_objects(coordf_t dist, const BoundingBoxf* bb)
{
// get the (transformed) size of each instance so that we take
// into account their different transformations when packing
// Pointfs instance_sizes;
// Pointfs instance_centers;
// for (const ModelObject *o : this->objects)
// for (size_t i = 0; i < o->instances.size(); ++ i) {
// // an accurate snug bounding box around the transformed mesh.
// BoundingBoxf3 bbox(o->instance_bounding_box(i, true));
// instance_sizes.emplace_back(to_2d(bbox.size()));
// instance_centers.emplace_back(to_2d(bbox.center()));
// }
// Pointfs positions;
// if (! _arrange(instance_sizes, dist, bb, positions))
// return false;
// size_t idx = 0;
// for (ModelObject *o : this->objects) {
// for (ModelInstance *i : o->instances) {
// Vec2d offset_xy = positions[idx] - instance_centers[idx];
// i->set_offset(Vec3d(offset_xy(0), offset_xy(1), i->get_offset(Z)));
// ++idx;
// }
// o->invalidate_bounding_box();
// }
// return true;
{
size_t count = 0;
for (auto obj : objects) count += obj->instances.size();
@ -414,29 +386,23 @@ bool Model::arrange_objects(coordf_t dist, const BoundingBoxf* bb)
instances.emplace_back(minst);
}
arrangement::BedShapeHint bedhint;
if (bb) {
bedhint.type = arrangement::BedShapeType::BOX;
bedhint.shape.box = BoundingBox(scaled(bb->min), scaled(bb->max));
}
if (bb)
bedhint = arrangement::BedShapeHint(
BoundingBox(scaled(bb->min), scaled(bb->max)));
arrangement::arrange(input, scaled(dist), bedhint);
bool ret = true;
for(size_t i = 0; i < input.size(); ++i) {
auto inst = instances[i];
inst->set_rotation(Z, input[i].rotation);
auto tr = unscaled<double>(input[i].translation);
inst->set_offset(X, tr.x());
inst->set_offset(Y, tr.y());
if (input[i].bed_idx != 0) ret = false; // no logical beds are allowed
if (input[i].bed_idx == 0) { // no logical beds are allowed
instances[i]->apply_arrange_result(input[i].translation,
input[i].rotation);
} else ret = false;
}
return ret;
}
@ -1842,28 +1808,37 @@ void ModelInstance::transform_polygon(Polygon* polygon) const
arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const
{
static const double SIMPLIFY_TOLERANCE_MM = 0.1;
Vec3d rotation = get_rotation();
rotation.z() = 0.;
Transform3d trafo_instance =
Geometry::assemble_transform(Vec3d::Zero(), rotation,
get_scaling_factor(), get_mirror());
Polygon p = get_object()->convex_hull_2d(trafo_instance);
assert(!p.points.empty());
// this may happen for malformed models, see:
// https://github.com/prusa3d/PrusaSlicer/issues/2209
if (p.points.empty()) return {{}};
Polygons pp{p};
pp = p.simplify(scaled<double>(SIMPLIFY_TOLERANCE_MM));
if (!pp.empty()) p = pp.front();
ExPolygon ep; ep.contour = std::move(p);
if (!m_arrange_cache.valid) {
Vec3d rotation = get_rotation();
rotation.z() = 0.;
Transform3d trafo_instance =
Geometry::assemble_transform(Vec3d::Zero(), rotation,
get_scaling_factor(), get_mirror());
return {ep, Vec2crd{scaled(get_offset(X)), scaled(get_offset(Y))}, get_rotation(Z)};
Polygon p = get_object()->convex_hull_2d(trafo_instance);
assert(!p.points.empty());
// this may happen for malformed models, see:
// https://github.com/prusa3d/PrusaSlicer/issues/2209
if (p.points.empty()) return {{}};
Polygons pp{p};
pp = p.simplify(scaled<double>(SIMPLIFY_TOLERANCE_MM));
if (!pp.empty()) p = pp.front();
m_arrange_cache.poly.contour = std::move(p);
m_arrange_cache.valid = true;
}
arrangement::ArrangePolygon ret{m_arrange_cache.poly,
Vec2crd{scaled(get_offset(X)),
scaled(get_offset(Y))},
get_rotation(Z)};
ret.bed_idx = m_arrange_cache.bed_idx;
return ret;
}
// Test whether the two models contain the same number of ModelObjects with the same set of IDs

View file

@ -512,7 +512,7 @@ public:
ModelObject* get_object() const { return this->object; }
const Geometry::Transformation& get_transformation() const { return m_transformation; }
void set_transformation(const Geometry::Transformation& transformation) { m_transformation = transformation; }
void set_transformation(const Geometry::Transformation& transformation) { m_transformation = transformation; m_arrange_cache.valid = false; }
const Vec3d& get_offset() const { return m_transformation.get_offset(); }
double get_offset(Axis axis) const { return m_transformation.get_offset(axis); }
@ -523,21 +523,21 @@ public:
const Vec3d& get_rotation() const { return m_transformation.get_rotation(); }
double get_rotation(Axis axis) const { return m_transformation.get_rotation(axis); }
void set_rotation(const Vec3d& rotation) { m_transformation.set_rotation(rotation); }
void set_rotation(Axis axis, double rotation) { m_transformation.set_rotation(axis, rotation); }
void set_rotation(const Vec3d& rotation) { m_transformation.set_rotation(rotation); m_arrange_cache.valid = false; }
void set_rotation(Axis axis, double rotation) { m_transformation.set_rotation(axis, rotation); if (axis != Z) m_arrange_cache.valid = false; }
const Vec3d& get_scaling_factor() const { return m_transformation.get_scaling_factor(); }
double get_scaling_factor(Axis axis) const { return m_transformation.get_scaling_factor(axis); }
void set_scaling_factor(const Vec3d& scaling_factor) { m_transformation.set_scaling_factor(scaling_factor); }
void set_scaling_factor(Axis axis, double scaling_factor) { m_transformation.set_scaling_factor(axis, scaling_factor); }
void set_scaling_factor(const Vec3d& scaling_factor) { m_transformation.set_scaling_factor(scaling_factor); m_arrange_cache.valid = false; }
void set_scaling_factor(Axis axis, double scaling_factor) { m_transformation.set_scaling_factor(axis, scaling_factor); m_arrange_cache.valid = false; }
const Vec3d& get_mirror() const { return m_transformation.get_mirror(); }
double get_mirror(Axis axis) const { return m_transformation.get_mirror(axis); }
bool is_left_handed() const { return m_transformation.is_left_handed(); }
void set_mirror(const Vec3d& mirror) { m_transformation.set_mirror(mirror); }
void set_mirror(Axis axis, double mirror) { m_transformation.set_mirror(axis, mirror); }
void set_mirror(const Vec3d& mirror) { m_transformation.set_mirror(mirror); m_arrange_cache.valid = false; }
void set_mirror(Axis axis, double mirror) { m_transformation.set_mirror(axis, mirror); m_arrange_cache.valid = false; }
// To be called on an external mesh
void transform_mesh(TriangleMesh* mesh, bool dont_translate = false) const;
@ -554,20 +554,17 @@ public:
bool is_printable() const { return print_volume_state == PVS_Inside; }
// /////////////////////////////////////////////////////////////////////////
// Implement arrangement::Arrangeable interface
// /////////////////////////////////////////////////////////////////////////
// Getting the input polygon for arrange
arrangement::ArrangePolygon get_arrange_polygon() const;
// Apply the arrange result on the ModelInstance
void apply_arrange_result(Vec2crd offs, double rot_rads)
void apply_arrange_result(Vec2crd offs, double rot_rads, int bed_idx = 0)
{
// write the transformation data into the model instance
set_rotation(Z, rot_rads);
set_offset(X, unscale<double>(offs(X)));
set_offset(Y, unscale<double>(offs(Y)));
m_arrange_cache.bed_idx = bed_idx;
}
protected:
@ -583,15 +580,28 @@ private:
ModelObject* object;
// Constructor, which assigns a new unique ID.
explicit ModelInstance(ModelObject *object) : object(object), print_volume_state(PVS_Inside) {}
explicit ModelInstance(ModelObject *object) : object(object), print_volume_state(PVS_Inside)
{
get_arrange_polygon(); // initialize the arrange cache
}
// Constructor, which assigns a new unique ID.
explicit ModelInstance(ModelObject *object, const ModelInstance &other) :
m_transformation(other.m_transformation), object(object), print_volume_state(PVS_Inside) {}
m_transformation(other.m_transformation), object(object), print_volume_state(PVS_Inside)
{
get_arrange_polygon(); // initialize the arrange cache
}
ModelInstance() = delete;
explicit ModelInstance(ModelInstance &&rhs) = delete;
ModelInstance& operator=(const ModelInstance &rhs) = delete;
ModelInstance& operator=(ModelInstance &&rhs) = delete;
// Warning! This object is not guarded against concurrency.
mutable struct ArrangeCache {
bool valid = false;
int bed_idx { arrangement::UNARRANGED };
ExPolygon poly;
} m_arrange_cache;
};
// The print bed content.