update test

This commit is contained in:
QIDI TECH
2025-02-10 15:31:36 +08:00
parent 7529de7fe1
commit 748e5f2db2
76 changed files with 9796 additions and 99 deletions

View File

@@ -0,0 +1,268 @@
#ifndef ARRANGE2_HPP
#define ARRANGE2_HPP
#include <libslic3r/MinAreaBoundingBox.hpp>
#include <arrange/NFP/NFPArrangeItemTraits.hpp>
#include "Scene.hpp"
#include "Items/MutableItemTraits.hpp"
namespace Slic3r { namespace arr2 {
template<class ArrItem> class Arranger
{
public:
class Ctl : public ArrangeTaskCtl {
public:
virtual void on_packed(ArrItem &item) {};
};
virtual ~Arranger() = default;
virtual void arrange(std::vector<ArrItem> &items,
const std::vector<ArrItem> &fixed,
const ExtendedBed &bed,
Ctl &ctl) = 0;
void arrange(std::vector<ArrItem> &items,
const std::vector<ArrItem> &fixed,
const ExtendedBed &bed,
ArrangeTaskCtl &ctl);
void arrange(std::vector<ArrItem> &items,
const std::vector<ArrItem> &fixed,
const ExtendedBed &bed,
Ctl &&ctl)
{
arrange(items, fixed, bed, ctl);
}
void arrange(std::vector<ArrItem> &items,
const std::vector<ArrItem> &fixed,
const ExtendedBed &bed,
ArrangeTaskCtl &&ctl)
{
arrange(items, fixed, bed, ctl);
}
static std::unique_ptr<Arranger> create(const ArrangeSettingsView &settings);
};
template<class ArrItem> using ArrangerCtl = typename Arranger<ArrItem>::Ctl;
template<class ArrItem>
class DefaultArrangerCtl : public Arranger<ArrItem>::Ctl {
ArrangeTaskCtl *taskctl = nullptr;
public:
DefaultArrangerCtl() = default;
explicit DefaultArrangerCtl(ArrangeTaskCtl &ctl) : taskctl{&ctl} {}
void update_status(int st) override
{
if (taskctl)
taskctl->update_status(st);
}
bool was_canceled() const override
{
if (taskctl)
return taskctl->was_canceled();
return false;
}
};
template<class ArrItem>
void Arranger<ArrItem>::arrange(std::vector<ArrItem> &items,
const std::vector<ArrItem> &fixed,
const ExtendedBed &bed,
ArrangeTaskCtl &ctl)
{
arrange(items, fixed, bed, DefaultArrangerCtl<ArrItem>{ctl});
}
class EmptyItemOutlineError: public std::exception {
static constexpr const char *Msg = "No outline can be derived for object";
public:
const char* what() const noexcept override { return Msg; }
};
template<class ArrItem> class ArrangeableToItemConverter
{
public:
virtual ~ArrangeableToItemConverter() = default;
// May throw EmptyItemOutlineError
virtual ArrItem convert(const Arrangeable &arrbl, coord_t offs = 0) const = 0;
// Returns the extent of simplification that the converter utilizes when
// creating arrange items. Zero shall mean no simplification at all.
virtual coord_t simplification_tolerance() const { return 0; }
static std::unique_ptr<ArrangeableToItemConverter> create(
ArrangeSettingsView::GeometryHandling geometry_handling,
coord_t safety_d);
static std::unique_ptr<ArrangeableToItemConverter> create(
const Scene &sc)
{
return create(sc.settings().get_geometry_handling(),
scaled(sc.settings().get_distance_from_objects()));
}
};
template<class DStore, class = WritableDataStoreOnly<DStore>>
class AnyWritableDataStore: public AnyWritable
{
DStore &dstore;
public:
AnyWritableDataStore(DStore &store): dstore{store} {}
void write(std::string_view key, std::any d) override
{
set_data(dstore, std::string{key}, std::move(d));
}
};
template<class ArrItem>
class BasicItemConverter : public ArrangeableToItemConverter<ArrItem>
{
coord_t m_safety_d;
coord_t m_simplify_tol;
public:
BasicItemConverter(coord_t safety_d = 0, coord_t simpl_tol = 0)
: m_safety_d{safety_d}, m_simplify_tol{simpl_tol}
{}
coord_t safety_dist() const noexcept { return m_safety_d; }
coord_t simplification_tolerance() const override
{
return m_simplify_tol;
}
};
template<class ArrItem>
class ConvexItemConverter : public BasicItemConverter<ArrItem>
{
public:
using BasicItemConverter<ArrItem>::BasicItemConverter;
ArrItem convert(const Arrangeable &arrbl, coord_t offs) const override;
};
template<class ArrItem>
class AdvancedItemConverter : public BasicItemConverter<ArrItem>
{
protected:
virtual ArrItem get_arritem(const Arrangeable &arrbl, coord_t eps) const;
public:
using BasicItemConverter<ArrItem>::BasicItemConverter;
ArrItem convert(const Arrangeable &arrbl, coord_t offs) const override;
};
template<class ArrItem>
class BalancedItemConverter : public AdvancedItemConverter<ArrItem>
{
protected:
ArrItem get_arritem(const Arrangeable &arrbl, coord_t offs) const override;
public:
using AdvancedItemConverter<ArrItem>::AdvancedItemConverter;
};
template<class ArrItem, class En = void> struct ImbueableItemTraits_
{
static constexpr const char *Key = "object_id";
static void imbue_id(ArrItem &itm, const ObjectID &id)
{
set_arbitrary_data(itm, Key, id);
}
static std::optional<ObjectID> retrieve_id(const ArrItem &itm)
{
std::optional<ObjectID> ret;
auto idptr = get_data<const ObjectID>(itm, Key);
if (idptr)
ret = *idptr;
return ret;
}
};
template<class ArrItem>
using ImbueableItemTraits = ImbueableItemTraits_<StripCVRef<ArrItem>>;
template<class ArrItem>
void imbue_id(ArrItem &itm, const ObjectID &id)
{
ImbueableItemTraits<ArrItem>::imbue_id(itm, id);
}
template<class ArrItem>
std::optional<ObjectID> retrieve_id(const ArrItem &itm)
{
return ImbueableItemTraits<ArrItem>::retrieve_id(itm);
}
template<class ArrItem>
bool apply_arrangeitem(const ArrItem &itm, ArrangeableModel &mdl)
{
bool ret = false;
if (auto id = retrieve_id(itm)) {
mdl.visit_arrangeable(*id, [&itm, &ret](Arrangeable &arrbl) {
if ((ret = arrbl.assign_bed(get_bed_index(itm))))
arrbl.transform(unscaled(get_translation(itm)), get_rotation(itm));
});
}
return ret;
}
template<class ArrItem>
double get_min_area_bounding_box_rotation(const ArrItem &itm)
{
return MinAreaBoundigBox{envelope_convex_hull(itm),
MinAreaBoundigBox::pcConvex}
.angle_to_X();
}
template<class ArrItem>
double get_fit_into_bed_rotation(const ArrItem &itm, const RectangleBed &bed)
{
double ret = 0.;
auto bbsz = envelope_bounding_box(itm).size();
auto binbb = bounding_box(bed);
auto binbbsz = binbb.size();
if (bbsz.x() >= binbbsz.x() || bbsz.y() >= binbbsz.y())
ret = fit_into_box_rotation(envelope_convex_hull(itm), binbb);
return ret;
}
template<class ArrItem>
auto get_corrected_bed(const ExtendedBed &bed,
const ArrangeableToItemConverter<ArrItem> &converter)
{
auto bedcpy = bed;
visit_bed([tol = -converter.simplification_tolerance()](auto &rawbed) {
rawbed = offset(rawbed, tol);
}, bedcpy);
return bedcpy;
}
}} // namespace Slic3r::arr2
#endif // ARRANGE2_HPP

View File

