diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 5e166b44f9..16f8fec4e6 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -607,11 +607,15 @@ ModelObject::~ModelObject() // maintains the m_model pointer ModelObject& ModelObject::assign_copy(const ModelObject &rhs) { - this->copy_id(rhs); + assert(this->id().invalid() || this->id() == rhs.id()); + assert(this->config.id().invalid() || this->config.id() == rhs.config.id()); + this->copy_id(rhs); this->name = rhs.name; this->input_file = rhs.input_file; + // Copies the config's ID this->config = rhs.config; + assert(this->config.id() == rhs.config.id()); this->sla_support_points = rhs.sla_support_points; this->sla_points_status = rhs.sla_points_status; this->layer_height_ranges = rhs.layer_height_ranges; @@ -643,11 +647,14 @@ ModelObject& ModelObject::assign_copy(const ModelObject &rhs) // maintains the m_model pointer ModelObject& ModelObject::assign_copy(ModelObject &&rhs) { + assert(this->id().invalid()); this->copy_id(rhs); this->name = std::move(rhs.name); this->input_file = std::move(rhs.input_file); + // Moves the config's ID this->config = std::move(rhs.config); + assert(this->config.id() == rhs.config.id()); this->sla_support_points = std::move(rhs.sla_support_points); this->sla_points_status = std::move(rhs.sla_points_status); this->layer_height_ranges = std::move(rhs.layer_height_ranges); @@ -1176,13 +1183,19 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b if (keep_upper && upper_mesh.facets_count() > 0) { ModelVolume* vol = upper->add_volume(upper_mesh); vol->name = volume->name; - vol->config = volume->config; + // Don't copy the config's ID. + static_cast(vol->config) = static_cast(volume->config); + assert(vol->config.id().valid()); + assert(vol->config.id() != volume->config.id()); vol->set_material(volume->material_id(), *volume->material()); } if (keep_lower && lower_mesh.facets_count() > 0) { ModelVolume* vol = lower->add_volume(lower_mesh); vol->name = volume->name; - vol->config = volume->config; + // Don't copy the config's ID. + static_cast(vol->config) = static_cast(volume->config); + assert(vol->config.id().valid()); + assert(vol->config.id() != volume->config.id()); vol->set_material(volume->material_id(), *volume->material()); // Compute the lower part instances' bounding boxes to figure out where to place @@ -1257,7 +1270,10 @@ void ModelObject::split(ModelObjectPtrs* new_objects) // XXX: this seems to be the only real usage of m_model, maybe refactor this so that it's not needed? ModelObject* new_object = m_model->add_object(); new_object->name = this->name; - new_object->config = this->config; + // Don't copy the config's ID. + static_cast(new_object->config) = static_cast(this->config); + assert(new_object->config.id().valid()); + assert(new_object->config.id() != this->config.id()); new_object->instances.reserve(this->instances.size()); for (const ModelInstance *model_instance : this->instances) new_object->add_instance(*model_instance); @@ -1852,19 +1868,24 @@ void check_model_ids_validity(const Model &model) { std::set ids; auto check = [&ids](ObjectID id) { - assert(id.id > 0); + assert(id.valid()); assert(ids.find(id) == ids.end()); ids.insert(id); }; for (const ModelObject *model_object : model.objects) { check(model_object->id()); - for (const ModelVolume *model_volume : model_object->volumes) + check(model_object->config.id()); + for (const ModelVolume *model_volume : model_object->volumes) { check(model_volume->id()); + check(model_volume->config.id()); + } for (const ModelInstance *model_instance : model_object->instances) check(model_instance->id()); } - for (const auto mm : model.materials) + for (const auto mm : model.materials) { check(mm.second->id()); + check(mm.second->config.id()); + } } void check_model_ids_equal(const Model &model1, const Model &model2) @@ -1875,10 +1896,13 @@ void check_model_ids_equal(const Model &model1, const Model &model2) const ModelObject &model_object1 = *model1.objects[idx_model]; const ModelObject &model_object2 = * model2.objects[idx_model]; assert(model_object1.id() == model_object2.id()); + assert(model_object1.config.id() == model_object2.config.id()); assert(model_object1.volumes.size() == model_object2.volumes.size()); assert(model_object1.instances.size() == model_object2.instances.size()); - for (size_t i = 0; i < model_object1.volumes.size(); ++ i) + for (size_t i = 0; i < model_object1.volumes.size(); ++ i) { assert(model_object1.volumes[i]->id() == model_object2.volumes[i]->id()); + assert(model_object1.volumes[i]->config.id() == model_object2.volumes[i]->config.id()); + } for (size_t i = 0; i < model_object1.instances.size(); ++ i) assert(model_object1.instances[i]->id() == model_object2.instances[i]->id()); } @@ -1889,6 +1913,7 @@ void check_model_ids_equal(const Model &model1, const Model &model2) for (; it1 != model1.materials.end(); ++ it1, ++ it2) { assert(it1->first == it2->first); // compare keys assert(it1->second->id() == it2->second->id()); + assert(it1->second->config.id() == it2->second->config.id()); } } } diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 8e9f7ecaa9..5fd958f862 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -31,6 +31,34 @@ namespace UndoRedo { class StackImpl; } +class ModelConfig : public ObjectBase, public DynamicPrintConfig +{ +private: + friend class cereal::access; + friend class UndoRedo::StackImpl; + friend class ModelObject; + friend class ModelVolume; + friend class ModelMaterial; + + // Constructors to be only called by derived classes. + // Default constructor to assign a unique ID. + explicit ModelConfig() {} + // Constructor with ignored int parameter to assign an invalid ID, to be replaced + // by an existing ID copied from elsewhere. + explicit ModelConfig(int) : ObjectBase(-1) {} + // Copy constructor copies the ID. + explicit ModelConfig(const ModelConfig &cfg) : ObjectBase(-1), DynamicPrintConfig(cfg) { this->copy_id(cfg); } + // Move constructor copies the ID. + explicit ModelConfig(ModelConfig &&cfg) : ObjectBase(-1), DynamicPrintConfig(std::move(cfg)) { this->copy_id(cfg); } + + ModelConfig& operator=(const ModelConfig &rhs) = default; + ModelConfig& operator=(ModelConfig &&rhs) = default; + + template void serialize(Archive &ar) { + ar(cereal::base_class(this)); + } +}; + typedef std::string t_model_material_id; typedef std::string t_model_material_attribute; typedef std::map t_model_material_attributes; @@ -43,10 +71,10 @@ typedef std::vector ModelInstancePtrs; #define OBJECTBASE_DERIVED_COPY_MOVE_CLONE(TYPE) \ /* Copy a model, copy the IDs. The Print::apply() will call the TYPE::copy() method */ \ /* to make a private copy for background processing. */ \ - static TYPE* new_copy(const TYPE &rhs) { return new TYPE(rhs); } \ - static TYPE* new_copy(TYPE &&rhs) { return new TYPE(std::move(rhs)); } \ - static TYPE make_copy(const TYPE &rhs) { return TYPE(rhs); } \ - static TYPE make_copy(TYPE &&rhs) { return TYPE(std::move(rhs)); } \ + static TYPE* new_copy(const TYPE &rhs) { auto *ret = new TYPE(rhs); assert(ret->id() == rhs.id()); return ret; } \ + static TYPE* new_copy(TYPE &&rhs) { auto *ret = new TYPE(std::move(rhs)); assert(ret->id() == rhs.id()); return ret; } \ + static TYPE make_copy(const TYPE &rhs) { TYPE ret(rhs); assert(ret.id() == rhs.id()); return ret; } \ + static TYPE make_copy(TYPE &&rhs) { TYPE ret(std::move(rhs)); assert(ret.id() == rhs.id()); return ret; } \ TYPE& assign_copy(const TYPE &rhs); \ TYPE& assign_copy(TYPE &&rhs); \ /* Copy a TYPE, generate new IDs. The front end will use this call. */ \ @@ -54,26 +82,24 @@ typedef std::vector ModelInstancePtrs; /* Default constructor assigning an invalid ID. */ \ auto obj = new TYPE(-1); \ obj->assign_clone(rhs); \ + assert(obj->id().valid() && obj->id() != rhs.id()); \ return obj; \ } \ TYPE make_clone(const TYPE &rhs) { \ /* Default constructor assigning an invalid ID. */ \ TYPE obj(-1); \ obj.assign_clone(rhs); \ + assert(obj.id().valid() && obj.id() != rhs.id()); \ return obj; \ } \ TYPE& assign_clone(const TYPE &rhs) { \ this->assign_copy(rhs); \ + assert(this->id().valid() && this->id() == rhs.id()); \ this->assign_new_unique_ids_recursive(); \ + assert(this->id().valid() && this->id() != rhs.id()); \ return *this; \ } -#define OBJECTBASE_DERIVED_PRIVATE_COPY_MOVE(TYPE) \ -private: \ - /* Private constructor with an unused int parameter will create a TYPE instance with an invalid ID. */ \ - explicit TYPE(int) : ObjectBase(-1) {}; \ - void assign_new_unique_ids_recursive(); - // Material, which may be shared across multiple ModelObjects of a single Model. class ModelMaterial final : public ObjectBase { @@ -81,32 +107,40 @@ public: // Attributes are defined by the AMF file format, but they don't seem to be used by Slic3r for any purpose. t_model_material_attributes attributes; // Dynamic configuration storage for the object specific configuration values, overriding the global configuration. - DynamicPrintConfig config; + ModelConfig config; Model* get_model() const { return m_model; } void apply(const t_model_material_attributes &attributes) { this->attributes.insert(attributes.begin(), attributes.end()); } -protected: - friend class Model; - // Constructor, which assigns a new unique ID. - ModelMaterial(Model *model) : m_model(model) {} - // Copy constructor copies the ID and m_model! - ModelMaterial(const ModelMaterial &rhs) = default; - void set_model(Model *model) { m_model = model; } - private: // Parent, owning this material. Model *m_model; - + + // To be accessed by the Model. + friend class Model; + // Constructor, which assigns a new unique ID to the material and to its config. + ModelMaterial(Model *model) : m_model(model) { assert(this->id().valid()); } + // Copy constructor copies the IDs of the ModelMaterial and its config, and m_model! + ModelMaterial(const ModelMaterial &rhs) = default; + void set_model(Model *model) { m_model = model; } + void set_new_unique_id() { ObjectBase::set_new_unique_id(); this->config.set_new_unique_id(); } + + // To be accessed by the serialization and Undo/Redo code. + friend class cereal::access; + friend class UndoRedo::StackImpl; + // Create an object for deserialization, don't allocate IDs for ModelMaterial and its config. + ModelMaterial() : ObjectBase(-1), config(-1), m_model(nullptr) { assert(this->id().invalid()); assert(this->config.id().invalid()); } + template void serialize(Archive &ar) { + assert(this->id().invalid()); assert(this->config.id().invalid()); + ar(attributes, config); + // assert(this->id().valid()); assert(this->config.id().valid()); + } + + // Disabled methods. ModelMaterial(ModelMaterial &&rhs) = delete; ModelMaterial& operator=(const ModelMaterial &rhs) = delete; ModelMaterial& operator=(ModelMaterial &&rhs) = delete; - - friend class cereal::access; - friend class UndoRedo::StackImpl; - ModelMaterial() : m_model(nullptr) {} - template void serialize(Archive &ar) { ar(cereal::base_class(this)); ar(attributes, config); } }; // A printable object, possibly having multiple print volumes (each with its own set of parameters and materials), @@ -115,7 +149,6 @@ private: // different rotation and different uniform scaling. class ModelObject final : public ObjectBase { - friend class Model; public: std::string name; std::string input_file; // XXX: consider fs::path @@ -126,7 +159,7 @@ public: // ModelVolumes are owned by this ModelObject. ModelVolumePtrs volumes; // Configuration parameters specific to a single ModelObject, overriding the global Slic3r settings. - DynamicPrintConfig config; + ModelConfig config; // Variation of a layer thickness for spans of Z coordinates. t_layer_height_ranges layer_height_ranges; // Profile of increasing z to a layer height, to be linearly interpolated when calculating the layers. @@ -236,25 +269,53 @@ public: std::string get_export_filename() const; - // Get full stl statistics for all object's meshes + // Get full stl statistics for all object's meshes stl_stats get_object_stl_stats() const; - // Get count of errors in the mesh( or all object's meshes, if volume index isn't defined) + // Get count of errors in the mesh( or all object's meshes, if volume index isn't defined) int get_mesh_errors_count(const int vol_idx = -1) const; private: - ModelObject(Model *model) : m_model(model), origin_translation(Vec3d::Zero()), - m_bounding_box_valid(false), m_raw_bounding_box_valid(false), m_raw_mesh_bounding_box_valid(false) {} - ~ModelObject(); + friend class Model; + // This constructor assigns new ID to this ModelObject and its config. + explicit ModelObject(Model *model) : m_model(model), origin_translation(Vec3d::Zero()), + m_bounding_box_valid(false), m_raw_bounding_box_valid(false), m_raw_mesh_bounding_box_valid(false) + { assert(this->id().valid()); } + explicit ModelObject(int) : ObjectBase(-1), config(-1), m_model(nullptr), origin_translation(Vec3d::Zero()), m_bounding_box_valid(false), m_raw_bounding_box_valid(false), m_raw_mesh_bounding_box_valid(false) + { assert(this->id().invalid()); assert(this->config.id().invalid()); } + ~ModelObject(); + void assign_new_unique_ids_recursive(); - /* To be able to return an object from own copy / clone methods. Hopefully the compiler will do the "Copy elision" */ - /* (Omits copy and move(since C++11) constructors, resulting in zero - copy pass - by - value semantics). */ - ModelObject(const ModelObject &rhs) : ObjectBase(-1), m_model(rhs.m_model) { this->assign_copy(rhs); } - explicit ModelObject(ModelObject &&rhs) : ObjectBase(-1) { this->assign_copy(std::move(rhs)); } - ModelObject& operator=(const ModelObject &rhs) { this->assign_copy(rhs); m_model = rhs.m_model; return *this; } - ModelObject& operator=(ModelObject &&rhs) { this->assign_copy(std::move(rhs)); m_model = rhs.m_model; return *this; } + // To be able to return an object from own copy / clone methods. Hopefully the compiler will do the "Copy elision" + // (Omits copy and move(since C++11) constructors, resulting in zero - copy pass - by - value semantics). + ModelObject(const ModelObject &rhs) : ObjectBase(-1), config(-1), m_model(rhs.m_model) { + assert(this->id().invalid()); assert(this->config.id().invalid()); assert(rhs.id() != rhs.config.id()); + this->assign_copy(rhs); + assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id()); + assert(this->id() == rhs.id()); assert(this->config.id() == rhs.config.id()); + } + explicit ModelObject(ModelObject &&rhs) : ObjectBase(-1), config(-1) { + assert(this->id().invalid()); assert(this->config.id().invalid()); assert(rhs.id() != rhs.config.id()); + this->assign_copy(std::move(rhs)); + assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id()); + assert(this->id() == rhs.id()); assert(this->config.id() == rhs.config.id()); + } + ModelObject& operator=(const ModelObject &rhs) { + this->assign_copy(rhs); + m_model = rhs.m_model; + assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id()); + assert(this->id() == rhs.id()); assert(this->config.id() == rhs.config.id()); + return *this; + } + ModelObject& operator=(ModelObject &&rhs) { + this->assign_copy(std::move(rhs)); + m_model = rhs.m_model; + assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id()); + assert(this->id() == rhs.id()); assert(this->config.id() == rhs.config.id()); + return *this; + } + void set_new_unique_id() { ObjectBase::set_new_unique_id(); this->config.set_new_unique_id(); } OBJECTBASE_DERIVED_COPY_MOVE_CLONE(ModelObject) - OBJECTBASE_DERIVED_PRIVATE_COPY_MOVE(ModelObject) // Parent object, owning this ModelObject. Set to nullptr here, so the macros above will have it initialized. Model *m_model = nullptr; @@ -275,8 +336,11 @@ private: // Undo / Redo through the cereal serialization library friend class cereal::access; friend class UndoRedo::StackImpl; - ModelObject() : m_model(nullptr), m_bounding_box_valid(false), m_raw_bounding_box_valid(false), m_raw_mesh_bounding_box_valid(false) {} - template void serialize(Archive &ar) { + // Used for deserialization -> Don't allocate any IDs for the ModelObject or its config. + ModelObject() : ObjectBase(-1), config(-1), m_model(nullptr), m_bounding_box_valid(false), m_raw_bounding_box_valid(false), m_raw_mesh_bounding_box_valid(false) { + assert(this->id().invalid()); assert(this->config.id().invalid()); + } + template void serialize(Archive &ar) { ar(cereal::base_class(this)); ar(name, input_file, instances, volumes, config, layer_height_ranges, layer_height_profile, sla_support_points, sla_points_status, origin_translation, m_bounding_box, m_bounding_box_valid, m_raw_bounding_box, m_raw_bounding_box_valid, m_raw_mesh_bounding_box, m_raw_mesh_bounding_box_valid); @@ -307,7 +371,7 @@ public: void reset_mesh() { m_mesh = std::make_shared(); } // Configuration parameters specific to an object model geometry or a modifier volume, // overriding the global Slic3r settings and the ModelObject settings. - DynamicPrintConfig config; + ModelConfig config; // A parent object owning this modifier volume. ModelObject* get_object() const { return this->object; }; @@ -388,13 +452,14 @@ public: const Transform3d& get_matrix(bool dont_translate = false, bool dont_rotate = false, bool dont_scale = false, bool dont_mirror = false) const { return m_transformation.get_matrix(dont_translate, dont_rotate, dont_scale, dont_mirror); } - using ObjectBase::set_new_unique_id; + void set_new_unique_id() { ObjectBase::set_new_unique_id(); this->config.set_new_unique_id(); } protected: friend class Print; friend class SLAPrint; friend class ModelObject; + // Copies IDs of both the ModelVolume and its config. explicit ModelVolume(const ModelVolume &rhs) = default; void set_model_object(ModelObject *model_object) { object = model_object; } void transform_this_mesh(const Transform3d& t, bool fix_left_handed); @@ -420,33 +485,46 @@ private: ModelVolume(ModelObject *object, const TriangleMesh &mesh) : m_mesh(new TriangleMesh(mesh)), m_type(ModelVolumeType::MODEL_PART), object(object) { + assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id()); if (mesh.stl.stats.number_of_facets > 1) calculate_convex_hull(); } ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull) : - m_mesh(new TriangleMesh(std::move(mesh))), m_convex_hull(new TriangleMesh(std::move(convex_hull))), m_type(ModelVolumeType::MODEL_PART), object(object) {} + m_mesh(new TriangleMesh(std::move(mesh))), m_convex_hull(new TriangleMesh(std::move(convex_hull))), m_type(ModelVolumeType::MODEL_PART), object(object) { + assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id()); + } // Copying an existing volume, therefore this volume will get a copy of the ID assigned. ModelVolume(ModelObject *object, const ModelVolume &other) : - ObjectBase(other), // copy the ID + ObjectBase(other), name(other.name), m_mesh(other.m_mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation) { + assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id()); + assert(this->id() == other.id() && this->config.id() == other.config.id()); this->set_material_id(other.material_id()); } // Providing a new mesh, therefore this volume will get a new unique ID assigned. ModelVolume(ModelObject *object, const ModelVolume &other, const TriangleMesh &&mesh) : name(other.name), m_mesh(new TriangleMesh(std::move(mesh))), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation) { + assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id()); + assert(this->id() == other.id() && this->config.id() == other.config.id()); this->set_material_id(other.material_id()); + this->config.set_new_unique_id(); if (mesh.stl.stats.number_of_facets > 1) calculate_convex_hull(); + assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id()); + assert(this->id() != other.id() && this->config.id() != other.config.id()); } ModelVolume& operator=(ModelVolume &rhs) = delete; friend class cereal::access; friend class UndoRedo::StackImpl; - ModelVolume() : object(nullptr) {} + // Used for deserialization, therefore no IDs are allocated. + ModelVolume() : ObjectBase(-1), config(-1), object(nullptr) { + assert(this->id().invalid()); assert(this->config.id().invalid()); + } template void serialize(Archive &ar) { ar(name, config, m_mesh, m_type, m_material_id, m_convex_hull, m_transformation, m_is_splittable); } @@ -530,10 +608,10 @@ private: ModelObject* object; // Constructor, which assigns a new unique ID. - explicit ModelInstance(ModelObject *object) : object(object), print_volume_state(PVS_Inside) {} + explicit ModelInstance(ModelObject *object) : object(object), print_volume_state(PVS_Inside) { assert(this->id().valid()); } // Constructor, which assigns a new unique ID. explicit ModelInstance(ModelObject *object, const ModelInstance &other) : - m_transformation(other.m_transformation), object(object), print_volume_state(PVS_Inside) {} + m_transformation(other.m_transformation), object(object), print_volume_state(PVS_Inside) { assert(this->id().valid() && this->id() != other.id()); } explicit ModelInstance(ModelInstance &&rhs) = delete; ModelInstance& operator=(const ModelInstance &rhs) = delete; @@ -541,9 +619,9 @@ private: friend class cereal::access; friend class UndoRedo::StackImpl; - ModelInstance() : object(nullptr) {} + // Used for deserialization, therefore no IDs are allocated. + ModelInstance() : ObjectBase(-1), object(nullptr) { assert(this->id().invalid()); } template void serialize(Archive &ar) { - ar(cereal::base_class(this)); ar(m_transformation, print_volume_state); } }; @@ -565,15 +643,15 @@ public: ModelObjectPtrs objects; // Default constructor assigns a new ID to the model. - Model() {} + Model() { assert(this->id().valid()); } ~Model() { this->clear_objects(); this->clear_materials(); } /* To be able to return an object from own copy / clone methods. Hopefully the compiler will do the "Copy elision" */ /* (Omits copy and move(since C++11) constructors, resulting in zero - copy pass - by - value semantics). */ - Model(const Model &rhs) : ObjectBase(-1) { this->assign_copy(rhs); } - explicit Model(Model &&rhs) : ObjectBase(-1) { this->assign_copy(std::move(rhs)); } - Model& operator=(const Model &rhs) { this->assign_copy(rhs); return *this; } - Model& operator=(Model &&rhs) { this->assign_copy(std::move(rhs)); return *this; } + Model(const Model &rhs) : ObjectBase(-1) { assert(this->id().invalid()); this->assign_copy(rhs); assert(this->id().valid()); assert(this->id() == rhs.id()); } + explicit Model(Model &&rhs) : ObjectBase(-1) { assert(this->id().invalid()); this->assign_copy(std::move(rhs)); assert(this->id().valid()); assert(this->id() == rhs.id()); } + Model& operator=(const Model &rhs) { this->assign_copy(rhs); assert(this->id().valid()); assert(this->id() == rhs.id()); return *this; } + Model& operator=(Model &&rhs) { this->assign_copy(std::move(rhs)); assert(this->id().valid()); assert(this->id() == rhs.id()); return *this; } OBJECTBASE_DERIVED_COPY_MOVE_CLONE(Model) @@ -633,12 +711,13 @@ public: std::string propose_export_file_name_and_path(const std::string &new_extension) const; private: - OBJECTBASE_DERIVED_PRIVATE_COPY_MOVE(Model) + explicit Model(int) : ObjectBase(-1) { assert(this->id().invalid()); }; + void assign_new_unique_ids_recursive(); friend class cereal::access; friend class UndoRedo::StackImpl; template void serialize(Archive &ar) { - ar(cereal::base_class(this), materials, objects); + ar(materials, objects); } }; diff --git a/src/libslic3r/ObjectID.hpp b/src/libslic3r/ObjectID.hpp index f00d6f61ea..0988acf5a9 100644 --- a/src/libslic3r/ObjectID.hpp +++ b/src/libslic3r/ObjectID.hpp @@ -33,6 +33,7 @@ public: bool operator>=(const ObjectID &rhs) const { return this->id >= rhs.id; } bool valid() const { return id != 0; } + bool invalid() const { return id == 0; } size_t id; @@ -72,6 +73,7 @@ protected: void assign_new_unique_ids_recursive() { this->set_new_unique_id(); } private: + friend class UndoRedo::StackImpl; ObjectID m_id; static inline ObjectID generate_new_id() { return ObjectID(++ s_last_id); } diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 6d8c6e2bbb..e545b9b7bc 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -612,7 +612,7 @@ static inline void model_volume_list_copy_configs(ModelObject &model_object_dst, assert(mv_src.id() == mv_dst.id()); // Copy the ModelVolume data. mv_dst.name = mv_src.name; - mv_dst.config = mv_src.config; + static_cast(mv_dst.config) = static_cast(mv_src.config); //FIXME what to do with the materials? // mv_dst.m_material_id = mv_src.m_material_id; ++ i_src; @@ -899,7 +899,7 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co // Synchronize Object's config. bool object_config_changed = model_object.config != model_object_new.config; if (object_config_changed) - model_object.config = model_object_new.config; + static_cast(model_object.config) = static_cast(model_object_new.config); if (! object_diff.empty() || object_config_changed) { PrintObjectConfig new_config = PrintObject::object_config_from_model_object(m_default_object_config, model_object, num_extruders); auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id())); diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index aa7cf58b53..47b259f647 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -368,7 +368,7 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf // Synchronize Object's config. bool object_config_changed = model_object.config != model_object_new.config; if (object_config_changed) - model_object.config = model_object_new.config; + static_cast(model_object.config) = static_cast(model_object_new.config); if (! object_diff.empty() || object_config_changed) { SLAPrintObjectConfig new_config = m_default_object_config; normalize_and_apply_config(new_config, model_object.config); diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 97168ee045..0784b70ffe 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -1160,7 +1160,7 @@ void Selection::copy_to_clipboard() ModelObject* dst_object = m_clipboard.add_object(); dst_object->name = src_object->name; dst_object->input_file = src_object->input_file; - dst_object->config = src_object->config; + static_cast(dst_object->config) = static_cast(src_object->config); dst_object->sla_support_points = src_object->sla_support_points; dst_object->sla_points_status = src_object->sla_points_status; dst_object->layer_height_ranges = src_object->layer_height_ranges; diff --git a/src/slic3r/Utils/UndoRedo.cpp b/src/slic3r/Utils/UndoRedo.cpp index 1e49c13361..74565a3010 100644 --- a/src/slic3r/Utils/UndoRedo.cpp +++ b/src/slic3r/Utils/UndoRedo.cpp @@ -117,7 +117,9 @@ public: bool is_immutable() const override { return true; } void save(size_t active_snapshot_time, size_t current_time) { - assert(m_history.empty() || m_history.back().end() <= active_snapshot_time); + assert(m_history.empty() || m_history.back().end() <= active_snapshot_time || + // The snapshot of an immutable object may have already been taken from another mutable object. + (m_history.back().begin() <= active_snapshot_time && m_history.back().end() == current_time + 1)); if (m_history.empty() || m_history.back().end() < active_snapshot_time) m_history.emplace_back(active_snapshot_time, current_time + 1); else @@ -335,11 +337,11 @@ public: void load_model(const Slic3r::Model &model, size_t snapshot_time); void load_selection(const Slic3r::GUI::Selection &selection, size_t snapshot_time); - template ObjectID save_mutable_object(const T &object); + template ObjectID save_mutable_object(const T &object); template ObjectID save_immutable_object(std::shared_ptr &object); template T* load_mutable_object(const Slic3r::ObjectID id); template std::shared_ptr load_immutable_object(const Slic3r::ObjectID id); - template void load_mutable_object(const Slic3r::ObjectID id, T &target); + template void load_mutable_object(const Slic3r::ObjectID id, T &target); private: template ObjectID immutable_object_id(const std::shared_ptr &ptr) { @@ -379,6 +381,8 @@ class ModelObject; class ModelVolume; class ModelInstance; class ModelMaterial; +class ModelConfig; +class DynamicPrintConfig; class TriangleMesh; } // namespace Slic3r @@ -392,13 +396,14 @@ namespace cereal template struct specialize {}; template struct specialize {}; template struct specialize {}; + template struct specialize {}; template struct specialize, cereal::specialization::non_member_load_save> {}; // Store ObjectBase derived class onto the Undo / Redo stack as a separate object, // store just the ObjectID to this stream. template void save(BinaryOutputArchive& ar, T* const& ptr) { - ar(cereal::get_user_data(ar).save_mutable_object(*ptr)); + ar(cereal::get_user_data(ar).save_mutable_object(*ptr)); } // Load ObjectBase derived class from the Undo / Redo stack as a separate object @@ -411,6 +416,40 @@ namespace cereal ptr = stack.load_mutable_object(Slic3r::ObjectID(id)); } + // Store ObjectBase derived class onto the Undo / Redo stack as a separate object, + // store just the ObjectID to this stream. + template void save(BinaryOutputArchive &ar, const std::unique_ptr &ptr) + { + ar(cereal::get_user_data(ar).save_mutable_object(*ptr.get())); + } + + // Load ObjectBase derived class from the Undo / Redo stack as a separate object + // based on the ObjectID loaded from this stream. + template void load(BinaryInputArchive &ar, std::unique_ptr &ptr) + { + Slic3r::UndoRedo::StackImpl& stack = cereal::get_user_data(ar); + size_t id; + ar(id); + ptr.reset(stack.load_mutable_object(Slic3r::ObjectID(id))); + } + + // Store ObjectBase derived class onto the Undo / Redo stack as a separate object, + // store just the ObjectID to this stream. + void save(BinaryOutputArchive& ar, const Slic3r::ModelConfig &cfg) + { + ar(cereal::get_user_data(ar).save_mutable_object(cfg)); + } + + // Load ObjectBase derived class from the Undo / Redo stack as a separate object + // based on the ObjectID loaded from this stream. + void load(BinaryInputArchive& ar, Slic3r::ModelConfig &cfg) + { + Slic3r::UndoRedo::StackImpl& stack = cereal::get_user_data(ar); + size_t id; + ar(id); + stack.load_mutable_object(Slic3r::ObjectID(id), cfg); + } + // Store ObjectBase derived class onto the Undo / Redo stack as a separate object, // store just the ObjectID to this stream. template void save(BinaryOutputArchive &ar, const std::shared_ptr &ptr) @@ -458,7 +497,7 @@ namespace cereal namespace Slic3r { namespace UndoRedo { -template ObjectID StackImpl::save_mutable_object(const T &object) +template ObjectID StackImpl::save_mutable_object(const T &object) { // First find or allocate a history stack for the ObjectID of this object instance. auto it_object_history = m_objects.find(object.id()); @@ -469,7 +508,7 @@ template ObjectID StackImpl::save_mutable_object(const T &object) std::ostringstream oss; { Slic3r::UndoRedo::OutputArchive archive(*this, oss); - archive(object); + archive(static_cast(object)); } object_history->save(m_active_snapshot_time, m_current_time, oss.str()); return object.id(); @@ -491,7 +530,7 @@ template ObjectID StackImpl::save_immutable_object(std::shared_ptr T* StackImpl::load_mutable_object(const Slic3r::ObjectID id) { T *target = new T(); - this->load_mutable_object(id, *target); + this->load_mutable_object(id, *target); return target; } @@ -505,7 +544,7 @@ template std::shared_ptr StackImpl::load_immutable_object(c return object_history->shared_ptr(*this); } -template void StackImpl::load_mutable_object(const Slic3r::ObjectID id, T &target) +template void StackImpl::load_mutable_object(const Slic3r::ObjectID id, T &target) { // First find a history stack for the ObjectID of this object instance. auto it_object_history = m_objects.find(id); @@ -514,7 +553,8 @@ template void StackImpl::load_mutable_object(const Slic3r::ObjectID // Then get the data associated with the object history and m_active_snapshot_time. std::istringstream iss(object_history->load(m_active_snapshot_time)); Slic3r::UndoRedo::InputArchive archive(*this, iss); - archive(target); + target.m_id = id; + archive(static_cast(target)); } // The Undo / Redo stack is being initialized with an empty model and an empty selection. @@ -541,7 +581,7 @@ void StackImpl::take_snapshot(const std::string &snapshot_name, const Slic3r::Mo m_snapshots.erase(it, m_snapshots.end()); } // Take new snapshots. - this->save_mutable_object(model); + this->save_mutable_object(model); // this->save_mutable_object(selection); // Save the snapshot info m_snapshots.emplace_back(snapshot_name, m_current_time ++, model.id().id); @@ -556,8 +596,10 @@ void StackImpl::load_snapshot(size_t timestamp, Slic3r::Model &model, Slic3r::GU if (it_snapshot == m_snapshots.end() || it_snapshot->timestamp != timestamp) throw std::runtime_error((boost::format("Snapshot with timestamp %1% does not exist") % timestamp).str()); - this->load_mutable_object(ObjectID(it_snapshot->model_object_id), model); - this->load_mutable_object(selection.id(), selection); + model.clear_objects(); + model.clear_materials(); + this->load_mutable_object(ObjectID(it_snapshot->model_object_id), model); + this->load_mutable_object(selection.id(), selection); this->m_active_snapshot_time = timestamp; }