#ifndef MODELARRANGE_HPP #define MODELARRANGE_HPP #include "ExPolygon.hpp" #include "BoundingBox.hpp" namespace Slic3r { namespace arrangement { /// A geometry abstraction for a circular print bed. Similarly to BoundingBox. class CircleBed { Point center_; double radius_; public: inline CircleBed(): center_(0, 0), radius_(std::nan("")) {} inline CircleBed(const Point& c, double r): center_(c), radius_(r) {} inline double radius() const { return radius_; } inline const Point& center() const { return center_; } inline operator bool() { return !std::isnan(radius_); } }; /// Representing an unbounded bed. struct InfiniteBed { Point center; }; /// Types of print bed shapes. enum BedShapes { bsBox, bsCircle, bsIrregular, bsInfinite, bsUnknown }; /// 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 infbed{}; ~BedShape_u() {} BedShape_u() {}; } m_bed; public: BedShapeHint(){}; /// 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; } 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; } }; /// 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. 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 { 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... int priority{0}; /// Optional setter function which can store arbitrary data in its closure std::function setter = nullptr; /// Helper function to call the setter with the arrange data arguments void apply() const { if (setter) setter(*this); } /// Test if arrange() was called previously and gave a successful result. bool is_arranged() const { return bed_idx != UNARRANGED; } }; using ArrangePolygons = std::vector; /** * \brief Arranges the input polygons. * * 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 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. * * \param progressind Progress indicator callback called when * an object gets packed. The unsigned argument is the number of items * remaining to pack. * * \param stopcondition A predicate returning true if abort is needed. */ void arrange(ArrangePolygons & items, coord_t min_obj_distance, const BedShapeHint & bedhint, std::function progressind = nullptr, std::function stopcondition = nullptr); /// Same as the previous, only that it takes unmovable items as an /// additional argument. Those will be considered as already arranged objects. void arrange(ArrangePolygons & items, const ArrangePolygons & excludes, coord_t min_obj_distance, const BedShapeHint & bedhint, std::function progressind = nullptr, std::function stopcondition = nullptr); } // arr } // Slic3r #endif // MODELARRANGE_HPP