@@ -0,0 +1,96 @@
#ifndef ARRANGESETTINGSDB_APPCFG_HPP
#define ARRANGESETTINGSDB_APPCFG_HPP
#include <string>
#include "ArrangeSettingsView.hpp"
#include "libslic3r/AppConfig.hpp"
#include "libslic3r/PrintConfig.hpp"
namespace Slic3r {
class AppConfig;
class ArrangeSettingsDb_AppCfg: public arr2::ArrangeSettingsDb
{
public:
enum Slots { slotFFF, slotFFFSeqPrint, slotSLA };
private:
AppConfig *m_appcfg;
Slots m_current_slot = slotFFF;
struct FloatRange { float minval = 0.f, maxval = 100.f; };
struct Slot
{
Values vals;
Values defaults;
FloatRange dobj_range, dbed_range;
std::string postfix;
};
// Settings and their defaults are stored separately for fff,
// sla and fff sequential mode
Slot m_settings_fff, m_settings_fff_seq, m_settings_sla;
template<class Self>
static auto & get_slot(Self *self, Slots slot) {
switch(slot) {
case slotFFF: return self->m_settings_fff;
case slotFFFSeqPrint: return self->m_settings_fff_seq;
case slotSLA: return self->m_settings_sla;
}
return self->m_settings_fff;
}
template<class Self> static auto &get_slot(Self *self)
{
return get_slot(self, self->m_current_slot);
}
template<class Self>
static auto& get_ref(Self *self) { return get_slot(self).vals; }
public:
explicit ArrangeSettingsDb_AppCfg(AppConfig *appcfg);
void sync();
float get_distance_from_objects() const override { return get_ref(this).d_obj; }
float get_distance_from_bed() const override { return get_ref(this).d_bed; }
bool is_rotation_enabled() const override { return get_ref(this).rotations; }
XLPivots get_xl_alignment() const override { return m_settings_fff.vals.xl_align; }
GeometryHandling get_geometry_handling() const override { return m_settings_fff.vals.geom_handling; }
ArrangeStrategy get_arrange_strategy() const override { return m_settings_fff.vals.arr_strategy; }
void distance_from_obj_range(float &min, float &max) const override;
void distance_from_bed_range(float &min, float &max) const override;
ArrangeSettingsDb& set_distance_from_objects(float v) override;
ArrangeSettingsDb& set_distance_from_bed(float v) override;
ArrangeSettingsDb& set_rotation_enabled(bool v) override;
ArrangeSettingsDb& set_xl_alignment(XLPivots v) override;
ArrangeSettingsDb& set_geometry_handling(GeometryHandling v) override;
ArrangeSettingsDb& set_arrange_strategy(ArrangeStrategy v) override;
Values get_defaults() const override { return get_slot(this).defaults; }
void set_active_slot(Slots slot) noexcept { m_current_slot = slot; }
void set_distance_from_obj_range(Slots slot, float min, float max)
{
get_slot(this, slot).dobj_range = FloatRange{min, max};
}
void set_distance_from_bed_range(Slots slot, float min, float max)
{
get_slot(this, slot).dbed_range = FloatRange{min, max};
}
Values &get_defaults(Slots slot) { return get_slot(this, slot).defaults; }
};
} // namespace Slic3r
#endif // ARRANGESETTINGSDB_APPCFG_HPP

View File

@@ -0,0 +1,234 @@
#ifndef ARRANGESETTINGSVIEW_HPP
#define ARRANGESETTINGSVIEW_HPP
#include <string_view>
#include <array>
#include "libslic3r/StaticMap.hpp"
namespace Slic3r { namespace arr2 {
using namespace std::string_view_literals;
class ArrangeSettingsView
{
public:
enum GeometryHandling { ghConvex, ghBalanced, ghAdvanced, ghCount };
enum ArrangeStrategy { asAuto, asPullToCenter, asCount };
enum XLPivots {
xlpCenter,
xlpRearLeft,
xlpFrontLeft,
xlpFrontRight,
xlpRearRight,
xlpRandom,
xlpCount
};
virtual ~ArrangeSettingsView() = default;
virtual float get_distance_from_objects() const = 0;
virtual float get_distance_from_bed() const = 0;
virtual bool is_rotation_enabled() const = 0;
virtual XLPivots get_xl_alignment() const = 0;
virtual GeometryHandling get_geometry_handling() const = 0;
virtual ArrangeStrategy get_arrange_strategy() const = 0;
static constexpr std::string_view get_label(GeometryHandling v)
{
constexpr auto STR = std::array{
"0"sv, // convex
"1"sv, // balanced
"2"sv, // advanced
"-1"sv, // undefined
};
return STR[v];
}
static constexpr std::string_view get_label(ArrangeStrategy v)
{
constexpr auto STR = std::array{
"0"sv, // auto
"1"sv, // pulltocenter
"-1"sv, // undefined
};
return STR[v];
}
static constexpr std::string_view get_label(XLPivots v)
{
constexpr auto STR = std::array{
"0"sv, // center
"1"sv, // rearleft
"2"sv, // frontleft
"3"sv, // frontright
"4"sv, // rearright
"5"sv, // random
"-1"sv, // undefined
};
return STR[v];
}
private:
template<class EnumType, size_t N>
using EnumMap = StaticMap<std::string_view, EnumType, N>;
template<class EnumType, size_t N>
static constexpr std::optional<EnumType> get_enumval(std::string_view str,
const EnumMap<EnumType, N> &emap)
{
std::optional<EnumType> ret;
if (auto v = query(emap, str); v.has_value()) {
ret = *v;
}
return ret;
}
public:
static constexpr std::optional<GeometryHandling> to_geometry_handling(std::string_view str)
{
return get_enumval(str, GeometryHandlingLabels);
}
static constexpr std::optional<ArrangeStrategy> to_arrange_strategy(std::string_view str)
{
return get_enumval(str, ArrangeStrategyLabels);
}
static constexpr std::optional<XLPivots> to_xl_pivots(std::string_view str)
{
return get_enumval(str, XLPivotsLabels);
}
private:
static constexpr const auto GeometryHandlingLabels = make_staticmap<std::string_view, GeometryHandling>({
{"convex"sv, ghConvex},
{"balanced"sv, ghBalanced},
{"advanced"sv, ghAdvanced},
{"0"sv, ghConvex},
{"1"sv, ghBalanced},
{"2"sv, ghAdvanced},
});
static constexpr const auto ArrangeStrategyLabels = make_staticmap<std::string_view, ArrangeStrategy>({
{"auto"sv, asAuto},
{"pulltocenter"sv, asPullToCenter},
{"0"sv, asAuto},
{"1"sv, asPullToCenter}
});
static constexpr const auto XLPivotsLabels = make_staticmap<std::string_view, XLPivots>({
{"center"sv, xlpCenter },
{"rearleft"sv, xlpRearLeft },
{"frontleft"sv, xlpFrontLeft },
{"frontright"sv, xlpFrontRight },
{"rearright"sv, xlpRearRight },
{"random"sv, xlpRandom },
{"0"sv, xlpCenter },
{"1"sv, xlpRearLeft },
{"2"sv, xlpFrontLeft },
{"3"sv, xlpFrontRight },
{"4"sv, xlpRearRight },
{"5"sv, xlpRandom }
});
};
class ArrangeSettingsDb: public ArrangeSettingsView
{
public:
virtual void distance_from_obj_range(float &min, float &max) const = 0;
virtual void distance_from_bed_range(float &min, float &max) const = 0;
virtual ArrangeSettingsDb& set_distance_from_objects(float v) = 0;
virtual ArrangeSettingsDb& set_distance_from_bed(float v) = 0;
virtual ArrangeSettingsDb& set_rotation_enabled(bool v) = 0;
virtual ArrangeSettingsDb& set_xl_alignment(XLPivots v) = 0;
virtual ArrangeSettingsDb& set_geometry_handling(GeometryHandling v) = 0;
virtual ArrangeSettingsDb& set_arrange_strategy(ArrangeStrategy v) = 0;
struct Values {
float d_obj = 6.f, d_bed = 0.f;
bool rotations = false;
XLPivots xl_align = XLPivots::xlpFrontLeft;
GeometryHandling geom_handling = GeometryHandling::ghConvex;
ArrangeStrategy arr_strategy = ArrangeStrategy::asAuto;
Values() = default;
Values(const ArrangeSettingsView &sv)
{
d_bed = sv.get_distance_from_bed();
d_obj = sv.get_distance_from_objects();
arr_strategy = sv.get_arrange_strategy();
geom_handling = sv.get_geometry_handling();
rotations = sv.is_rotation_enabled();
xl_align = sv.get_xl_alignment();
}
};
virtual Values get_defaults() const { return {}; }
ArrangeSettingsDb& set_from(const ArrangeSettingsView &sv)
{
set_distance_from_bed(sv.get_distance_from_bed());
set_distance_from_objects(sv.get_distance_from_objects());
set_arrange_strategy(sv.get_arrange_strategy());
set_geometry_handling(sv.get_geometry_handling());
set_rotation_enabled(sv.is_rotation_enabled());
set_xl_alignment(sv.get_xl_alignment());
return *this;
}
};
class ArrangeSettings: public Slic3r::arr2::ArrangeSettingsDb
{
ArrangeSettingsDb::Values m_v = {};
public:
explicit ArrangeSettings(
const ArrangeSettingsDb::Values &v = {})
: m_v{v}
{}
explicit ArrangeSettings(const ArrangeSettingsView &v)
: m_v{v}
{}
float get_distance_from_objects() const override { return m_v.d_obj; }
float get_distance_from_bed() const override { return m_v.d_bed; }
bool is_rotation_enabled() const override { return m_v.rotations; }
XLPivots get_xl_alignment() const override { return m_v.xl_align; }
GeometryHandling get_geometry_handling() const override { return m_v.geom_handling; }
ArrangeStrategy get_arrange_strategy() const override { return m_v.arr_strategy; }
void distance_from_obj_range(float &min, float &max) const override { min = 0.f; max = 100.f; }
void distance_from_bed_range(float &min, float &max) const override { min = 0.f; max = 100.f; }
ArrangeSettings& set_distance_from_objects(float v) override { m_v.d_obj = v; return *this; }
ArrangeSettings& set_distance_from_bed(float v) override { m_v.d_bed = v; return *this; }
ArrangeSettings& set_rotation_enabled(bool v) override { m_v.rotations = v; return *this; }
ArrangeSettings& set_xl_alignment(XLPivots v) override { m_v.xl_align = v; return *this; }
ArrangeSettings& set_geometry_handling(GeometryHandling v) override { m_v.geom_handling = v; return *this; }
ArrangeSettings& set_arrange_strategy(ArrangeStrategy v) override { m_v.arr_strategy = v; return *this; }
auto & values() const { return m_v; }
auto & values() { return m_v; }
};
}} // namespace Slic3r::arr2
#endif // ARRANGESETTINGSVIEW_HPP

