#ifndef ARRANGEITEM_HPP #define ARRANGEITEM_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Slic3r { namespace arr2 { struct InfiniteBed; inline bool check_polygons_are_convex(const Polygons &pp) { return std::all_of(pp.begin(), pp.end(), [](const Polygon &p) { return polygon_is_convex(p); }); } // A class that stores a set of polygons that are garanteed to be all convex. // They collectively represent a decomposition of a more complex shape into // its convex part. Note that this class only stores the result of the decomp, // does not do the job itself. In debug mode, an explicit check is done for // each component to be convex. // // Additionally class stores a translation vector and a rotation angle for the // stored polygon, plus additional privitives that are all cached cached after // appying a the transformations. The caching is not thread safe! class DecomposedShape { Polygons m_shape; Vec2crd m_translation{0, 0}; // The translation of the poly double m_rotation{0.0}; // The rotation of the poly in radians mutable Polygons m_transformed_outline; mutable bool m_transformed_outline_valid = false; mutable Point m_reference_vertex; mutable std::vector m_refs; mutable std::vector m_mins; mutable bool m_reference_vertex_valid = false; mutable Point m_centroid; mutable bool m_centroid_valid = false; mutable Polygon m_convex_hull; mutable BoundingBox m_bounding_box; mutable double m_area = 0; public: DecomposedShape() = default; explicit DecomposedShape(Polygon sh) { m_shape.emplace_back(std::move(sh)); assert(check_polygons_are_convex(m_shape)); } explicit DecomposedShape(std::initializer_list pts) : DecomposedShape(Polygon{pts}) {} explicit DecomposedShape(Polygons sh) : m_shape{std::move(sh)} { assert(check_polygons_are_convex(m_shape)); } const Polygons &contours() const { return m_shape; } const Vec2crd &translation() const { return m_translation; } double rotation() const { return m_rotation; } void translation(const Vec2crd &v) { m_translation = v; m_transformed_outline_valid = false; m_reference_vertex_valid = false; m_centroid_valid = false; } void rotation(double v) { m_rotation = v; m_transformed_outline_valid = false; m_reference_vertex_valid = false; m_centroid_valid = false; } const Polygons &transformed_outline() const; const Polygon &convex_hull() const; const BoundingBox &bounding_box() const; // The cached reference vertex in the context of NFP creation. Always // refers to the leftmost upper vertex. const Vec2crd &reference_vertex() const; const Vec2crd &reference_vertex(size_t idx) const; // Also for NFP calculations, the rightmost lowest vertex of the shape. const Vec2crd &min_vertex(size_t idx) const; double area_unscaled() const { // update cache transformed_outline(); return m_area; } Vec2crd centroid() const; }; DecomposedShape decompose(const ExPolygons &polys); DecomposedShape decompose(const Polygon &p); class ArrangeItem { private: DecomposedShape m_shape; // Shape of item when it's not moving AnyPtr m_envelope; // Possibly different shape when packed ArbitraryDataStore m_datastore; int m_bed_idx{Unarranged}; // To which logical bed does this item belong int m_priority{0}; // For sorting std::optional m_bed_constraint; public: ArrangeItem() = default; explicit ArrangeItem(DecomposedShape shape) : m_shape(std::move(shape)), m_envelope{&m_shape} {} explicit ArrangeItem(DecomposedShape shape, DecomposedShape envelope) : m_shape(std::move(shape)) , m_envelope{std::make_unique(std::move(envelope))} {} explicit ArrangeItem(const ExPolygons &shape); explicit ArrangeItem(Polygon shape); explicit ArrangeItem(std::initializer_list pts) : ArrangeItem(Polygon{pts}) {} ArrangeItem(const ArrangeItem &); ArrangeItem(ArrangeItem &&) noexcept; ArrangeItem & operator=(const ArrangeItem &); ArrangeItem & operator=(ArrangeItem &&) noexcept; int bed_idx() const { return m_bed_idx; } int priority() const { return m_priority; } std::optional bed_constraint() const { return m_bed_constraint; }; void bed_idx(int v) { m_bed_idx = v; } void priority(int v) { m_priority = v; } void bed_constraint(std::optional v) { m_bed_constraint = v; } const ArbitraryDataStore &datastore() const { return m_datastore; } ArbitraryDataStore &datastore() { return m_datastore; } const DecomposedShape & shape() const { return m_shape; } void set_shape(DecomposedShape shape); const DecomposedShape & envelope() const { return *m_envelope; } void set_envelope(DecomposedShape envelope); const Vec2crd &translation() const { return m_shape.translation(); } double rotation() const { return m_shape.rotation(); } void translation(const Vec2crd &v) { m_shape.translation(v); m_envelope->translation(v); } void rotation(double v) { m_shape.rotation(v); m_envelope->rotation(v); } void update_caches() const { m_shape.reference_vertex(); m_envelope->reference_vertex(); m_shape.centroid(); m_envelope->centroid(); } }; template<> struct ArrangeItemTraits_ { static const Vec2crd &get_translation(const ArrangeItem &itm) { return itm.translation(); } static double get_rotation(const ArrangeItem &itm) { return itm.rotation(); } static int get_bed_index(const ArrangeItem &itm) { return itm.bed_idx(); } static int get_priority(const ArrangeItem &itm) { return itm.priority(); } static std::optional get_bed_constraint(const ArrangeItem &itm) { return itm.bed_constraint(); } // Setters: static void set_translation(ArrangeItem &itm, const Vec2crd &v) { itm.translation(v); } static void set_rotation(ArrangeItem &itm, double v) { itm.rotation(v); } static void set_bed_index(ArrangeItem &itm, int v) { itm.bed_idx(v); } static void set_bed_constraint(ArrangeItem &itm, std::optional v) { itm.bed_constraint(v); } }; // Some items can be containers of arbitrary data stored under string keys. template<> struct DataStoreTraits_ { static constexpr bool Implemented = true; template static const T *get(const ArrangeItem &itm, const std::string &key) { return itm.datastore().get(key); } // Same as above just not const. template static T *get(ArrangeItem &itm, const std::string &key) { return itm.datastore().get(key); } static bool has_key(const ArrangeItem &itm, const std::string &key) { return itm.datastore().has_key(key); } }; template<> struct WritableDataStoreTraits_ { static constexpr bool Implemented = true; template static void set(ArrangeItem &itm, const std::string &key, T &&data) { itm.datastore().add(key, std::forward(data)); } }; template static Polygons calculate_nfp_unnormalized(const ArrangeItem &item, const Range &fixed_items, StopCond &&stop_cond = {}) { size_t cap = 0; for (const ArrangeItem &fixitem : fixed_items) { const Polygons &outlines = fixitem.shape().transformed_outline(); cap += outlines.size(); } const Polygons &item_outlines = item.envelope().transformed_outline(); auto nfps = reserve_polygons(cap * item_outlines.size()); Vec2crd ref_whole = item.envelope().reference_vertex(); Polygon subnfp; for (const ArrangeItem &fixed : fixed_items) { // fixed_polys should already be a set of strictly convex polygons, // as ArrangeItem stores convex-decomposed polygons const Polygons & fixed_polys = fixed.shape().transformed_outline(); for (const Polygon &fixed_poly : fixed_polys) { Point max_fixed = Slic3r::reference_vertex(fixed_poly); for (size_t mi = 0; mi < item_outlines.size(); ++mi) { const Polygon &movable = item_outlines[mi]; const Vec2crd &mref = item.envelope().reference_vertex(mi); subnfp = nfp_convex_convex_legacy(fixed_poly, movable); Vec2crd min_movable = item.envelope().min_vertex(mi); Vec2crd dtouch = max_fixed - min_movable; Vec2crd top_other = mref + dtouch; Vec2crd max_nfp = Slic3r::reference_vertex(subnfp); auto dnfp = top_other - max_nfp; auto d = ref_whole - mref + dnfp; subnfp.translate(d); nfps.emplace_back(subnfp); } if (stop_cond()) break; nfps = union_(nfps); } if (stop_cond()) { nfps.clear(); break; } } return nfps; } template<> struct NFPArrangeItemTraits_ { template static ExPolygons calculate_nfp(const ArrangeItem &item, const Context &packing_context, const Bed &bed, StopCond &&stopcond) { auto static_items = all_items_range(packing_context); Polygons nfps = arr2::calculate_nfp_unnormalized(item, static_items, stopcond); ExPolygons nfp_ex; if (!stopcond()) { if constexpr (!std::is_convertible_v) { ExPolygons ifpbed = ifp_convex(bed, item.envelope().convex_hull()); nfp_ex = diff_ex(ifpbed, nfps); } else { nfp_ex = union_ex(nfps); } } item.update_caches(); return nfp_ex; } static const Vec2crd& reference_vertex(const ArrangeItem &item) { return item.envelope().reference_vertex(); } static BoundingBox envelope_bounding_box(const ArrangeItem &itm) { return itm.envelope().bounding_box(); } static BoundingBox fixed_bounding_box(const ArrangeItem &itm) { return itm.shape().bounding_box(); } static double envelope_area(const ArrangeItem &itm) { return itm.envelope().area_unscaled() * scaled(1.) * scaled(1.); } static double fixed_area(const ArrangeItem &itm) { return itm.shape().area_unscaled() * scaled(1.) * scaled(1.); } static const Polygons & envelope_outline(const ArrangeItem &itm) { return itm.envelope().transformed_outline(); } static const Polygons & fixed_outline(const ArrangeItem &itm) { return itm.shape().transformed_outline(); } static const Polygon & envelope_convex_hull(const ArrangeItem &itm) { return itm.envelope().convex_hull(); } static const Polygon & fixed_convex_hull(const ArrangeItem &itm) { return itm.shape().convex_hull(); } static const std::vector& allowed_rotations(const ArrangeItem &itm) { static const std::vector ret_zero = {0.}; const std::vector * ret_ptr = &ret_zero; auto rots = get_data>(itm, "rotations"); if (rots) { ret_ptr = rots; } return *ret_ptr; } static Vec2crd fixed_centroid(const ArrangeItem &itm) { return itm.shape().centroid(); } static Vec2crd envelope_centroid(const ArrangeItem &itm) { return itm.envelope().centroid(); } }; template<> struct IsMutableItem_: public std::true_type {}; template<> struct MutableItemTraits_ { static void set_priority(ArrangeItem &itm, int p) { itm.priority(p); } static void set_convex_shape(ArrangeItem &itm, const Polygon &shape) { itm.set_shape(DecomposedShape{shape}); } static void set_shape(ArrangeItem &itm, const ExPolygons &shape) { itm.set_shape(decompose(shape)); } static void set_convex_envelope(ArrangeItem &itm, const Polygon &envelope) { itm.set_envelope(DecomposedShape{envelope}); } static void set_envelope(ArrangeItem &itm, const ExPolygons &envelope) { itm.set_envelope(decompose(envelope)); } template static void set_arbitrary_data(ArrangeItem &itm, const std::string &key, T &&data) { set_data(itm, key, std::forward(data)); } static void set_allowed_rotations(ArrangeItem &itm, const std::vector &rotations) { set_data(itm, "rotations", rotations); } }; extern template struct ImbueableItemTraits_; extern template class ArrangeableToItemConverter; extern template struct ArrangeTask; extern template struct FillBedTask; extern template struct MultiplySelectionTask; extern template class Arranger; }} // namespace Slic3r::arr2 #endif // ARRANGEITEM_HPP