mirror of
https://github.com/QIDITECH/QIDISlicer.git
synced 2026-02-03 01:18:44 +03:00
Merge prusa 2.6.1
This commit is contained in:
92
src/libslic3r/Arrange/Items/ArbitraryDataStore.hpp
Normal file
92
src/libslic3r/Arrange/Items/ArbitraryDataStore.hpp
Normal file
@@ -0,0 +1,92 @@
|
||||
|
||||
#ifndef ARBITRARYDATASTORE_HPP
|
||||
#define ARBITRARYDATASTORE_HPP
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <any>
|
||||
|
||||
#include "libslic3r/Arrange/Core/DataStoreTraits.hpp"
|
||||
|
||||
namespace Slic3r { namespace arr2 {
|
||||
|
||||
// An associative container able to store and retrieve any data type.
|
||||
// Based on std::any
|
||||
class ArbitraryDataStore {
|
||||
std::map<std::string, std::any> m_data;
|
||||
|
||||
public:
|
||||
template<class T> void add(const std::string &key, T &&data)
|
||||
{
|
||||
m_data[key] = std::any{std::forward<T>(data)};
|
||||
}
|
||||
|
||||
void add(const std::string &key, std::any &&data)
|
||||
{
|
||||
m_data[key] = std::move(data);
|
||||
}
|
||||
|
||||
// Return nullptr if the key does not exist or the stored data has a
|
||||
// type other then T. Otherwise returns a pointer to the stored data.
|
||||
template<class T> const T *get(const std::string &key) const
|
||||
{
|
||||
auto it = m_data.find(key);
|
||||
return it != m_data.end() ? std::any_cast<T>(&(it->second)) :
|
||||
nullptr;
|
||||
}
|
||||
|
||||
// Same as above just not const.
|
||||
template<class T> T *get(const std::string &key)
|
||||
{
|
||||
auto it = m_data.find(key);
|
||||
return it != m_data.end() ? std::any_cast<T>(&(it->second)) : nullptr;
|
||||
}
|
||||
|
||||
bool has_key(const std::string &key) const
|
||||
{
|
||||
auto it = m_data.find(key);
|
||||
return it != m_data.end();
|
||||
}
|
||||
};
|
||||
|
||||
// Some items can be containers of arbitrary data stored under string keys.
|
||||
template<> struct DataStoreTraits_<ArbitraryDataStore>
|
||||
{
|
||||
static constexpr bool Implemented = true;
|
||||
|
||||
template<class T>
|
||||
static const T *get(const ArbitraryDataStore &s, const std::string &key)
|
||||
{
|
||||
return s.get<T>(key);
|
||||
}
|
||||
|
||||
// Same as above just not const.
|
||||
template<class T>
|
||||
static T *get(ArbitraryDataStore &s, const std::string &key)
|
||||
{
|
||||
return s.get<T>(key);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
static bool has_key(ArbitraryDataStore &s, const std::string &key)
|
||||
{
|
||||
return s.has_key(key);
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct WritableDataStoreTraits_<ArbitraryDataStore>
|
||||
{
|
||||
static constexpr bool Implemented = true;
|
||||
|
||||
template<class T>
|
||||
static void set(ArbitraryDataStore &store,
|
||||
const std::string &key,
|
||||
T &&data)
|
||||
{
|
||||
store.add(key, std::forward<T>(data));
|
||||
}
|
||||
};
|
||||
|
||||
}} // namespace Slic3r::arr2
|
||||
|
||||
#endif // ARBITRARYDATASTORE_HPP
|
||||
206
src/libslic3r/Arrange/Items/ArrangeItem.cpp
Normal file
206
src/libslic3r/Arrange/Items/ArrangeItem.cpp
Normal file
@@ -0,0 +1,206 @@
|
||||
|
||||
#include "ArrangeItem.hpp"
|
||||
|
||||
#include "libslic3r/Arrange/Core/NFP/NFPConcave_Tesselate.hpp"
|
||||
|
||||
#include "libslic3r/Arrange/ArrangeImpl.hpp"
|
||||
#include "libslic3r/Arrange/Tasks/ArrangeTaskImpl.hpp"
|
||||
#include "libslic3r/Arrange/Tasks/FillBedTaskImpl.hpp"
|
||||
#include "libslic3r/Arrange/Tasks/MultiplySelectionTaskImpl.hpp"
|
||||
|
||||
#include "libslic3r/Geometry/ConvexHull.hpp"
|
||||
|
||||
namespace Slic3r { namespace arr2 {
|
||||
|
||||
const Polygons &DecomposedShape::transformed_outline() const
|
||||
{
|
||||
constexpr auto sc = scaled<double>(1.) * scaled<double>(1.);
|
||||
|
||||
if (!m_transformed_outline_valid) {
|
||||
m_transformed_outline = contours();
|
||||
for (Polygon &poly : m_transformed_outline) {
|
||||
poly.rotate(rotation());
|
||||
poly.translate(translation());
|
||||
}
|
||||
|
||||
m_area = std::accumulate(m_transformed_outline.begin(),
|
||||
m_transformed_outline.end(), 0.,
|
||||
[sc](double s, const auto &p) {
|
||||
return s + p.area() / sc;
|
||||
});
|
||||
|
||||
m_convex_hull = Geometry::convex_hull(m_transformed_outline);
|
||||
m_bounding_box = get_extents(m_convex_hull);
|
||||
|
||||
m_transformed_outline_valid = true;
|
||||
}
|
||||
|
||||
return m_transformed_outline;
|
||||
}
|
||||
|
||||
const Polygon &DecomposedShape::convex_hull() const
|
||||
{
|
||||
if (!m_transformed_outline_valid)
|
||||
transformed_outline();
|
||||
|
||||
return m_convex_hull;
|
||||
}
|
||||
|
||||
const BoundingBox &DecomposedShape::bounding_box() const
|
||||
{
|
||||
if (!m_transformed_outline_valid)
|
||||
transformed_outline();
|
||||
|
||||
return m_bounding_box;
|
||||
}
|
||||
|
||||
const Vec2crd &DecomposedShape::reference_vertex() const
|
||||
{
|
||||
if (!m_reference_vertex_valid) {
|
||||
m_reference_vertex = Slic3r::reference_vertex(transformed_outline());
|
||||
m_refs.clear();
|
||||
m_mins.clear();
|
||||
m_refs.reserve(m_transformed_outline.size());
|
||||
m_mins.reserve(m_transformed_outline.size());
|
||||
for (auto &poly : m_transformed_outline) {
|
||||
m_refs.emplace_back(Slic3r::reference_vertex(poly));
|
||||
m_mins.emplace_back(Slic3r::min_vertex(poly));
|
||||
}
|
||||
m_reference_vertex_valid = true;
|
||||
}
|
||||
|
||||
return m_reference_vertex;
|
||||
}
|
||||
|
||||
const Vec2crd &DecomposedShape::reference_vertex(size_t i) const
|
||||
{
|
||||
if (!m_reference_vertex_valid) {
|
||||
reference_vertex();
|
||||
}
|
||||
|
||||
return m_refs[i];
|
||||
}
|
||||
|
||||
const Vec2crd &DecomposedShape::min_vertex(size_t idx) const
|
||||
{
|
||||
if (!m_reference_vertex_valid) {
|
||||
reference_vertex();
|
||||
}
|
||||
|
||||
return m_mins[idx];
|
||||
}
|
||||
|
||||
Vec2crd DecomposedShape::centroid() const
|
||||
{
|
||||
constexpr double area_sc = scaled<double>(1.) * scaled(1.);
|
||||
|
||||
if (!m_centroid_valid) {
|
||||
double total_area = 0.0;
|
||||
Vec2d cntr = Vec2d::Zero();
|
||||
|
||||
for (const Polygon& poly : transformed_outline()) {
|
||||
double parea = poly.area() / area_sc;
|
||||
Vec2d pcntr = unscaled(poly.centroid());
|
||||
total_area += parea;
|
||||
cntr += pcntr * parea;
|
||||
}
|
||||
|
||||
cntr /= total_area;
|
||||
m_centroid = scaled(cntr);
|
||||
m_centroid_valid = true;
|
||||
}
|
||||
|
||||
return m_centroid;
|
||||
}
|
||||
|
||||
DecomposedShape decompose(const ExPolygons &shape)
|
||||
{
|
||||
return DecomposedShape{convex_decomposition_tess(shape)};
|
||||
}
|
||||
|
||||
DecomposedShape decompose(const Polygon &shape)
|
||||
{
|
||||
Polygons convex_shapes;
|
||||
|
||||
bool is_convex = polygon_is_convex(shape);
|
||||
if (is_convex) {
|
||||
convex_shapes.emplace_back(shape);
|
||||
} else {
|
||||
convex_shapes = convex_decomposition_tess(shape);
|
||||
}
|
||||
|
||||
return DecomposedShape{std::move(convex_shapes)};
|
||||
}
|
||||
|
||||
ArrangeItem::ArrangeItem(const ExPolygons &shape)
|
||||
: m_shape{decompose(shape)}, m_envelope{&m_shape}
|
||||
{}
|
||||
|
||||
ArrangeItem::ArrangeItem(Polygon shape)
|
||||
: m_shape{decompose(shape)}, m_envelope{&m_shape}
|
||||
{}
|
||||
|
||||
ArrangeItem::ArrangeItem(const ArrangeItem &other)
|
||||
{
|
||||
this->operator= (other);
|
||||
}
|
||||
|
||||
ArrangeItem::ArrangeItem(ArrangeItem &&other) noexcept
|
||||
{
|
||||
this->operator=(std::move(other));
|
||||
}
|
||||
|
||||
ArrangeItem &ArrangeItem::operator=(const ArrangeItem &other)
|
||||
{
|
||||
m_shape = other.m_shape;
|
||||
m_datastore = other.m_datastore;
|
||||
m_bed_idx = other.m_bed_idx;
|
||||
m_priority = other.m_priority;
|
||||
|
||||
if (other.m_envelope.get() == &other.m_shape)
|
||||
m_envelope = &m_shape;
|
||||
else
|
||||
m_envelope = std::make_unique<DecomposedShape>(other.envelope());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void ArrangeItem::set_shape(DecomposedShape shape)
|
||||
{
|
||||
m_shape = std::move(shape);
|
||||
m_envelope = &m_shape;
|
||||
}
|
||||
|
||||
void ArrangeItem::set_envelope(DecomposedShape envelope)
|
||||
{
|
||||
m_envelope = std::make_unique<DecomposedShape>(std::move(envelope));
|
||||
|
||||
// Initial synch of transformations of envelope and shape.
|
||||
// They need to be in synch all the time
|
||||
m_envelope->translation(m_shape.translation());
|
||||
m_envelope->rotation(m_shape.rotation());
|
||||
}
|
||||
|
||||
ArrangeItem &ArrangeItem::operator=(ArrangeItem &&other) noexcept
|
||||
{
|
||||
m_shape = std::move(other.m_shape);
|
||||
m_datastore = std::move(other.m_datastore);
|
||||
m_bed_idx = other.m_bed_idx;
|
||||
m_priority = other.m_priority;
|
||||
|
||||
if (other.m_envelope.get() == &other.m_shape)
|
||||
m_envelope = &m_shape;
|
||||
else
|
||||
m_envelope = std::move(other.m_envelope);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template struct ImbueableItemTraits_<ArrangeItem>;
|
||||
template class ArrangeableToItemConverter<ArrangeItem>;
|
||||
template struct ArrangeTask<ArrangeItem>;
|
||||
template struct FillBedTask<ArrangeItem>;
|
||||
template struct MultiplySelectionTask<ArrangeItem>;
|
||||
template class Arranger<ArrangeItem>;
|
||||
|
||||
}} // namespace Slic3r::arr2
|
||||
481
src/libslic3r/Arrange/Items/ArrangeItem.hpp
Normal file
481
src/libslic3r/Arrange/Items/ArrangeItem.hpp
Normal file
@@ -0,0 +1,481 @@
|
||||
|
||||
#ifndef ARRANGEITEM_HPP
|
||||
#define ARRANGEITEM_HPP
|
||||
|
||||
#include <optional>
|
||||
#include <boost/variant.hpp>
|
||||
|
||||
#include "libslic3r/ExPolygon.hpp"
|
||||
#include "libslic3r/BoundingBox.hpp"
|
||||
#include "libslic3r/AnyPtr.hpp"
|
||||
|
||||
#include "libslic3r/Arrange/Core/PackingContext.hpp"
|
||||
#include "libslic3r/Arrange/Core/NFP/NFPArrangeItemTraits.hpp"
|
||||
#include "libslic3r/Arrange/Core/NFP/NFP.hpp"
|
||||
|
||||
#include "libslic3r/Arrange/Items/MutableItemTraits.hpp"
|
||||
|
||||
#include "libslic3r/Arrange/Arrange.hpp"
|
||||
#include "libslic3r/Arrange/Tasks/ArrangeTask.hpp"
|
||||
#include "libslic3r/Arrange/Tasks/FillBedTask.hpp"
|
||||
#include "libslic3r/Arrange/Tasks/MultiplySelectionTask.hpp"
|
||||
|
||||
#include "libslic3r/Arrange/Items/ArbitraryDataStore.hpp"
|
||||
|
||||
#include <libslic3r/ClipperUtils.hpp>
|
||||
|
||||
namespace Slic3r { namespace arr2 {
|
||||
|
||||
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<Point> m_refs;
|
||||
mutable std::vector<Point> 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<Point> 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<DecomposedShape> 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
|
||||
|
||||
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<DecomposedShape>(std::move(envelope))}
|
||||
{}
|
||||
|
||||
explicit ArrangeItem(const ExPolygons &shape);
|
||||
explicit ArrangeItem(Polygon shape);
|
||||
explicit ArrangeItem(std::initializer_list<Point> 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; }
|
||||
|
||||
void bed_idx(int v) { m_bed_idx = v; }
|
||||
void priority(int v) { m_priority = 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_<ArrangeItem>
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
};
|
||||
|
||||
// Some items can be containers of arbitrary data stored under string keys.
|
||||
template<> struct DataStoreTraits_<ArrangeItem>
|
||||
{
|
||||
static constexpr bool Implemented = true;
|
||||
|
||||
template<class T>
|
||||
static const T *get(const ArrangeItem &itm, const std::string &key)
|
||||
{
|
||||
return itm.datastore().get<T>(key);
|
||||
}
|
||||
|
||||
// Same as above just not const.
|
||||
template<class T>
|
||||
static T *get(ArrangeItem &itm, const std::string &key)
|
||||
{
|
||||
return itm.datastore().get<T>(key);
|
||||
}
|
||||
|
||||
static bool has_key(const ArrangeItem &itm, const std::string &key)
|
||||
{
|
||||
return itm.datastore().has_key(key);
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct WritableDataStoreTraits_<ArrangeItem>
|
||||
{
|
||||
static constexpr bool Implemented = true;
|
||||
|
||||
template<class T>
|
||||
static void set(ArrangeItem &itm,
|
||||
const std::string &key,
|
||||
T &&data)
|
||||
{
|
||||
itm.datastore().add(key, std::forward<T>(data));
|
||||
}
|
||||
};
|
||||
|
||||
template<class FixedIt, class StopCond = DefaultStopCondition>
|
||||
static Polygons calculate_nfp_unnormalized(const ArrangeItem &item,
|
||||
const Range<FixedIt> &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_<ArrangeItem> {
|
||||
template<class Context, class Bed, class StopCond>
|
||||
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<Bed, InfiniteBed>) {
|
||||
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<double>(1.) *
|
||||
scaled<double>(1.);
|
||||
}
|
||||
|
||||
static double fixed_area(const ArrangeItem &itm)
|
||||
{
|
||||
return itm.shape().area_unscaled() * scaled<double>(1.) *
|
||||
scaled<double>(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<double>& allowed_rotations(const ArrangeItem &itm)
|
||||
{
|
||||
static const std::vector<double> ret_zero = {0.};
|
||||
|
||||
const std::vector<double> * ret_ptr = &ret_zero;
|
||||
|
||||
auto rots = get_data<std::vector<double>>(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_<ArrangeItem>: public std::true_type {};
|
||||
|
||||
template<>
|
||||
struct MutableItemTraits_<ArrangeItem> {
|
||||
|
||||
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<class T>
|
||||
static void set_arbitrary_data(ArrangeItem &itm, const std::string &key, T &&data)
|
||||
{
|
||||
set_data(itm, key, std::forward<T>(data));
|
||||
}
|
||||
|
||||
static void set_allowed_rotations(ArrangeItem &itm, const std::vector<double> &rotations)
|
||||
{
|
||||
set_data(itm, "rotations", rotations);
|
||||
}
|
||||
};
|
||||
|
||||
extern template struct ImbueableItemTraits_<ArrangeItem>;
|
||||
extern template class ArrangeableToItemConverter<ArrangeItem>;
|
||||
extern template struct ArrangeTask<ArrangeItem>;
|
||||
extern template struct FillBedTask<ArrangeItem>;
|
||||
extern template struct MultiplySelectionTask<ArrangeItem>;
|
||||
extern template class Arranger<ArrangeItem>;
|
||||
|
||||
}} // namespace Slic3r::arr2
|
||||
|
||||
#endif // ARRANGEITEM_HPP
|
||||
137
src/libslic3r/Arrange/Items/MutableItemTraits.hpp
Normal file
137
src/libslic3r/Arrange/Items/MutableItemTraits.hpp
Normal file
@@ -0,0 +1,137 @@
|
||||
|
||||
#ifndef MutableItemTraits_HPP
|
||||
#define MutableItemTraits_HPP
|
||||
|
||||
#include "libslic3r/Arrange/Core/ArrangeItemTraits.hpp"
|
||||
#include "libslic3r/Arrange/Core/DataStoreTraits.hpp"
|
||||
|
||||
#include "libslic3r/ExPolygon.hpp"
|
||||
|
||||
namespace Slic3r { namespace arr2 {
|
||||
|
||||
template<class Itm> struct IsMutableItem_ : public std::false_type
|
||||
{};
|
||||
|
||||
// Using this interface to set up any arrange item. Provides default
|
||||
// implementation but it needs to be explicitly switched on with
|
||||
// IsMutableItem_ or completely reimplement a specialization.
|
||||
template<class Itm, class En = void> struct MutableItemTraits_
|
||||
{
|
||||
static_assert(IsMutableItem_<Itm>::value, "Not a Writable item type!");
|
||||
|
||||
static void set_priority(Itm &itm, int p) { itm.set_priority(p); }
|
||||
|
||||
static void set_convex_shape(Itm &itm, const Polygon &shape)
|
||||
{
|
||||
itm.set_convex_shape(shape);
|
||||
}
|
||||
|
||||
static void set_shape(Itm &itm, const ExPolygons &shape)
|
||||
{
|
||||
itm.set_shape(shape);
|
||||
}
|
||||
|
||||
static void set_convex_envelope(Itm &itm, const Polygon &envelope)
|
||||
{
|
||||
itm.set_convex_envelope(envelope);
|
||||
}
|
||||
|
||||
static void set_envelope(Itm &itm, const ExPolygons &envelope)
|
||||
{
|
||||
itm.set_envelope(envelope);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
static void set_arbitrary_data(Itm &itm, const std::string &key, T &&data)
|
||||
{
|
||||
if constexpr (IsWritableDataStore<Itm>)
|
||||
set_data(itm, key, std::forward<T>(data));
|
||||
}
|
||||
|
||||
static void set_allowed_rotations(Itm &itm,
|
||||
const std::vector<double> &rotations)
|
||||
{
|
||||
itm.set_allowed_rotations(rotations);
|
||||
}
|
||||
};
|
||||
|
||||
template<class T>
|
||||
using MutableItemTraits = MutableItemTraits_<StripCVRef<T>>;
|
||||
|
||||
template<class T> constexpr bool IsMutableItem = IsMutableItem_<T>::value;
|
||||
template<class T, class TT = T>
|
||||
using MutableItemOnly = std::enable_if_t<IsMutableItem<T>, TT>;
|
||||
|
||||
template<class Itm> void set_priority(Itm &itm, int p)
|
||||
{
|
||||
MutableItemTraits<Itm>::set_priority(itm, p);
|
||||
}
|
||||
|
||||
template<class Itm> void set_convex_shape(Itm &itm, const Polygon &shape)
|
||||
{
|
||||
MutableItemTraits<Itm>::set_convex_shape(itm, shape);
|
||||
}
|
||||
|
||||
template<class Itm> void set_shape(Itm &itm, const ExPolygons &shape)
|
||||
{
|
||||
MutableItemTraits<Itm>::set_shape(itm, shape);
|
||||
}
|
||||
|
||||
template<class Itm>
|
||||
void set_convex_envelope(Itm &itm, const Polygon &envelope)
|
||||
{
|
||||
MutableItemTraits<Itm>::set_convex_envelope(itm, envelope);
|
||||
}
|
||||
|
||||
template<class Itm> void set_envelope(Itm &itm, const ExPolygons &envelope)
|
||||
{
|
||||
MutableItemTraits<Itm>::set_envelope(itm, envelope);
|
||||
}
|
||||
|
||||
template<class T, class Itm>
|
||||
void set_arbitrary_data(Itm &itm, const std::string &key, T &&data)
|
||||
{
|
||||
MutableItemTraits<Itm>::set_arbitrary_data(itm, key, std::forward<T>(data));
|
||||
}
|
||||
|
||||
template<class Itm>
|
||||
void set_allowed_rotations(Itm &itm, const std::vector<double> &rotations)
|
||||
{
|
||||
MutableItemTraits<Itm>::set_allowed_rotations(itm, rotations);
|
||||
}
|
||||
|
||||
template<class ArrItem> int raise_priority(ArrItem &itm)
|
||||
{
|
||||
int ret = get_priority(itm) + 1;
|
||||
set_priority(itm, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<class ArrItem> int reduce_priority(ArrItem &itm)
|
||||
{
|
||||
int ret = get_priority(itm) - 1;
|
||||
set_priority(itm, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<class It> int lowest_priority(const Range<It> &item_range)
|
||||
{
|
||||
auto minp_it = std::min_element(item_range.begin(),
|
||||
item_range.end(),
|
||||
[](auto &itm1, auto &itm2) {
|
||||
return get_priority(itm1) <
|
||||
get_priority(itm2);
|
||||
});
|
||||
|
||||
int min_priority = 0;
|
||||
if (minp_it != item_range.end())
|
||||
min_priority = get_priority(*minp_it);
|
||||
|
||||
return min_priority;
|
||||
}
|
||||
|
||||
}} // namespace Slic3r::arr2
|
||||
|
||||
#endif // MutableItemTraits_HPP
|
||||
25
src/libslic3r/Arrange/Items/SimpleArrangeItem.cpp
Normal file
25
src/libslic3r/Arrange/Items/SimpleArrangeItem.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
#include "SimpleArrangeItem.hpp"
|
||||
#include "libslic3r/Arrange/ArrangeImpl.hpp"
|
||||
#include "libslic3r/Arrange/Tasks/ArrangeTaskImpl.hpp"
|
||||
#include "libslic3r/Arrange/Tasks/FillBedTaskImpl.hpp"
|
||||
#include "libslic3r/Arrange/Tasks/MultiplySelectionTaskImpl.hpp"
|
||||
|
||||
namespace Slic3r { namespace arr2 {
|
||||
|
||||
Polygon SimpleArrangeItem::outline() const
|
||||
{
|
||||
Polygon ret = shape();
|
||||
ret.rotate(m_rotation);
|
||||
ret.translate(m_translation);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
template class ArrangeableToItemConverter<SimpleArrangeItem>;
|
||||
template struct ArrangeTask<SimpleArrangeItem>;
|
||||
template struct FillBedTask<SimpleArrangeItem>;
|
||||
template struct MultiplySelectionTask<SimpleArrangeItem>;
|
||||
template class Arranger<SimpleArrangeItem>;
|
||||
|
||||
}} // namespace Slic3r::arr2
|
||||
219
src/libslic3r/Arrange/Items/SimpleArrangeItem.hpp
Normal file
219
src/libslic3r/Arrange/Items/SimpleArrangeItem.hpp
Normal file
@@ -0,0 +1,219 @@
|
||||
|
||||
#ifndef SIMPLEARRANGEITEM_HPP
|
||||
#define SIMPLEARRANGEITEM_HPP
|
||||
|
||||
#include "libslic3r/Arrange/Core/PackingContext.hpp"
|
||||
|
||||
#include "libslic3r/Arrange/Core/NFP/NFPArrangeItemTraits.hpp"
|
||||
#include "libslic3r/Arrange/Core/NFP/NFP.hpp"
|
||||
|
||||
#include "libslic3r/Arrange/Arrange.hpp"
|
||||
#include "libslic3r/Arrange/Tasks/ArrangeTask.hpp"
|
||||
#include "libslic3r/Arrange/Tasks/FillBedTask.hpp"
|
||||
#include "libslic3r/Arrange/Tasks/MultiplySelectionTask.hpp"
|
||||
|
||||
#include "libslic3r/Polygon.hpp"
|
||||
#include "libslic3r/Geometry/ConvexHull.hpp"
|
||||
|
||||
#include "MutableItemTraits.hpp"
|
||||
|
||||
namespace Slic3r { namespace arr2 {
|
||||
|
||||
class SimpleArrangeItem {
|
||||
Polygon m_shape;
|
||||
|
||||
Vec2crd m_translation = Vec2crd::Zero();
|
||||
double m_rotation = 0.;
|
||||
int m_priority = 0;
|
||||
int m_bed_idx = Unarranged;
|
||||
|
||||
std::vector<double> m_allowed_rotations = {0.};
|
||||
ObjectID m_obj_id;
|
||||
|
||||
public:
|
||||
explicit SimpleArrangeItem(Polygon chull = {}): m_shape{std::move(chull)} {}
|
||||
|
||||
void set_shape(Polygon chull) { m_shape = std::move(chull); }
|
||||
|
||||
const Vec2crd& get_translation() const noexcept { return m_translation; }
|
||||
double get_rotation() const noexcept { return m_rotation; }
|
||||
int get_priority() const noexcept { return m_priority; }
|
||||
int get_bed_index() const noexcept { return m_bed_idx; }
|
||||
|
||||
void set_translation(const Vec2crd &v) { m_translation = v; }
|
||||
void set_rotation(double v) noexcept { m_rotation = v; }
|
||||
void set_priority(int v) noexcept { m_priority = v; }
|
||||
void set_bed_index(int v) noexcept { m_bed_idx = v; }
|
||||
|
||||
const Polygon &shape() const { return m_shape; }
|
||||
Polygon outline() const;
|
||||
|
||||
const auto &allowed_rotations() const noexcept
|
||||
{
|
||||
return m_allowed_rotations;
|
||||
}
|
||||
|
||||
void set_allowed_rotations(std::vector<double> rots)
|
||||
{
|
||||
m_allowed_rotations = std::move(rots);
|
||||
}
|
||||
|
||||
void set_object_id(const ObjectID &id) noexcept { m_obj_id = id; }
|
||||
const ObjectID & get_object_id() const noexcept { return m_obj_id; }
|
||||
};
|
||||
|
||||
template<> struct NFPArrangeItemTraits_<SimpleArrangeItem>
|
||||
{
|
||||
template<class Context, class Bed, class StopCond>
|
||||
static ExPolygons calculate_nfp(const SimpleArrangeItem &item,
|
||||
const Context &packing_context,
|
||||
const Bed &bed,
|
||||
StopCond &&stop_cond)
|
||||
{
|
||||
auto fixed_items = all_items_range(packing_context);
|
||||
auto nfps = reserve_polygons(fixed_items.size());
|
||||
for (const SimpleArrangeItem &fixed_part : fixed_items) {
|
||||
Polygon subnfp = nfp_convex_convex_legacy(fixed_part.outline(),
|
||||
item.outline());
|
||||
nfps.emplace_back(subnfp);
|
||||
|
||||
|
||||
if (stop_cond()) {
|
||||
nfps.clear();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ExPolygons nfp_ex;
|
||||
if (!stop_cond()) {
|
||||
if constexpr (!std::is_convertible_v<Bed, InfiniteBed>) {
|
||||
ExPolygons ifpbed = ifp_convex(bed, item.outline());
|
||||
nfp_ex = diff_ex(ifpbed, nfps);
|
||||
} else {
|
||||
nfp_ex = union_ex(nfps);
|
||||
}
|
||||
}
|
||||
|
||||
return nfp_ex;
|
||||
}
|
||||
|
||||
static Vec2crd reference_vertex(const SimpleArrangeItem &item)
|
||||
{
|
||||
return Slic3r::reference_vertex(item.outline());
|
||||
}
|
||||
|
||||
static BoundingBox envelope_bounding_box(const SimpleArrangeItem &itm)
|
||||
{
|
||||
return get_extents(itm.outline());
|
||||
}
|
||||
|
||||
static BoundingBox fixed_bounding_box(const SimpleArrangeItem &itm)
|
||||
{
|
||||
return get_extents(itm.outline());
|
||||
}
|
||||
|
||||
static Polygons envelope_outline(const SimpleArrangeItem &itm)
|
||||
{
|
||||
return {itm.outline()};
|
||||
}
|
||||
|
||||
static Polygons fixed_outline(const SimpleArrangeItem &itm)
|
||||
{
|
||||
return {itm.outline()};
|
||||
}
|
||||
|
||||
static Polygon envelope_convex_hull(const SimpleArrangeItem &itm)
|
||||
{
|
||||
return Geometry::convex_hull(itm.outline());
|
||||
}
|
||||
|
||||
static Polygon fixed_convex_hull(const SimpleArrangeItem &itm)
|
||||
{
|
||||
return Geometry::convex_hull(itm.outline());
|
||||
}
|
||||
|
||||
static double envelope_area(const SimpleArrangeItem &itm)
|
||||
{
|
||||
return itm.shape().area();
|
||||
}
|
||||
|
||||
static double fixed_area(const SimpleArrangeItem &itm)
|
||||
{
|
||||
return itm.shape().area();
|
||||
}
|
||||
|
||||
static const auto& allowed_rotations(const SimpleArrangeItem &itm) noexcept
|
||||
{
|
||||
return itm.allowed_rotations();
|
||||
}
|
||||
|
||||
static Vec2crd fixed_centroid(const SimpleArrangeItem &itm) noexcept
|
||||
{
|
||||
return itm.outline().centroid();
|
||||
}
|
||||
|
||||
static Vec2crd envelope_centroid(const SimpleArrangeItem &itm) noexcept
|
||||
{
|
||||
return itm.outline().centroid();
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct IsMutableItem_<SimpleArrangeItem>: public std::true_type {};
|
||||
|
||||
template<>
|
||||
struct MutableItemTraits_<SimpleArrangeItem> {
|
||||
|
||||
static void set_priority(SimpleArrangeItem &itm, int p) { itm.set_priority(p); }
|
||||
static void set_convex_shape(SimpleArrangeItem &itm, const Polygon &shape)
|
||||
{
|
||||
itm.set_shape(shape);
|
||||
}
|
||||
static void set_shape(SimpleArrangeItem &itm, const ExPolygons &shape)
|
||||
{
|
||||
itm.set_shape(Geometry::convex_hull(shape));
|
||||
}
|
||||
static void set_convex_envelope(SimpleArrangeItem &itm, const Polygon &envelope)
|
||||
{
|
||||
itm.set_shape(envelope);
|
||||
}
|
||||
static void set_envelope(SimpleArrangeItem &itm, const ExPolygons &envelope)
|
||||
{
|
||||
itm.set_shape(Geometry::convex_hull(envelope));
|
||||
}
|
||||
|
||||
template<class T>
|
||||
static void set_data(SimpleArrangeItem &itm, const std::string &key, T &&data)
|
||||
{}
|
||||
|
||||
static void set_allowed_rotations(SimpleArrangeItem &itm, const std::vector<double> &rotations)
|
||||
{
|
||||
itm.set_allowed_rotations(rotations);
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct ImbueableItemTraits_<SimpleArrangeItem>
|
||||
{
|
||||
static void imbue_id(SimpleArrangeItem &itm, const ObjectID &id)
|
||||
{
|
||||
itm.set_object_id(id);
|
||||
}
|
||||
|
||||
static std::optional<ObjectID> retrieve_id(const SimpleArrangeItem &itm)
|
||||
{
|
||||
std::optional<ObjectID> ret;
|
||||
if (itm.get_object_id().valid())
|
||||
ret = itm.get_object_id();
|
||||
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
extern template class ArrangeableToItemConverter<SimpleArrangeItem>;
|
||||
extern template struct ArrangeTask<SimpleArrangeItem>;
|
||||
extern template struct FillBedTask<SimpleArrangeItem>;
|
||||
extern template struct MultiplySelectionTask<SimpleArrangeItem>;
|
||||
extern template class Arranger<SimpleArrangeItem>;
|
||||
|
||||
}} // namespace Slic3r::arr2
|
||||
|
||||
#endif // SIMPLEARRANGEITEM_HPP
|
||||
80
src/libslic3r/Arrange/Items/TrafoOnlyArrangeItem.hpp
Normal file
80
src/libslic3r/Arrange/Items/TrafoOnlyArrangeItem.hpp
Normal file
@@ -0,0 +1,80 @@
|
||||
|
||||
#ifndef TRAFOONLYARRANGEITEM_HPP
|
||||
#define TRAFOONLYARRANGEITEM_HPP
|
||||
|
||||
#include "libslic3r/Arrange/Core/ArrangeItemTraits.hpp"
|
||||
|
||||
#include "libslic3r/Arrange/Items/ArbitraryDataStore.hpp"
|
||||
#include "libslic3r/Arrange/Items/MutableItemTraits.hpp"
|
||||
|
||||
namespace Slic3r { namespace arr2 {
|
||||
|
||||
class TrafoOnlyArrangeItem {
|
||||
int m_bed_idx = Unarranged;
|
||||
int m_priority = 0;
|
||||
Vec2crd m_translation = Vec2crd::Zero();
|
||||
double m_rotation = 0.;
|
||||
|
||||
ArbitraryDataStore m_datastore;
|
||||
|
||||
public:
|
||||
TrafoOnlyArrangeItem() = default;
|
||||
|
||||
template<class ArrItm>
|
||||
explicit TrafoOnlyArrangeItem(const ArrItm &other)
|
||||
: m_bed_idx{arr2::get_bed_index(other)},
|
||||
m_priority{arr2::get_priority(other)},
|
||||
m_translation(arr2::get_translation(other)),
|
||||
m_rotation{arr2::get_rotation(other)}
|
||||
{}
|
||||
|
||||
const Vec2crd& get_translation() const noexcept { return m_translation; }
|
||||
double get_rotation() const noexcept { return m_rotation; }
|
||||
int get_bed_index() const noexcept { return m_bed_idx; }
|
||||
int get_priority() const noexcept { return m_priority; }
|
||||
|
||||
const ArbitraryDataStore &datastore() const noexcept { return m_datastore; }
|
||||
ArbitraryDataStore &datastore() { return m_datastore; }
|
||||
};
|
||||
|
||||
template<> struct DataStoreTraits_<TrafoOnlyArrangeItem>
|
||||
{
|
||||
static constexpr bool Implemented = true;
|
||||
|
||||
template<class T>
|
||||
static const T *get(const TrafoOnlyArrangeItem &itm, const std::string &key)
|
||||
{
|
||||
return itm.datastore().get<T>(key);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
static T *get(TrafoOnlyArrangeItem &itm, const std::string &key)
|
||||
{
|
||||
return itm.datastore().get<T>(key);
|
||||
}
|
||||
|
||||
static bool has_key(const TrafoOnlyArrangeItem &itm, const std::string &key)
|
||||
{
|
||||
return itm.datastore().has_key(key);
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct IsMutableItem_<TrafoOnlyArrangeItem>: public std::true_type {};
|
||||
|
||||
template<> struct WritableDataStoreTraits_<TrafoOnlyArrangeItem>
|
||||
{
|
||||
static constexpr bool Implemented = true;
|
||||
|
||||
template<class T>
|
||||
static void set(TrafoOnlyArrangeItem &itm,
|
||||
const std::string &key,
|
||||
T &&data)
|
||||
{
|
||||
set_data(itm.datastore(), key, std::forward<T>(data));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace arr2
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // TRAFOONLYARRANGEITEM_HPP
|
||||
Reference in New Issue
Block a user