View File

@@ -0,0 +1,91 @@
#ifndef ARBITRARYDATASTORE_HPP
#define ARBITRARYDATASTORE_HPP
#include <string>
#include <map>
#include <any>
#include <arrange/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

View File

@@ -0,0 +1,509 @@
#ifndef ARRANGEITEM_HPP
#define ARRANGEITEM_HPP
#include <boost/variant.hpp>
#include <libslic3r/ClipperUtils.hpp>
#include <assert.h>
#include <stddef.h>
#include <optional>
#include <algorithm>
#include <initializer_list>
#include <memory>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
#include <cassert>
#include <cstddef>
#include <libslic3r/ExPolygon.hpp>
#include <libslic3r/BoundingBox.hpp>
#include <libslic3r/AnyPtr.hpp>
#include <libslic3r/Point.hpp>
#include <libslic3r/Polygon.hpp>
#include <libslic3r/libslic3r.h>
#include <arrange/PackingContext.hpp>
#include <arrange/NFP/NFPArrangeItemTraits.hpp>
#include <arrange/NFP/NFP.hpp>
#include <arrange/ArrangeBase.hpp>
#include <arrange/ArrangeItemTraits.hpp>
#include <arrange/DataStoreTraits.hpp>
#include <arrange-wrapper/Items/MutableItemTraits.hpp>
#include <arrange-wrapper/Arrange.hpp>
#include <arrange-wrapper/Tasks/ArrangeTask.hpp>
#include <arrange-wrapper/Tasks/FillBedTask.hpp>
#include <arrange-wrapper/Tasks/MultiplySelectionTask.hpp>
#include <arrange-wrapper/Items/ArbitraryDataStore.hpp>
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<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
std::optional<int> 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<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; }
std::optional<int> 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<int> 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_<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();
}
static std::optional<int> 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<int> v)
{
itm.bed_constraint(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

View File

@@ -0,0 +1,136 @@
#ifndef MutableItemTraits_HPP
#define MutableItemTraits_HPP
#include <libslic3r/ExPolygon.hpp>
#include <arrange/ArrangeItemTraits.hpp>
#include <arrange/DataStoreTraits.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

View File

@@ -0,0 +1,233 @@
#ifndef SIMPLEARRANGEITEM_HPP
#define SIMPLEARRANGEITEM_HPP
#include <optional>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
#include <libslic3r/Polygon.hpp>
#include <libslic3r/Geometry/ConvexHull.hpp>
#include <libslic3r/BoundingBox.hpp>
#include <libslic3r/ClipperUtils.hpp>
#include <libslic3r/ObjectID.hpp>
#include <libslic3r/Point.hpp>
#include <arrange/ArrangeItemTraits.hpp>
#include <arrange/PackingContext.hpp>
#include <arrange/NFP/NFPArrangeItemTraits.hpp>
#include <arrange/NFP/NFP.hpp>
#include <arrange-wrapper/Arrange.hpp>
#include <arrange-wrapper/Tasks/FillBedTask.hpp>
#include <arrange-wrapper/Tasks/ArrangeTask.hpp>
#include <arrange-wrapper/Items/MutableItemTraits.hpp>
namespace Slic3r { namespace arr2 {
struct InfiniteBed;
class SimpleArrangeItem {
Polygon m_shape;
Vec2crd m_translation = Vec2crd::Zero();
double m_rotation = 0.;
int m_priority = 0;
int m_bed_idx = Unarranged;
std::optional<int> m_bed_constraint;
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; }
std::optional<int> get_bed_constraint() const noexcept {
return m_bed_constraint;
}
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; }
void set_bed_constraint(std::optional<int> v) noexcept { m_bed_constraint = 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

View File

@@ -0,0 +1,82 @@
#ifndef TRAFOONLYARRANGEITEM_HPP
#define TRAFOONLYARRANGEITEM_HPP
#include <arrange/ArrangeItemTraits.hpp>
#include "ArbitraryDataStore.hpp"
#include "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.;
std::optional<int> m_bed_constraint;
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)},
m_bed_constraint{arr2::get_bed_constraint(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; }
std::optional<int> get_bed_constraint() const noexcept { return m_bed_constraint; }
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

View File

@@ -0,0 +1,41 @@
#ifndef MODELARRANGE_HPP
#define MODELARRANGE_HPP
#include <stddef.h>
#include <vector>
#include <cstddef>
#include <arrange/Beds.hpp>
#include "Scene.hpp"
namespace Slic3r {
class Model;
class ModelInstance;
namespace arr2 {
class ArrangeSettingsView;
} // namespace arr2
using ModelInstancePtrs = std::vector<ModelInstance*>;
//void duplicate(Model &model, ArrangePolygons &copies, VirtualBedFn);
void duplicate_objects(Model &model, size_t copies_num);
bool arrange_objects(Model &model,
const arr2::ArrangeBed &bed,
const arr2::ArrangeSettingsView &settings);
void duplicate_objects(Model & model,
size_t copies_num,
const arr2::ArrangeBed &bed,
const arr2::ArrangeSettingsView &settings);
void duplicate(Model & model,
size_t copies_num,
const arr2::ArrangeBed &bed,
const arr2::ArrangeSettingsView &settings);
} // namespace Slic3r
#endif // MODELARRANGE_HPP

View File

@@ -0,0 +1,440 @@
#ifndef ARR2_SCENE_HPP
#define ARR2_SCENE_HPP
#include <stddef.h>
#include <boost/variant.hpp>
#include <boost/variant/variant.hpp>
#include <any>
#include <string_view>
#include <algorithm>
#include <functional>
#include <memory>
#include <set>
#include <type_traits>
#include <utility>
#include <vector>
#include <cstddef>
#include <libslic3r/ObjectID.hpp>
#include <libslic3r/AnyPtr.hpp>
#include <libslic3r/BoundingBox.hpp>
#include <libslic3r/ExPolygon.hpp>
#include <libslic3r/Point.hpp>
#include <libslic3r/Polygon.hpp>
#include <libslic3r/libslic3r.h>
#include <arrange/Beds.hpp>
#include "ArrangeSettingsView.hpp"
#include "SegmentedRectangleBed.hpp"
namespace Slic3r { namespace arr2 {
// This module contains all the necessary high level interfaces for
// arrangement. No dependency on the rest of libslic3r is intoduced here. (No
// Model, ModelObject, etc...) except for ObjectID.
// An interface that allows to store arbitrary data (std::any) under a specific
// key in an object implementing the interface. This is later used to pass
// arbitrary parameters from any arrangeable object down to the arrangement core.
class AnyWritable
{
public:
virtual ~AnyWritable() = default;
virtual void write(std::string_view key, std::any d) = 0;
};
// The interface that captures the objects which are actually moved around.
// Implementations must provide means to extract the 2D outline that is used
// by the arrangement core.
class Arrangeable
{
public:
virtual ~Arrangeable() = default;
// ID is implementation specific, must uniquely identify an Arrangeable
// object.
virtual ObjectID id() const = 0;
// This is different than id(), and identifies an underlying group into
// which the Arrangeable belongs. Can be used to group arrangeables sharing
// the same outline.
virtual ObjectID geometry_id() const = 0;
// Outline extraction can be a demanding operation, so there is a separate
// method the extract the full outline of an object and the convex hull only
// It will depend on the arrangement config to choose which one is called.
// convex_outline might be considerably faster than calling full_outline()
// and then calculating the convex hull from that.
virtual ExPolygons full_outline() const = 0;
virtual Polygon convex_outline() const = 0;
// Envelope is the boundary that an arrangeble object might have which
// is used when the object is being placed or moved around. Once it is
// placed, the outline (convex or full) will be used to determine the
// boundaries instead of the envelope. This concept can be used to
// implement arranging objects with support structures that can overlap,
// but never touch the actual object. In this case, full envelope would
// return the silhouette of the object with supports (pad, brim, etc...) and
// outline would be the actual object boundary.
virtual ExPolygons full_envelope() const { return {}; }
virtual Polygon convex_envelope() const { return {}; }
// Write the transformations determined by the arrangement into the object
virtual void transform(const Vec2d &transl, double rot) = 0;
// An arrangeable can be printable or unprintable, they should not be on
// the same bed. (See arrange tasks)
virtual bool is_printable() const { return true; }
// An arrangeable can be selected or not, this will determine if treated
// as static objects or movable ones.
virtual bool is_selected() const { return true; }
// Determines the order in which the objects are arranged. Higher priority
// objects are arranged first.
virtual int priority() const { return 0; }
virtual std::optional<int> bed_constraint() const { return std::nullopt; }
// Any implementation specific properties can be passed to the arrangement
// core by overriding this method. This implies that the specific Arranger
// will be able to interpret these properties. An example usage is to mark
// special objects (like a wipe tower)
virtual void imbue_data(AnyWritable &datastore) const {}
// for convinience to pass an AnyWritable created in the same expression
// as the method call
void imbue_data(AnyWritable &&datastore) const { imbue_data(datastore); }
// An Arrangeable might reside on a logical bed instead of the real one
// in case that the arrangement can not fit it onto the real bed. Handling
// of logical beds is also implementation specific and are specified with
// the next two methods:
// Returns the bed index on which the given Arrangeable is sitting.
virtual int get_bed_index() const = 0;
// Assign the Arrangeable to the given bed index. Note that this
// method can return false, indicating that the given bed is not available
// to be occupied.
virtual bool assign_bed(int bed_idx) = 0;
};
// Arrangeable objects are provided by an ArrangeableModel which is also able to
// create new arrangeables given a prototype id to copy.
class ArrangeableModel
{
public:
virtual ~ArrangeableModel() = default;
// Visit all arrangeable in this model and call the provided visitor
virtual void for_each_arrangeable(std::function<void(Arrangeable &)>) = 0;
virtual void for_each_arrangeable(std::function<void(const Arrangeable&)>) const = 0;
// Visit a specific arrangeable identified by it's id
virtual void visit_arrangeable(const ObjectID &id, std::function<void(const Arrangeable &)>) const = 0;
virtual void visit_arrangeable(const ObjectID &id, std::function<void(Arrangeable &)>) = 0;
// Add a new arrangeable which is a copy of the one matching prototype_id
// Return the new object id or an invalid id if the new object was not
// created.
virtual ObjectID add_arrangeable(const ObjectID &prototype_id) = 0;
size_t arrangeable_count() const
{
size_t cnt = 0;
for_each_arrangeable([&cnt](auto &) { ++cnt; });
return cnt;
}
};
// The special bed type used by XL printers
using XLBed = SegmentedRectangleBed<std::integral_constant<size_t, 4>,
std::integral_constant<size_t, 4>>;
// ExtendedBed is a variant type holding all bed types supported by the
// arrange core and the additional XLBed
template<class... Args> struct ExtendedBed_
{
using Type =
boost::variant<XLBed, /* insert other types if needed*/ Args...>;
};
template<class... Args> struct ExtendedBed_<boost::variant<Args...>>
{
using Type = boost::variant<XLBed, Args...>;
};
using ExtendedBed = typename ExtendedBed_<ArrangeBed>::Type;
template<class BedFn> void visit_bed(BedFn &&fn, const ExtendedBed &bed)
{
boost::apply_visitor(fn, bed);
}
template<class BedFn> void visit_bed(BedFn &&fn, ExtendedBed &bed)
{
boost::apply_visitor(fn, bed);
}
inline BoundingBox bounding_box(const ExtendedBed &bed)
{
BoundingBox bedbb;
visit_bed([&bedbb](auto &rawbed) { bedbb = bounding_box(rawbed); }, bed);
return bedbb;
}
inline Vec2crd bed_gap(const ExtendedBed &bed)
{
Vec2crd gap;
visit_bed([&gap](auto &rawbed) { gap = bed_gap(rawbed); }, bed);
return gap;
}
class Scene;
// SceneBuilderBase is intended for Scene construction. A simple constructor
// is not enough here to capture all the possible ways of constructing a Scene.
// Subclasses of SceneBuilderBase can add more domain specific methods and
// overloads. An rvalue object of this class is handed over to the Scene
// constructor which can then establish itself using the provided builder.
// A little CRTP is used to implement fluent interface returning Subclass
// references.
template<class Subclass>
class SceneBuilderBase
{
protected:
AnyPtr<ArrangeableModel> m_arrangeable_model;
AnyPtr<const ArrangeSettingsView> m_settings;
ExtendedBed m_bed = arr2::InfiniteBed{};
coord_t m_brims_offs = 0;
coord_t m_skirt_offs = 0;
public:
virtual ~SceneBuilderBase() = default;
SceneBuilderBase() = default;
SceneBuilderBase(const SceneBuilderBase &) = delete;
SceneBuilderBase& operator=(const SceneBuilderBase &) = delete;
SceneBuilderBase(SceneBuilderBase &&) = default;
SceneBuilderBase& operator=(SceneBuilderBase &&) = default;
// All setters return an rvalue reference so that at the end, the
// build_scene method can be called fluently
Subclass &&set_arrange_settings(AnyPtr<const ArrangeSettingsView> settings)
{
m_settings = std::move(settings);
return std::move(static_cast<Subclass&>(*this));
}
Subclass &&set_arrange_settings(const ArrangeSettingsView &settings)
{
m_settings = std::make_unique<ArrangeSettings>(settings);
return std::move(static_cast<Subclass&>(*this));
}
Subclass &&set_bed(const Points &pts, const Vec2crd &gap)
{
m_bed = arr2::to_arrange_bed(pts, gap);
return std::move(static_cast<Subclass&>(*this));
}
Subclass && set_bed(const arr2::ArrangeBed &bed)
{
m_bed = bed;
return std::move(static_cast<Subclass&>(*this));
}
Subclass &&set_bed(const XLBed &bed)
{
m_bed = bed;
return std::move(static_cast<Subclass&>(*this));
}
Subclass &&set_arrangeable_model(AnyPtr<ArrangeableModel> model)
{
m_arrangeable_model = std::move(model);
return std::move(static_cast<Subclass&>(*this));
}
// Can only be called on an rvalue instance (hence the && at the end),
// the method will potentially move its content into sc
virtual void build_scene(Scene &sc) &&;
};
class BasicSceneBuilder: public SceneBuilderBase<BasicSceneBuilder> {};
// The Scene class captures all data needed to do an arrangement.
class Scene
{
template <class Sub> friend class SceneBuilderBase;
// These fields always need to be initialized to valid objects after
// construction of Scene which is ensured by the SceneBuilder
AnyPtr<ArrangeableModel> m_amodel;
AnyPtr<const ArrangeSettingsView> m_settings;
ExtendedBed m_bed;
public:
// Scene can only be built from an rvalue SceneBuilder whose content will
// potentially be moved to the constructed Scene object.
template<class Sub>
explicit Scene(SceneBuilderBase<Sub> &&bld)
{
std::move(bld).build_scene(*this);
}
const ArrangeableModel &model() const noexcept { return *m_amodel; }
ArrangeableModel &model() noexcept { return *m_amodel; }
const ArrangeSettingsView &settings() const noexcept { return *m_settings; }
template<class BedFn> void visit_bed(BedFn &&fn) const
{
arr2::visit_bed(fn, m_bed);
}
const ExtendedBed & bed() const { return m_bed; }
std::vector<ObjectID> selected_ids() const;
};
// Get all the ObjectIDs of Arrangeables which are in selected state
std::set<ObjectID> selected_geometry_ids(const Scene &sc);
// A dummy, empty ArrangeableModel for testing and as placeholder to avoiod using nullptr
class EmptyArrangeableModel: public ArrangeableModel
{
public:
void for_each_arrangeable(std::function<void(Arrangeable &)>) override {}
void for_each_arrangeable(std::function<void(const Arrangeable&)>) const override {}
void visit_arrangeable(const ObjectID &id, std::function<void(const Arrangeable &)>) const override {}
void visit_arrangeable(const ObjectID &id, std::function<void(Arrangeable &)>) override {}
ObjectID add_arrangeable(const ObjectID &prototype_id) override { return {}; }
};
template<class Subclass>
void SceneBuilderBase<Subclass>::build_scene(Scene &sc) &&
{
if (!m_arrangeable_model)
m_arrangeable_model = std::make_unique<EmptyArrangeableModel>();
if (!m_settings)
m_settings = std::make_unique<arr2::ArrangeSettings>();
// Apply the bed minimum distance by making the original bed smaller
// and arranging on this smaller bed.
coord_t inset = std::max(scaled(m_settings->get_distance_from_bed()),
m_skirt_offs + m_brims_offs);
// Objects have also a minimum distance from each other implemented
// as inflation applied to object outlines. This object distance
// does not apply to the bed, so the bed is inflated by this amount
// to compensate.
coord_t md = scaled(m_settings->get_distance_from_objects());
md = md / 2 - inset;
// Applying the final bed with the corrected dimensions to account
// for safety distances
visit_bed([md](auto &rawbed) { rawbed = offset(rawbed, md); }, m_bed);
sc.m_settings = std::move(m_settings);
sc.m_amodel = std::move(m_arrangeable_model);
sc.m_bed = std::move(m_bed);
}
// Arrange tasks produce an object implementing this interface. The arrange
// result can be applied to an ArrangeableModel which may or may not succeed.
// The ArrangeableModel could be in a different state (it's objects may have
// changed or removed) than it was at the time of arranging.
class ArrangeResult
{
public:
virtual ~ArrangeResult() = default;
virtual bool apply_on(ArrangeableModel &mdlwt) = 0;
};
enum class Tasks { Arrange, FillBed };
class ArrangeTaskCtl
{
public:
virtual ~ArrangeTaskCtl() = default;
virtual void update_status(int st) = 0;
virtual bool was_canceled() const = 0;
};
class DummyCtl : public ArrangeTaskCtl
{
public:
void update_status(int) override {}
bool was_canceled() const override { return false; }
};
class ArrangeTaskBase
{
public:
using Ctl = ArrangeTaskCtl;
virtual ~ArrangeTaskBase() = default;
[[nodiscard]] virtual std::unique_ptr<ArrangeResult> process(Ctl &ctl) = 0;
[[nodiscard]] virtual int item_count_to_process() const = 0;
[[nodiscard]] static std::unique_ptr<ArrangeTaskBase> create(
Tasks task_type, const Scene &sc);
[[nodiscard]] std::unique_ptr<ArrangeResult> process(Ctl &&ctl)
{
return process(ctl);
}
[[nodiscard]] std::unique_ptr<ArrangeResult> process()
{
return process(DummyCtl{});
}
};
bool arrange(Scene &scene, ArrangeTaskCtl &ctl);
inline bool arrange(Scene &scene, ArrangeTaskCtl &&ctl = DummyCtl{})
{
return arrange(scene, ctl);
}
inline bool arrange(Scene &&scene, ArrangeTaskCtl &ctl)
{
return arrange(scene, ctl);
}
inline bool arrange(Scene &&scene, ArrangeTaskCtl &&ctl = DummyCtl{})
{
return arrange(scene, ctl);
}
template<class Builder, class Ctl = DummyCtl>
bool arrange(SceneBuilderBase<Builder> &&builder, Ctl &&ctl = {})
{
return arrange(Scene{std::move(builder)}, ctl);
}
} // namespace arr2
} // namespace Slic3r
#endif // ARR2_SCENE_HPP

View File

@@ -0,0 +1,722 @@
#ifndef SCENEBUILDER_HPP
#define SCENEBUILDER_HPP
#include <assert.h>
#include <stddef.h>
#include <algorithm>
#include <functional>
#include <initializer_list>
#include <memory>
#include <type_traits>
#include <utility>
#include <vector>
#include <cassert>
#include <cstddef>
#include <libslic3r/AnyPtr.hpp>
#include <libslic3r/BoundingBox.hpp>
#include <libslic3r/ExPolygon.hpp>
#include <libslic3r/Model.hpp>
#include <libslic3r/ObjectID.hpp>
#include <libslic3r/Point.hpp>
#include <libslic3r/Polygon.hpp>
#include <libslic3r/libslic3r.h>
#include <arrange/ArrangeItemTraits.hpp>
#include <arrange/Beds.hpp>
#include "Scene.hpp"
namespace Slic3r {
class Model;
class ModelInstance;
class ModelWipeTower;
class Print;
class SLAPrint;
class SLAPrintObject;
class PrintObject;
class DynamicPrintConfig;
namespace arr2 {
using SelectionPredicate = std::function<bool(int)>;
// Objects implementing this interface should know how to present the wipe tower
// as an Arrangeable. If the wipe tower is not present, the overloads of visit() shouldn't do
// anything. (See MissingWipeTowerHandler)
class WipeTowerHandler
{
public:
virtual ~WipeTowerHandler() = default;
virtual void visit(std::function<void(Arrangeable &)>) = 0;
virtual void visit(std::function<void(const Arrangeable &)>) const = 0;
virtual void set_selection_predicate(SelectionPredicate pred) = 0;
virtual ObjectID get_id() const = 0;
};
// Something that has a bounding box and can be displaced by arbitrary 2D offset and rotated
// by arbitrary rotation. Used as targets to place on virtual beds. Normally this would correspond
// to ModelInstances but the same functionality was needed in more contexts.
class VBedPlaceable {
public:
virtual ~VBedPlaceable() = default;
virtual BoundingBoxf bounding_box() const = 0;
virtual void displace(const Vec2d &transl, double rot) = 0;
};
// An interface to handle virtual beds for VBedPlaceable objects. A VBedPlaceable
// may be assigned to a logical bed identified by an integer index value (zero
// is the actual physical bed). The VBedPlaceable may still be outside of it's
// bed, regardless of being assigned to it. The handler object should provide
// means to read the assigned bed index of a VBedPlaceable, to assign a
// different bed index and to provide a trafo that maps it to the physical bed
// given a logical bed index. The reason is that the arrangement expects items
// to be in the coordinate system of the physical bed.
class VirtualBedHandler
{
public:
virtual ~VirtualBedHandler() = default;
// Returns the bed index on which the given VBedPlaceable is sitting.
virtual int get_bed_index(const VBedPlaceable &obj) const = 0;
// The returned trafo can be used to displace the VBedPlaceable
// to the coordinate system of the physical bed, should that differ from
// the coordinate space of a logical bed.
virtual Transform3d get_physical_bed_trafo(int bed_index) const = 0;
// Assign the VBedPlaceable to the given bed index. Note that this
// method can return false, indicating that the given bed is not available
// to be occupied (e.g. the handler has a limited amount of logical bed)
virtual bool assign_bed(VBedPlaceable &obj, int bed_idx) = 0;
bool assign_bed(VBedPlaceable &&obj, int bed_idx)
{
return assign_bed(obj, bed_idx);
}
static std::unique_ptr<VirtualBedHandler> create(const ExtendedBed &bed);
};
// Holds the info about which object (ID) is selected/unselected
class SelectionMask
{
public:
virtual ~SelectionMask() = default;
virtual std::vector<bool> selected_objects() const = 0;
virtual std::vector<bool> selected_instances(int obj_id) const = 0;
virtual bool is_wipe_tower_selected(int wipe_tower_index) const = 0;
};
class FixedSelection : public Slic3r::arr2::SelectionMask
{
std::vector<std::vector<bool>> m_seldata;
bool m_wp = false;
public:
FixedSelection() = default;
explicit FixedSelection(std::initializer_list<std::vector<bool>> seld,
bool wp = false)
: m_seldata{std::move(seld)}, m_wp{wp}
{}
explicit FixedSelection(const Model &m);
explicit FixedSelection(const SelectionMask &other);
std::vector<bool> selected_objects() const override;
std::vector<bool> selected_instances(int obj_id) const override
{
return obj_id < int(m_seldata.size()) ? m_seldata[obj_id] :
std::vector<bool>{};
}
bool is_wipe_tower_selected(int) const override { return m_wp; }
};
// Common part of any Arrangeable which is a wipe tower
struct ArrangeableWipeTowerBase: public Arrangeable
{
ObjectID oid;
Polygon poly;
SelectionPredicate selection_pred;
int bed_index{0};
ArrangeableWipeTowerBase(
const ObjectID &objid,
Polygon shape,
int bed_index,
SelectionPredicate selection_predicate = [](int){ return false; })
: oid{objid},
poly{std::move(shape)},
bed_index{bed_index},
selection_pred{std::move(selection_predicate)}
{}
ObjectID id() const override { return oid; }
ObjectID geometry_id() const override { return {}; }
ExPolygons full_outline() const override
{
auto cpy = poly;
return {ExPolygon{std::move(cpy)}};
}
Polygon convex_outline() const override
{
return poly;
}
bool is_selected() const override
{
return selection_pred(bed_index);
}
int get_bed_index() const override;
bool assign_bed(int /*bed_idx*/) override;
int priority() const override { return 1; }
std::optional<int> bed_constraint() const override {
return this->bed_index;
}
void transform(const Vec2d &transl, double rot) override {}
void imbue_data(AnyWritable &datastore) const override
{
datastore.write("is_wipe_tower", {});
}
};
class SceneBuilder;
struct InstPos { size_t obj_idx = 0, inst_idx = 0; };
using BedConstraints = std::map<ObjectID, int>;
// Implementing ArrangeableModel interface for QIDISlicer's Model, ModelObject, ModelInstance data
// hierarchy
class ArrangeableSlicerModel: public ArrangeableModel
{
protected:
AnyPtr<Model> m_model;
std::vector<AnyPtr<WipeTowerHandler>> m_wths; // Determines how wipe tower is handled
AnyPtr<VirtualBedHandler> m_vbed_handler; // Determines how virtual beds are handled
AnyPtr<const SelectionMask> m_selmask; // Determines which objects are selected/unselected
BedConstraints m_bed_constraints;
std::optional<std::set<ObjectID>> m_considered_instances;
private:
friend class SceneBuilder;
template<class Self, class Fn>
static void for_each_arrangeable_(Self &&self, Fn &&fn);
template<class Self, class Fn>
static void visit_arrangeable_(Self &&self, const ObjectID &id, Fn &&fn);
public:
explicit ArrangeableSlicerModel(SceneBuilder &builder);
~ArrangeableSlicerModel();
void for_each_arrangeable(std::function<void(Arrangeable &)>) override;
void for_each_arrangeable(std::function<void(const Arrangeable&)>) const override;
void visit_arrangeable(const ObjectID &id, std::function<void(const Arrangeable &)>) const override;
void visit_arrangeable(const ObjectID &id, std::function<void(Arrangeable &)>) override;
ObjectID add_arrangeable(const ObjectID &prototype_id) override;
Model & get_model() { return *m_model; }
const Model &get_model() const { return *m_model; }
};
// SceneBuilder implementation for QIDISlicer API.
class SceneBuilder: public SceneBuilderBase<SceneBuilder>
{
protected:
AnyPtr<Model> m_model;
std::vector<AnyPtr<WipeTowerHandler>> m_wipetower_handlers;
BedConstraints m_bed_constraints;
std::optional<std::set<ObjectID>> m_considered_instances;
AnyPtr<VirtualBedHandler> m_vbed_handler;
AnyPtr<const SelectionMask> m_selection;
AnyPtr<const SLAPrint> m_sla_print;
AnyPtr<const Print> m_fff_print;
bool m_xl_printer = false;
void set_brim_and_skirt();
public:
SceneBuilder();
~SceneBuilder();
SceneBuilder(SceneBuilder&&);
SceneBuilder& operator=(SceneBuilder&&);
SceneBuilder && set_model(AnyPtr<Model> mdl);
SceneBuilder && set_model(Model &mdl);
SceneBuilder && set_fff_print(AnyPtr<const Print> fffprint);
SceneBuilder && set_sla_print(AnyPtr<const SLAPrint> mdl_print);
using SceneBuilderBase<SceneBuilder>::set_bed;
SceneBuilder &&set_bed(const DynamicPrintConfig &cfg, const Vec2crd &gap);
SceneBuilder &&set_bed(const Print &print, const Vec2crd &gap);
SceneBuilder && set_wipe_tower_handlers(std::vector<AnyPtr<WipeTowerHandler>> &&handlers)
{
m_wipetower_handlers = std::move(handlers);
return std::move(*this);
}
SceneBuilder && set_bed_constraints(BedConstraints &&bed_constraints)
{
m_bed_constraints = std::move(bed_constraints);
return std::move(*this);
}
SceneBuilder && set_considered_instances(std::set<ObjectID> &&considered_instances)
{
m_considered_instances = std::move(considered_instances);
return std::move(*this);
}
SceneBuilder && set_virtual_bed_handler(AnyPtr<VirtualBedHandler> vbedh)
{
m_vbed_handler = std::move(vbedh);
return std::move(*this);
}
SceneBuilder && set_sla_print(const SLAPrint *slaprint);
SceneBuilder && set_selection(AnyPtr<const SelectionMask> sel)
{
m_selection = std::move(sel);
return std::move(*this);
}
// Can only be called on an rvalue instance (hence the && at the end),
// the method will potentially move its content into sc
void build_scene(Scene &sc) && override;
void build_arrangeable_slicer_model(ArrangeableSlicerModel &amodel);
};
// Only a physical bed, non-zero bed index values are discarded.
class PhysicalOnlyVBedHandler final : public VirtualBedHandler
{
public:
using VirtualBedHandler::assign_bed;
int get_bed_index(const VBedPlaceable &obj) const override { return 0; }
Transform3d get_physical_bed_trafo(int bed_index) const override
{
return Transform3d::Identity();
}
bool assign_bed(VBedPlaceable &inst, int bed_idx) override;
};
// A virtual bed handler implementation, that defines logical beds to be created
// on the right side of the physical bed along the X axis in a row
class XStriderVBedHandler final : public VirtualBedHandler
{
coord_t m_stride_scaled;
coord_t m_start;
public:
explicit XStriderVBedHandler(const BoundingBox &bedbb, coord_t xgap)
: m_stride_scaled{bedbb.size().x() + 2 * std::max(0, xgap)},
m_start{bedbb.min.x() - std::max(0, xgap)}
{
}
coord_t stride_scaled() const { return m_stride_scaled; }
// Can return negative indices when the instance is to the left of the
// physical bed
int get_bed_index(const VBedPlaceable &obj) const override;
// Only positive beds are accepted
bool assign_bed(VBedPlaceable &inst, int bed_idx) override;
using VirtualBedHandler::assign_bed;
Transform3d get_physical_bed_trafo(int bed_index) const override;
};
// Same as XStriderVBedHandler only that it lays out vbeds on the Y axis
class YStriderVBedHandler final : public VirtualBedHandler
{
coord_t m_stride_scaled;
coord_t m_start;
public:
coord_t stride_scaled() const { return m_stride_scaled; }
explicit YStriderVBedHandler(const BoundingBox &bedbb, coord_t ygap)
: m_stride_scaled{bedbb.size().y() + 2 * std::max(0, ygap)}
, m_start{bedbb.min.y() - std::max(0, ygap)}
{}
int get_bed_index(const VBedPlaceable &obj) const override;
bool assign_bed(VBedPlaceable &inst, int bed_idx) override;
Transform3d get_physical_bed_trafo(int bed_index) const override;
};
class GridStriderVBedHandler: public VirtualBedHandler
{
XStriderVBedHandler m_xstrider;
YStriderVBedHandler m_ystrider;
public:
GridStriderVBedHandler(const BoundingBox &bedbb, const Vec2crd &gap)
: m_xstrider{bedbb, gap.x()}
, m_ystrider{bedbb, gap.y()}
{}
int get_bed_index(const VBedPlaceable &obj) const override;
bool assign_bed(VBedPlaceable &inst, int bed_idx) override;
Transform3d get_physical_bed_trafo(int bed_index) const override;
};
std::vector<size_t> selected_object_indices(const SelectionMask &sm);
std::vector<size_t> selected_instance_indices(int obj_idx, const SelectionMask &sm);
coord_t get_skirt_inset(const Print &fffprint);
coord_t brim_offset(const PrintObject &po);
// unscaled coords are necessary to be able to handle bigger coordinate range
// than what is available with scaled coords. This is useful when working with
// virtual beds.
void transform_instance(ModelInstance &mi,
const Vec2d &transl_unscaled,
double rot,
const Transform3d &physical_tr = Transform3d::Identity());
BoundingBoxf3 instance_bounding_box(const ModelInstance &mi,
bool dont_translate = false);
BoundingBoxf3 instance_bounding_box(const ModelInstance &mi,
const Transform3d &tr,
bool dont_translate = false);
constexpr double UnscaledCoordLimit = 1000.;
ExPolygons extract_full_outline(const ModelInstance &inst,
const Transform3d &tr = Transform3d::Identity());
Polygon extract_convex_outline(const ModelInstance &inst,
const Transform3d &tr = Transform3d::Identity());
size_t model_instance_count (const Model &m);
class VBedPlaceableMI : public VBedPlaceable
{
ModelInstance *m_mi;
public:
explicit VBedPlaceableMI(ModelInstance &mi) : m_mi{&mi} {}
BoundingBoxf bounding_box() const override { return to_2d(instance_bounding_box(*m_mi)); }
void displace(const Vec2d &transl, double rot) override
{
transform_instance(*m_mi, transl, rot);
}
};
// Arrangeable interface implementation for ModelInstances
template<class InstPtr, class VBedHPtr>
class ArrangeableModelInstance : public Arrangeable, VBedPlaceable
{
InstPtr *m_mi;
VBedHPtr *m_vbedh;
const SelectionMask *m_selmask;
InstPos m_pos_within_model;
std::optional<int> m_bed_constraint;
public:
explicit ArrangeableModelInstance(InstPtr *mi,
VBedHPtr *vbedh,
const SelectionMask *selmask,
const InstPos &pos,
const std::optional<int> bed_constraint)
: m_mi{mi}, m_vbedh{vbedh}, m_selmask{selmask}, m_pos_within_model{pos}, m_bed_constraint(bed_constraint)
{
assert(m_mi != nullptr && m_vbedh != nullptr);
}
// Arrangeable:
ObjectID id() const override { return m_mi->id(); }
ObjectID geometry_id() const override { return m_mi->get_object()->id(); }
ExPolygons full_outline() const override;
Polygon convex_outline() const override;
bool is_printable() const override { return m_mi->printable; }
bool is_selected() const override;
void transform(const Vec2d &tr, double rot) override;
int get_bed_index() const override { return m_vbedh->get_bed_index(*this); }
bool assign_bed(int bed_idx) override;
std::optional<int> bed_constraint() const override { return m_bed_constraint; }
// VBedPlaceable:
BoundingBoxf bounding_box() const override { return to_2d(instance_bounding_box(*m_mi)); }
void displace(const Vec2d &transl, double rot) override
{
if constexpr (!std::is_const_v<InstPtr>)
transform_instance(*m_mi, transl, rot);
}
};
extern template class ArrangeableModelInstance<ModelInstance, VirtualBedHandler>;
extern template class ArrangeableModelInstance<const ModelInstance, const VirtualBedHandler>;
// Arrangeable implementation for an SLAPrintObject to be able to arrange with the supports and pad
class ArrangeableSLAPrintObject : public Arrangeable
{
const SLAPrintObject *m_po;
Arrangeable *m_arrbl;
Transform3d m_inst_trafo;
std::optional<int> m_bed_constraint;
public:
ArrangeableSLAPrintObject(const SLAPrintObject *po,
Arrangeable *arrbl,
const std::optional<int> bed_constraint,
const Transform3d &inst_tr = Transform3d::Identity())
: m_po{po}, m_arrbl{arrbl}, m_inst_trafo{inst_tr}, m_bed_constraint(bed_constraint)
{}
ObjectID id() const override { return m_arrbl->id(); }
ObjectID geometry_id() const override { return m_arrbl->geometry_id(); }
ExPolygons full_outline() const override;
ExPolygons full_envelope() const override;
Polygon convex_outline() const override;
Polygon convex_envelope() const override;
void transform(const Vec2d &transl, double rot) override
{
m_arrbl->transform(transl, rot);
}
int get_bed_index() const override { return m_arrbl->get_bed_index(); }
bool assign_bed(int bedidx) override
{
return m_arrbl->assign_bed(bedidx);
}
std::optional<int> bed_constraint() const override { return m_bed_constraint; }
bool is_printable() const override { return m_arrbl->is_printable(); }
bool is_selected() const override { return m_arrbl->is_selected(); }
int priority() const override { return m_arrbl->priority(); }
};
// Extension of ArrangeableSlicerModel for SLA
class ArrangeableSLAPrint : public ArrangeableSlicerModel {
const SLAPrint *m_slaprint;
friend class SceneBuilder;
template<class Self, class Fn>
static void for_each_arrangeable_(Self &&self, Fn &&fn);
template<class Self, class Fn>
static void visit_arrangeable_(Self &&self, const ObjectID &id, Fn &&fn);
public:
explicit ArrangeableSLAPrint(const SLAPrint *slaprint, SceneBuilder &builder)
: m_slaprint{slaprint}
, ArrangeableSlicerModel{builder}
{
assert(slaprint != nullptr);
}
void for_each_arrangeable(std::function<void(Arrangeable &)>) override;
void for_each_arrangeable(
std::function<void(const Arrangeable &)>) const override;
void visit_arrangeable(
const ObjectID &id,
std::function<void(const Arrangeable &)>) const override;
void visit_arrangeable(const ObjectID &id,
std::function<void(Arrangeable &)>) override;
};
template<class Mdl>
auto find_instance_by_id(Mdl &&model, const ObjectID &id)
{
std::remove_reference_t<
decltype(std::declval<Mdl>().objects[0]->instances[0])>
ret = nullptr;
InstPos pos;
for (auto * obj : model.objects) {
for (auto *inst : obj->instances) {
if (inst->id() == id) {
ret = inst;
break;
}
++pos.inst_idx;
}
if (ret)
break;
++pos.obj_idx;
pos.inst_idx = 0;
}
return std::make_pair(ret, pos);
}
struct ModelDuplicate
{
ObjectID id;
Vec2d tr = Vec2d::Zero();
double rot = 0.;
int bed_idx = Unarranged;
};
// Implementing the Arrangeable interface with the whole Model being one outline
// with all its objects and instances.
template<class Mdl, class Dup, class VBH>
class ArrangeableFullModel: public Arrangeable, VBedPlaceable
{
Mdl *m_mdl;
Dup *m_dup;
VBH *m_vbh;
public:
explicit ArrangeableFullModel(Mdl *mdl,
Dup *md,
VBH *vbh)
: m_mdl{mdl}, m_dup{md}, m_vbh{vbh}
{
assert(m_mdl != nullptr);
}
ObjectID id() const override { return m_dup->id.id + 1; }
ObjectID geometry_id() const override;
ExPolygons full_outline() const override;
Polygon convex_outline() const override;
bool is_printable() const override { return true; }
bool is_selected() const override { return m_dup->id == 0; }
int get_bed_index() const override
{
return m_vbh->get_bed_index(*this);
}
void transform(const Vec2d &tr, double rot) override
{
if constexpr (!std::is_const_v<Mdl> && !std::is_const_v<Dup>) {
m_dup->tr += tr;
m_dup->rot += rot;
}
}
bool assign_bed(int bed_idx) override
{
bool ret = false;
if constexpr (!std::is_const_v<VBH> && !std::is_const_v<Dup>) {
if ((ret = m_vbh->assign_bed(*this, bed_idx)))
m_dup->bed_idx = bed_idx;
}
return ret;
}
BoundingBoxf bounding_box() const override { return unscaled(get_extents(convex_outline())); }
void displace(const Vec2d &transl, double rot) override
{
transform(transl, rot);
}
};
extern template class ArrangeableFullModel<Model, ModelDuplicate, VirtualBedHandler>;
extern template class ArrangeableFullModel<const Model, const ModelDuplicate, const VirtualBedHandler>;
// An implementation of the ArrangeableModel to be used for the full model 'duplicate' feature
// accessible from CLI
class DuplicableModel: public ArrangeableModel {
AnyPtr<Model> m_model;
AnyPtr<VirtualBedHandler> m_vbh;
std::vector<ModelDuplicate> m_duplicates;
BoundingBox m_bedbb;
template<class Self, class Fn>
static void visit_arrangeable_(Self &&self, const ObjectID &id, Fn &&fn)
{
if (id.valid()) {
size_t idx = id.id - 1;
if (idx < self.m_duplicates.size()) {
auto &md = self.m_duplicates[idx];
ArrangeableFullModel arrbl{self.m_model.get(), &md, self.m_vbh.get()};
fn(arrbl);
}
}
}
public:
explicit DuplicableModel(AnyPtr<Model> mdl,
AnyPtr<VirtualBedHandler> vbh,
const BoundingBox &bedbb);
~DuplicableModel();
void for_each_arrangeable(std::function<void(Arrangeable &)> fn) override
{
for (ModelDuplicate &md : m_duplicates) {
ArrangeableFullModel arrbl{m_model.get(), &md, m_vbh.get()};
fn(arrbl);
}
}
void for_each_arrangeable(std::function<void(const Arrangeable&)> fn) const override
{
for (const ModelDuplicate &md : m_duplicates) {
ArrangeableFullModel arrbl{m_model.get(), &md, m_vbh.get()};
fn(arrbl);
}
}
void visit_arrangeable(const ObjectID &id, std::function<void(const Arrangeable &)> fn) const override
{
visit_arrangeable_(*this, id, fn);
}
void visit_arrangeable(const ObjectID &id, std::function<void(Arrangeable &)> fn) override
{
visit_arrangeable_(*this, id, fn);
}
ObjectID add_arrangeable(const ObjectID &prototype_id) override;
void apply_duplicates();
};
} // namespace arr2
} // namespace Slic3r
#endif // SCENEBUILDER_HPP

View File

@@ -0,0 +1,121 @@
#ifndef SEGMENTEDRECTANGLEBED_HPP
#define SEGMENTEDRECTANGLEBED_HPP
#include <arrange/Beds.hpp>
namespace Slic3r { namespace arr2 {
enum class RectPivots {
Center, BottomLeft, BottomRight, TopLeft, TopRight
};
template<class T> struct IsSegmentedBed_ : public std::false_type {};
template<class T> constexpr bool IsSegmentedBed = IsSegmentedBed_<StripCVRef<T>>::value;
template<class SegX = void, class SegY = void, class Pivot = void>
struct SegmentedRectangleBed {
Vec<2, size_t> segments = Vec<2, size_t>::Ones();
BoundingBox bb;
Vec2crd gap;
RectPivots pivot = RectPivots::Center;
SegmentedRectangleBed() = default;
SegmentedRectangleBed(const BoundingBox &bb,
size_t segments_x,
size_t segments_y,
const Vec2crd &gap,
const RectPivots pivot = RectPivots::Center)
: segments{segments_x, segments_y}, bb{bb}, gap{gap}, pivot{pivot}
{}
size_t segments_x() const noexcept { return segments.x(); }
size_t segments_y() const noexcept { return segments.y(); }
auto alignment() const noexcept { return pivot; }
};
template<size_t SegX, size_t SegY>
struct SegmentedRectangleBed<std::integral_constant<size_t, SegX>,
std::integral_constant<size_t, SegY>>
{
BoundingBox bb;
Vec2crd gap;
RectPivots pivot = RectPivots::Center;
SegmentedRectangleBed() = default;
explicit SegmentedRectangleBed(const BoundingBox &b,
const Vec2crd &gap,
const RectPivots pivot = RectPivots::Center)
: bb{b},
gap{gap}
{}
size_t segments_x() const noexcept { return SegX; }
size_t segments_y() const noexcept { return SegY; }
auto alignment() const noexcept { return pivot; }
};
template<size_t SegX, size_t SegY, RectPivots pivot>
struct SegmentedRectangleBed<std::integral_constant<size_t, SegX>,
std::integral_constant<size_t, SegY>,
std::integral_constant<RectPivots, pivot>>
{
BoundingBox bb;
Vec2crd gap;
SegmentedRectangleBed() = default;
explicit SegmentedRectangleBed(const BoundingBox &b, const Vec2crd &gap) : bb{b}, gap{gap} {}
size_t segments_x() const noexcept { return SegX; }
size_t segments_y() const noexcept { return SegY; }
auto alignment() const noexcept { return pivot; }
};
template<class... Args>
struct IsSegmentedBed_<SegmentedRectangleBed<Args...>>
: public std::true_type {};
template<class... Args>
auto offset(const SegmentedRectangleBed<Args...> &bed, coord_t val_scaled)
{
auto cpy = bed;
cpy.bb.offset(val_scaled);
return cpy;
}
template<class...Args>
auto bounding_box(const SegmentedRectangleBed<Args...> &bed)
{
return bed.bb;
}
template<class...Args>
auto bed_gap(const SegmentedRectangleBed<Args...> &bed)
{
return bed.gap;
}
template<class...Args>
auto area(const SegmentedRectangleBed<Args...> &bed)
{
return arr2::area(bed.bb);
}
template<class...Args>
ExPolygons to_expolygons(const SegmentedRectangleBed<Args...> &bed)
{
return to_expolygons(RectangleBed{bed.bb});
}
template<class SegB>
struct IsRectangular_<SegB, std::enable_if_t<IsSegmentedBed<SegB>, void>> : public std::true_type
{};
}} // namespace Slic3r::arr2
#endif // SEGMENTEDRECTANGLEBED_HPP

View File

@@ -0,0 +1,81 @@
#ifndef ARRANGETASK_HPP
#define ARRANGETASK_HPP
#include <arrange-wrapper/Arrange.hpp>
#include <arrange-wrapper/Items/TrafoOnlyArrangeItem.hpp>
namespace Slic3r { namespace arr2 {
struct ArrangeTaskResult : public ArrangeResult
{
std::vector<TrafoOnlyArrangeItem> items;
bool apply_on(ArrangeableModel &mdl) override
{
bool ret = true;
for (auto &itm : items) {
if (is_arranged(itm))
ret = ret && apply_arrangeitem(itm, mdl);
}
return ret;
}
template<class ArrItem>
void add_item(const ArrItem &itm)
{
items.emplace_back(itm);
if (auto id = retrieve_id(itm))
imbue_id(items.back(), *id);
}
template<class It>
void add_items(const Range<It> &items_range)
{
for (auto &itm : items_range)
add_item(itm);
}
};
template<class ArrItem> struct ArrangeTask : public ArrangeTaskBase
{
struct ArrangeSet
{
std::vector<ArrItem> selected, unselected;
} printable, unprintable;
ExtendedBed bed;
ArrangeSettings settings;
static std::unique_ptr<ArrangeTask> create(
const Scene &sc,
const ArrangeableToItemConverter<ArrItem> &converter);
static std::unique_ptr<ArrangeTask> create(const Scene &sc)
{
auto conv = ArrangeableToItemConverter<ArrItem>::create(sc);
return create(sc, *conv);
}
std::unique_ptr<ArrangeResult> process(Ctl &ctl) override
{
return process_native(ctl);
}
std::unique_ptr<ArrangeTaskResult> process_native(Ctl &ctl);
std::unique_ptr<ArrangeTaskResult> process_native(Ctl &&ctl)
{
return process_native(ctl);
}
int item_count_to_process() const override
{
return static_cast<int>(printable.selected.size() +
unprintable.selected.size());
}
};
} // namespace arr2
} // namespace Slic3r
#endif // ARRANGETASK_HPP

View File

@@ -0,0 +1,57 @@
#ifndef FILLBEDTASK_HPP
#define FILLBEDTASK_HPP
#include <arrange-wrapper/Arrange.hpp>
#include "MultiplySelectionTask.hpp"
namespace Slic3r { namespace arr2 {
struct FillBedTaskResult: public MultiplySelectionTaskResult {};
template<class ArrItem>
struct FillBedTask: public ArrangeTaskBase
{
std::optional<ArrItem> prototype_item;
std::vector<ArrItem> selected, unselected;
// For workaround regarding "holes" when filling the bed with the same
// item's copies
std::vector<ArrItem> selected_fillers;
ArrangeSettings settings;
ExtendedBed bed;
size_t selected_existing_count = 0;
std::unique_ptr<FillBedTaskResult> process_native(Ctl &ctl);
std::unique_ptr<FillBedTaskResult> process_native(Ctl &&ctl)
{
return process_native(ctl);
}
std::unique_ptr<ArrangeResult> process(Ctl &ctl) override
{
return process_native(ctl);
}
int item_count_to_process() const override
{
return selected.size();
}
static std::unique_ptr<FillBedTask> create(
const Scene &sc,
const ArrangeableToItemConverter<ArrItem> &converter);
static std::unique_ptr<FillBedTask> create(const Scene &sc)
{
auto conv = ArrangeableToItemConverter<ArrItem>::create(sc);
return create(sc, *conv);
}
};
} // namespace arr2
} // namespace Slic3r
#endif // FILLBEDTASK_HPP

View File

@@ -0,0 +1,108 @@
#ifndef MULTIPLYSELECTIONTASK_HPP
#define MULTIPLYSELECTIONTASK_HPP
#include <arrange-wrapper/Arrange.hpp>
#include <arrange-wrapper/Items/TrafoOnlyArrangeItem.hpp>
namespace Slic3r { namespace arr2 {
struct MultiplySelectionTaskResult: public ArrangeResult {
ObjectID prototype_id;
std::vector<TrafoOnlyArrangeItem> arranged_items;
std::vector<TrafoOnlyArrangeItem> to_add;
bool apply_on(ArrangeableModel &mdl) override
{
bool ret = prototype_id.valid();
if (!ret)
return ret;
for (auto &itm : to_add) {
auto id = mdl.add_arrangeable(prototype_id);
imbue_id(itm, id);
ret = ret && apply_arrangeitem(itm, mdl);
}
for (auto &itm : arranged_items) {
if (is_arranged(itm))
ret = ret && apply_arrangeitem(itm, mdl);
}
return ret;
}
template<class ArrItem>
void add_arranged_item(const ArrItem &itm)
{
arranged_items.emplace_back(itm);
if (auto id = retrieve_id(itm))
imbue_id(arranged_items.back(), *id);
}
template<class It>
void add_arranged_items(const Range<It> &items_range)
{
arranged_items.reserve(items_range.size());
for (auto &itm : items_range)
add_arranged_item(itm);
}
template<class ArrItem> void add_new_item(const ArrItem &itm)
{
to_add.emplace_back(itm);
}
template<class It> void add_new_items(const Range<It> &items_range)
{
to_add.reserve(items_range.size());
for (auto &itm : items_range) {
to_add.emplace_back(itm);
}
}
};
template<class ArrItem>
struct MultiplySelectionTask: public ArrangeTaskBase
{
std::optional<ArrItem> prototype_item;
std::vector<ArrItem> selected, unselected;
ArrangeSettings settings;
ExtendedBed bed;
size_t selected_existing_count = 0;
std::unique_ptr<MultiplySelectionTaskResult> process_native(Ctl &ctl);
std::unique_ptr<MultiplySelectionTaskResult> process_native(Ctl &&ctl)
{
return process_native(ctl);
}
std::unique_ptr<ArrangeResult> process(Ctl &ctl) override
{
return process_native(ctl);
}
int item_count_to_process() const override
{
return selected.size();
}
static std::unique_ptr<MultiplySelectionTask> create(
const Scene &sc,
size_t multiply_count,
const ArrangeableToItemConverter<ArrItem> &converter);
static std::unique_ptr<MultiplySelectionTask> create(const Scene &sc,
size_t multiply_count)
{
auto conv = ArrangeableToItemConverter<ArrItem>::create(sc);
return create(sc, multiply_count, *conv);
}
};
}} // namespace Slic3r::arr2
#endif // MULTIPLYSELECTIONTASK_HPP