mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-11-02 20:51:23 -07:00 
			
		
		
		
	New class ModelConfig wrapping DynamicPrintConfig and a timestamp
to help with detecting "not changed" event when taking Undo/Redo snapshot or synchronizing with the back-end. Converted layer height profile and supports / seam painted areas to the same timestamp controlled structure.
This commit is contained in:
		
							parent
							
								
									0d6eb842b0
								
							
						
					
					
						commit
						54976e29bb
					
				
					 28 changed files with 366 additions and 215 deletions
				
			
		| 
						 | 
				
			
			@ -683,23 +683,23 @@ namespace Slic3r {
 | 
			
		|||
            // m_layer_heights_profiles are indexed by a 1 based model object index.
 | 
			
		||||
            IdToLayerHeightsProfileMap::iterator obj_layer_heights_profile = m_layer_heights_profiles.find(object.second + 1);
 | 
			
		||||
            if (obj_layer_heights_profile != m_layer_heights_profiles.end())
 | 
			
		||||
                model_object->layer_height_profile = obj_layer_heights_profile->second;
 | 
			
		||||
                model_object->layer_height_profile.set(std::move(obj_layer_heights_profile->second));
 | 
			
		||||
 | 
			
		||||
            // m_layer_config_ranges are indexed by a 1 based model object index.
 | 
			
		||||
            IdToLayerConfigRangesMap::iterator obj_layer_config_ranges = m_layer_config_ranges.find(object.second + 1);
 | 
			
		||||
            if (obj_layer_config_ranges != m_layer_config_ranges.end())
 | 
			
		||||
                model_object->layer_config_ranges = obj_layer_config_ranges->second;
 | 
			
		||||
                model_object->layer_config_ranges = std::move(obj_layer_config_ranges->second);
 | 
			
		||||
 | 
			
		||||
            // m_sla_support_points are indexed by a 1 based model object index.
 | 
			
		||||
            IdToSlaSupportPointsMap::iterator obj_sla_support_points = m_sla_support_points.find(object.second + 1);
 | 
			
		||||
            if (obj_sla_support_points != m_sla_support_points.end() && !obj_sla_support_points->second.empty()) {
 | 
			
		||||
                model_object->sla_support_points = obj_sla_support_points->second;
 | 
			
		||||
                model_object->sla_support_points = std::move(obj_sla_support_points->second);
 | 
			
		||||
                model_object->sla_points_status = sla::PointsStatus::UserModified;
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            IdToSlaDrainHolesMap::iterator obj_drain_holes = m_sla_drain_holes.find(object.second + 1);
 | 
			
		||||
            if (obj_drain_holes != m_sla_drain_holes.end() && !obj_drain_holes->second.empty()) {
 | 
			
		||||
                model_object->sla_drain_holes = obj_drain_holes->second;
 | 
			
		||||
                model_object->sla_drain_holes = std::move(obj_drain_holes->second);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            IdToMetadataMap::iterator obj_metadata = m_objects_metadata.find(object.first);
 | 
			
		||||
| 
						 | 
				
			
			@ -934,7 +934,7 @@ namespace Slic3r {
 | 
			
		|||
                    double max_z = range_tree.get<double>("<xmlattr>.max_z");
 | 
			
		||||
 | 
			
		||||
                    // get Z range information
 | 
			
		||||
                    DynamicPrintConfig& config = config_ranges[{ min_z, max_z }];
 | 
			
		||||
                    DynamicPrintConfig config;
 | 
			
		||||
 | 
			
		||||
                    for (const auto& option : range_tree)
 | 
			
		||||
                    {
 | 
			
		||||
| 
						 | 
				
			
			@ -945,10 +945,12 @@ namespace Slic3r {
 | 
			
		|||
 | 
			
		||||
                        config.set_deserialize(opt_key, value);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    config_ranges[{ min_z, max_z }].assign_config(std::move(config));
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (!config_ranges.empty())
 | 
			
		||||
                    m_layer_config_ranges.insert(IdToLayerConfigRangesMap::value_type(obj_idx, config_ranges));
 | 
			
		||||
                    m_layer_config_ranges.insert(IdToLayerConfigRangesMap::value_type(obj_idx, std::move(config_ranges)));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -2407,7 +2409,7 @@ namespace Slic3r {
 | 
			
		|||
            triangles_count += (int)its.indices.size();
 | 
			
		||||
            volume_it->second.last_triangle_id = triangles_count - 1;
 | 
			
		||||
 | 
			
		||||
            for (size_t i = 0; i < its.indices.size(); ++ i)
 | 
			
		||||
            for (int i = 0; i < int(its.indices.size()); ++ i)
 | 
			
		||||
            {
 | 
			
		||||
                stream << "     <" << TRIANGLE_TAG << " ";
 | 
			
		||||
                for (int j = 0; j < 3; ++j)
 | 
			
		||||
| 
						 | 
				
			
			@ -2472,7 +2474,7 @@ namespace Slic3r {
 | 
			
		|||
        for (const ModelObject* object : model.objects)
 | 
			
		||||
        {
 | 
			
		||||
            ++count;
 | 
			
		||||
            const std::vector<double> &layer_height_profile = object->layer_height_profile;
 | 
			
		||||
            const std::vector<double>& layer_height_profile = object->layer_height_profile.get();
 | 
			
		||||
            if ((layer_height_profile.size() >= 4) && ((layer_height_profile.size() % 2) == 0))
 | 
			
		||||
            {
 | 
			
		||||
                sprintf(buffer, "object_id=%d|", count);
 | 
			
		||||
| 
						 | 
				
			
			@ -2527,7 +2529,7 @@ namespace Slic3r {
 | 
			
		|||
                    range_tree.put("<xmlattr>.max_z", range.first.second);
 | 
			
		||||
 | 
			
		||||
                    // store range configuration
 | 
			
		||||
                    const DynamicPrintConfig& config = range.second;
 | 
			
		||||
                    const ModelConfig& config = range.second;
 | 
			
		||||
                    for (const std::string& opt_key : config.keys())
 | 
			
		||||
                    {
 | 
			
		||||
                        pt::ptree& opt_tree = range_tree.add("option", config.opt_serialize(opt_key));
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -688,7 +688,7 @@ void AMFParserContext::endElement(const char * /* name */)
 | 
			
		|||
        else if (strncmp(m_value[0].c_str(), "slic3r.", 7) == 0) {
 | 
			
		||||
            const char *opt_key = m_value[0].c_str() + 7;
 | 
			
		||||
            if (print_config_def.options.find(opt_key) != print_config_def.options.end()) {
 | 
			
		||||
                DynamicPrintConfig *config = nullptr;
 | 
			
		||||
                ModelConfig *config = nullptr;
 | 
			
		||||
                if (m_path.size() == 3) {
 | 
			
		||||
                    if (m_path[1] == NODE_TYPE_MATERIAL && m_material)
 | 
			
		||||
                        config = &m_material->config;
 | 
			
		||||
| 
						 | 
				
			
			@ -706,15 +706,17 @@ void AMFParserContext::endElement(const char * /* name */)
 | 
			
		|||
            } else if (m_path.size() == 3 && m_path[1] == NODE_TYPE_OBJECT && m_object && strcmp(opt_key, "layer_height_profile") == 0) {
 | 
			
		||||
                // Parse object's layer height profile, a semicolon separated list of floats.
 | 
			
		||||
                char *p = m_value[1].data();
 | 
			
		||||
                std::vector<coordf_t> data;
 | 
			
		||||
                for (;;) {
 | 
			
		||||
                    char *end = strchr(p, ';');
 | 
			
		||||
                    if (end != nullptr)
 | 
			
		||||
	                    *end = 0;
 | 
			
		||||
                    m_object->layer_height_profile.push_back(float(atof(p)));
 | 
			
		||||
                    data.emplace_back(float(atof(p)));
 | 
			
		||||
					if (end == nullptr)
 | 
			
		||||
						break;
 | 
			
		||||
					p = end + 1;
 | 
			
		||||
                }
 | 
			
		||||
                m_object->layer_height_profile.set(std::move(data));
 | 
			
		||||
            }
 | 
			
		||||
            else if (m_path.size() == 3 && m_path[1] == NODE_TYPE_OBJECT && m_object && strcmp(opt_key, "sla_support_points") == 0) {
 | 
			
		||||
                // Parse object's layer height profile, a semicolon separated list of floats.
 | 
			
		||||
| 
						 | 
				
			
			@ -1095,7 +1097,7 @@ bool store_amf(const char* path, Model* model, const DynamicPrintConfig* config,
 | 
			
		|||
            stream << "    <metadata type=\"slic3r." << key << "\">" << object->config.opt_serialize(key) << "</metadata>\n";
 | 
			
		||||
        if (!object->name.empty())
 | 
			
		||||
            stream << "    <metadata type=\"name\">" << xml_escape(object->name) << "</metadata>\n";
 | 
			
		||||
        const std::vector<double> &layer_height_profile = object->layer_height_profile;
 | 
			
		||||
        const std::vector<double> &layer_height_profile = object->layer_height_profile.get();
 | 
			
		||||
        if (layer_height_profile.size() >= 4 && (layer_height_profile.size() % 2) == 0) {
 | 
			
		||||
            // Store the layer height profile as a single semicolon separated list.
 | 
			
		||||
            stream << "    <metadata type=\"slic3r.layer_height_profile\">";
 | 
			
		||||
| 
						 | 
				
			
			@ -1112,7 +1114,7 @@ bool store_amf(const char* path, Model* model, const DynamicPrintConfig* config,
 | 
			
		|||
            // Store the layer config range as a single semicolon separated list.
 | 
			
		||||
            stream << "    <layer_config_ranges>\n";
 | 
			
		||||
            size_t layer_counter = 0;
 | 
			
		||||
            for (auto range : config_ranges) {
 | 
			
		||||
            for (const auto &range : config_ranges) {
 | 
			
		||||
                stream << "      <range id=\"" << layer_counter << "\">\n";
 | 
			
		||||
 | 
			
		||||
                stream << "        <metadata type=\"slic3r.layer_height_range\">";
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1050,7 +1050,7 @@ void ModelObject::convert_units(ModelObjectPtrs& new_objects, bool from_imperial
 | 
			
		|||
            ModelVolume* vol = new_object->add_volume(mesh);
 | 
			
		||||
            vol->name = volume->name;
 | 
			
		||||
            // Don't copy the config's ID.
 | 
			
		||||
            static_cast<DynamicPrintConfig&>(vol->config) = static_cast<const DynamicPrintConfig&>(volume->config);
 | 
			
		||||
            vol->config.assign_config(volume->config);
 | 
			
		||||
            assert(vol->config.id().valid());
 | 
			
		||||
            assert(vol->config.id() != volume->config.id());
 | 
			
		||||
            vol->set_material(volume->material_id(), *volume->material());
 | 
			
		||||
| 
						 | 
				
			
			@ -1193,7 +1193,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b
 | 
			
		|||
                ModelVolume* vol = upper->add_volume(upper_mesh);
 | 
			
		||||
                vol->name	= volume->name;
 | 
			
		||||
                // Don't copy the config's ID.
 | 
			
		||||
				static_cast<DynamicPrintConfig&>(vol->config) = static_cast<const DynamicPrintConfig&>(volume->config);
 | 
			
		||||
                vol->config.assign_config(volume->config);
 | 
			
		||||
    			assert(vol->config.id().valid());
 | 
			
		||||
	    		assert(vol->config.id() != volume->config.id());
 | 
			
		||||
                vol->set_material(volume->material_id(), *volume->material());
 | 
			
		||||
| 
						 | 
				
			
			@ -1202,8 +1202,8 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b
 | 
			
		|||
                ModelVolume* vol = lower->add_volume(lower_mesh);
 | 
			
		||||
                vol->name	= volume->name;
 | 
			
		||||
                // Don't copy the config's ID.
 | 
			
		||||
				static_cast<DynamicPrintConfig&>(vol->config) = static_cast<const DynamicPrintConfig&>(volume->config);
 | 
			
		||||
    			assert(vol->config.id().valid());
 | 
			
		||||
                vol->config.assign_config(volume->config);
 | 
			
		||||
                assert(vol->config.id().valid());
 | 
			
		||||
	    		assert(vol->config.id() != volume->config.id());
 | 
			
		||||
                vol->set_material(volume->material_id(), *volume->material());
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1280,7 +1280,7 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
 | 
			
		|||
        ModelObject* new_object = m_model->add_object();    
 | 
			
		||||
        new_object->name   = this->name;
 | 
			
		||||
        // Don't copy the config's ID.
 | 
			
		||||
		static_cast<DynamicPrintConfig&>(new_object->config) = static_cast<const DynamicPrintConfig&>(this->config);
 | 
			
		||||
		new_object->config.assign_config(this->config);
 | 
			
		||||
		assert(new_object->config.id().valid());
 | 
			
		||||
		assert(new_object->config.id() != this->config.id());
 | 
			
		||||
        new_object->instances.reserve(this->instances.size());
 | 
			
		||||
| 
						 | 
				
			
			@ -1867,7 +1867,6 @@ arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const
 | 
			
		|||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
indexed_triangle_set FacetsAnnotation::get_facets(const ModelVolume& mv, EnforcerBlockerType type) const
 | 
			
		||||
{
 | 
			
		||||
    TriangleSelector selector(mv.mesh());
 | 
			
		||||
| 
						 | 
				
			
			@ -1876,29 +1875,23 @@ indexed_triangle_set FacetsAnnotation::get_facets(const ModelVolume& mv, Enforce
 | 
			
		|||
    return out;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
bool FacetsAnnotation::set(const TriangleSelector& selector)
 | 
			
		||||
{
 | 
			
		||||
    std::map<int, std::vector<bool>> sel_map = selector.serialize();
 | 
			
		||||
    if (sel_map != m_data) {
 | 
			
		||||
        m_data = sel_map;
 | 
			
		||||
        update_timestamp();
 | 
			
		||||
        this->touch();
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void FacetsAnnotation::clear()
 | 
			
		||||
{
 | 
			
		||||
    m_data.clear();
 | 
			
		||||
    update_timestamp();
 | 
			
		||||
    this->reset_timestamp();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// Following function takes data from a triangle and encodes it as string
 | 
			
		||||
// of hexadecimal numbers (one digit per triangle). Used for 3MF export,
 | 
			
		||||
// changing it may break backwards compatibility !!!!!
 | 
			
		||||
| 
						 | 
				
			
			@ -1926,8 +1919,6 @@ std::string FacetsAnnotation::get_triangle_as_string(int triangle_idx) const
 | 
			
		|||
    return out;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// Recover triangle splitting & state from string of hexadecimal values previously
 | 
			
		||||
// generated by get_triangle_as_string. Used to load from 3MF.
 | 
			
		||||
void FacetsAnnotation::set_triangle_from_string(int triangle_id, const std::string& str)
 | 
			
		||||
| 
						 | 
				
			
			@ -1951,12 +1942,8 @@ void FacetsAnnotation::set_triangle_from_string(int triangle_id, const std::stri
 | 
			
		|||
            code.insert(code.end(), bool(dec & (1 << i)));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// Test whether the two models contain the same number of ModelObjects with the same set of IDs
 | 
			
		||||
// ordered in the same order. In that case it is not necessary to kill the background processing.
 | 
			
		||||
bool model_object_list_equal(const Model &model_old, const Model &model_new)
 | 
			
		||||
| 
						 | 
				
			
			@ -2024,7 +2011,7 @@ bool model_custom_supports_data_changed(const ModelObject& mo, const ModelObject
 | 
			
		|||
    assert(! model_volume_list_changed(mo, mo_new, ModelVolumeType::MODEL_PART));
 | 
			
		||||
    assert(mo.volumes.size() == mo_new.volumes.size());
 | 
			
		||||
    for (size_t i=0; i<mo.volumes.size(); ++i) {
 | 
			
		||||
        if (! mo_new.volumes[i]->m_supported_facets.is_same_as(mo.volumes[i]->m_supported_facets))
 | 
			
		||||
        if (! mo_new.volumes[i]->m_supported_facets.timestamp_matches(mo.volumes[i]->m_supported_facets))
 | 
			
		||||
            return true;
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
| 
						 | 
				
			
			@ -2034,7 +2021,7 @@ bool model_custom_seam_data_changed(const ModelObject& mo, const ModelObject& mo
 | 
			
		|||
    assert(! model_volume_list_changed(mo, mo_new, ModelVolumeType::MODEL_PART));
 | 
			
		||||
    assert(mo.volumes.size() == mo_new.volumes.size());
 | 
			
		||||
    for (size_t i=0; i<mo.volumes.size(); ++i) {
 | 
			
		||||
        if (! mo_new.volumes[i]->m_seam_facets.is_same_as(mo.volumes[i]->m_seam_facets))
 | 
			
		||||
        if (! mo_new.volumes[i]->m_seam_facets.timestamp_matches(mo.volumes[i]->m_seam_facets))
 | 
			
		||||
            return true;
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
| 
						 | 
				
			
			@ -2050,7 +2037,7 @@ extern bool model_has_multi_part_objects(const Model &model)
 | 
			
		|||
 | 
			
		||||
extern bool model_has_advanced_features(const Model &model)
 | 
			
		||||
{
 | 
			
		||||
	auto config_is_advanced = [](const DynamicPrintConfig &config) {
 | 
			
		||||
	auto config_is_advanced = [](const ModelConfig &config) {
 | 
			
		||||
        return ! (config.empty() || (config.size() == 1 && config.cbegin()->first == "extruder"));
 | 
			
		||||
	};
 | 
			
		||||
    for (const ModelObject *model_object : model.objects) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,7 +18,6 @@
 | 
			
		|||
#include <string>
 | 
			
		||||
#include <utility>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <chrono>
 | 
			
		||||
 | 
			
		||||
namespace cereal {
 | 
			
		||||
	class BinaryInputArchive;
 | 
			
		||||
| 
						 | 
				
			
			@ -45,7 +44,7 @@ namespace UndoRedo {
 | 
			
		|||
	class StackImpl;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class ModelConfig : public ObjectBase, public DynamicPrintConfig
 | 
			
		||||
class ModelConfigObject : public ObjectBase, public ModelConfig
 | 
			
		||||
{
 | 
			
		||||
private:
 | 
			
		||||
	friend class cereal::access;
 | 
			
		||||
| 
						 | 
				
			
			@ -56,21 +55,25 @@ private:
 | 
			
		|||
 | 
			
		||||
    // Constructors to be only called by derived classes.
 | 
			
		||||
    // Default constructor to assign a unique ID.
 | 
			
		||||
    explicit ModelConfig() {}
 | 
			
		||||
    explicit ModelConfigObject() {}
 | 
			
		||||
    // 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) {}
 | 
			
		||||
    explicit ModelConfigObject(int) : ObjectBase(-1) {}
 | 
			
		||||
    // Copy constructor copies the ID.
 | 
			
		||||
	explicit ModelConfig(const ModelConfig &cfg) : ObjectBase(-1), DynamicPrintConfig(cfg) { this->copy_id(cfg); }
 | 
			
		||||
	explicit ModelConfigObject(const ModelConfigObject &cfg) : ObjectBase(-1), ModelConfig(cfg) { this->copy_id(cfg); }
 | 
			
		||||
    // Move constructor copies the ID.
 | 
			
		||||
	explicit ModelConfig(ModelConfig &&cfg) : ObjectBase(-1), DynamicPrintConfig(std::move(cfg)) { this->copy_id(cfg); }
 | 
			
		||||
	explicit ModelConfigObject(ModelConfigObject &&cfg) : ObjectBase(-1), ModelConfig(std::move(cfg)) { this->copy_id(cfg); }
 | 
			
		||||
 | 
			
		||||
	ModelConfig& operator=(const ModelConfig &rhs) = default;
 | 
			
		||||
    ModelConfig& operator=(ModelConfig &&rhs) = default;
 | 
			
		||||
    Timestamp          timestamp() const throw() override { return this->ModelConfig::timestamp(); }
 | 
			
		||||
    bool               object_id_and_timestamp_match(const ModelConfigObject &rhs) const throw() { return this->id() == rhs.id() && this->timestamp() == rhs.timestamp(); }
 | 
			
		||||
 | 
			
		||||
	template<class Archive> void serialize(Archive &ar) {
 | 
			
		||||
		ar(cereal::base_class<DynamicPrintConfig>(this));
 | 
			
		||||
	}
 | 
			
		||||
    // called by ModelObject::assign_copy()
 | 
			
		||||
	ModelConfigObject& operator=(const ModelConfigObject &rhs) = default;
 | 
			
		||||
    ModelConfigObject& operator=(ModelConfigObject &&rhs) = default;
 | 
			
		||||
 | 
			
		||||
    template<class Archive> void serialize(Archive &ar) {
 | 
			
		||||
        ar(cereal::base_class<ModelConfig>(this));
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
namespace Internal {
 | 
			
		||||
| 
						 | 
				
			
			@ -136,7 +139,7 @@ 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.
 | 
			
		||||
    ModelConfig config;
 | 
			
		||||
    ModelConfigObject config;
 | 
			
		||||
 | 
			
		||||
    Model* get_model() const { return m_model; }
 | 
			
		||||
    void apply(const t_model_material_attributes &attributes)
 | 
			
		||||
| 
						 | 
				
			
			@ -162,7 +165,7 @@ private:
 | 
			
		|||
	ModelMaterial() : ObjectBase(-1), config(-1), m_model(nullptr) { assert(this->id().invalid()); assert(this->config.id().invalid()); }
 | 
			
		||||
	template<class Archive> void serialize(Archive &ar) { 
 | 
			
		||||
		assert(this->id().invalid()); assert(this->config.id().invalid());
 | 
			
		||||
		Internal::StaticSerializationWrapper<ModelConfig> config_wrapper(config);
 | 
			
		||||
		Internal::StaticSerializationWrapper<ModelConfigObject> config_wrapper(config);
 | 
			
		||||
		ar(attributes, config_wrapper);
 | 
			
		||||
		// assert(this->id().valid()); assert(this->config.id().valid());
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -173,6 +176,23 @@ private:
 | 
			
		|||
    ModelMaterial& operator=(ModelMaterial &&rhs) = delete;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class LayerHeightProfile final : public ObjectWithTimestamp {
 | 
			
		||||
public:
 | 
			
		||||
    std::vector<coordf_t> get() const throw() { return m_data; }
 | 
			
		||||
    bool                  empty() const throw() { return m_data.empty(); }
 | 
			
		||||
    void                  set(const std::vector<coordf_t> &data) { if (m_data != data) { m_data = data; this->touch(); } }
 | 
			
		||||
    void                  set(std::vector<coordf_t> &&data) { if (m_data != data) { m_data = std::move(data); this->touch(); } }
 | 
			
		||||
    void                  clear() { m_data.clear(); this->touch(); }
 | 
			
		||||
 | 
			
		||||
    template<class Archive> void serialize(Archive &ar)
 | 
			
		||||
    {
 | 
			
		||||
        ar(cereal::base_class<ObjectWithTimestamp>(this), m_data);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    std::vector<coordf_t> m_data;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// A printable object, possibly having multiple print volumes (each with its own set of parameters and materials),
 | 
			
		||||
// and possibly having multiple modifier volumes, each modifier volume with its set of parameters and materials.
 | 
			
		||||
// Each ModelObject may be instantiated mutliple times, each instance having different placement on the print bed,
 | 
			
		||||
| 
						 | 
				
			
			@ -189,12 +209,12 @@ public:
 | 
			
		|||
    // ModelVolumes are owned by this ModelObject.
 | 
			
		||||
    ModelVolumePtrs         volumes;
 | 
			
		||||
    // Configuration parameters specific to a single ModelObject, overriding the global Slic3r settings.
 | 
			
		||||
    ModelConfig      		config;
 | 
			
		||||
    ModelConfigObject 		config;
 | 
			
		||||
    // Variation of a layer thickness for spans of Z coordinates + optional parameter overrides.
 | 
			
		||||
    t_layer_config_ranges   layer_config_ranges;
 | 
			
		||||
    // Profile of increasing z to a layer height, to be linearly interpolated when calculating the layers.
 | 
			
		||||
    // The pairs of <z, layer_height> are packed into a 1D array.
 | 
			
		||||
    std::vector<coordf_t>   layer_height_profile;
 | 
			
		||||
    LayerHeightProfile      layer_height_profile;
 | 
			
		||||
    // Whether or not this object is printable
 | 
			
		||||
    bool                    printable;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -381,8 +401,10 @@ private:
 | 
			
		|||
	}
 | 
			
		||||
	template<class Archive> void serialize(Archive &ar) {
 | 
			
		||||
		ar(cereal::base_class<ObjectBase>(this));
 | 
			
		||||
		Internal::StaticSerializationWrapper<ModelConfig> config_wrapper(config);
 | 
			
		||||
        ar(name, input_file, instances, volumes, config_wrapper, layer_config_ranges, layer_height_profile, sla_support_points, sla_points_status, sla_drain_holes, printable, origin_translation,
 | 
			
		||||
		Internal::StaticSerializationWrapper<ModelConfigObject> config_wrapper(config);
 | 
			
		||||
        Internal::StaticSerializationWrapper<LayerHeightProfile> layer_heigth_profile_wrapper(layer_height_profile);
 | 
			
		||||
        ar(name, input_file, instances, volumes, config_wrapper, layer_config_ranges, layer_heigth_profile_wrapper, 
 | 
			
		||||
            sla_support_points, sla_points_status, sla_drain_holes, printable, 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);
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -403,34 +425,27 @@ enum class EnforcerBlockerType : int8_t {
 | 
			
		|||
    BLOCKER   = 2
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class FacetsAnnotation {
 | 
			
		||||
class FacetsAnnotation final : public ObjectWithTimestamp {
 | 
			
		||||
public:
 | 
			
		||||
    using ClockType = std::chrono::steady_clock;
 | 
			
		||||
 | 
			
		||||
    const std::map<int, std::vector<bool>>& get_data() const { return m_data; }
 | 
			
		||||
    void assign(const FacetsAnnotation &rhs) { if (! this->timestamp_matches(rhs)) this->m_data = rhs.m_data; }
 | 
			
		||||
    void assign(FacetsAnnotation &&rhs) { if (! this->timestamp_matches(rhs)) this->m_data = rhs.m_data; }
 | 
			
		||||
    const std::map<int, std::vector<bool>>& get_data() const throw() { return m_data; }
 | 
			
		||||
    bool set(const TriangleSelector& selector);
 | 
			
		||||
    indexed_triangle_set get_facets(const ModelVolume& mv, EnforcerBlockerType type) const;
 | 
			
		||||
    void clear();
 | 
			
		||||
    std::string get_triangle_as_string(int i) const;
 | 
			
		||||
    void set_triangle_from_string(int triangle_id, const std::string& str);
 | 
			
		||||
 | 
			
		||||
    ClockType::time_point get_timestamp() const { return timestamp; }
 | 
			
		||||
    bool is_same_as(const FacetsAnnotation& other) const {
 | 
			
		||||
        return timestamp == other.get_timestamp();
 | 
			
		||||
    }
 | 
			
		||||
private:
 | 
			
		||||
    friend class cereal::access;
 | 
			
		||||
    friend class UndoRedo::StackImpl;
 | 
			
		||||
 | 
			
		||||
    template<class Archive> void serialize(Archive &ar)
 | 
			
		||||
    {
 | 
			
		||||
        ar(m_data);
 | 
			
		||||
        ar(cereal::base_class<ObjectWithTimestamp>(this), m_data);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    std::map<int, std::vector<bool>> m_data;
 | 
			
		||||
 | 
			
		||||
    ClockType::time_point timestamp;
 | 
			
		||||
    void update_timestamp() {
 | 
			
		||||
        timestamp = ClockType::now();
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// An object STL, or a modifier volume, over which a different set of parameters shall be applied.
 | 
			
		||||
| 
						 | 
				
			
			@ -465,7 +480,7 @@ public:
 | 
			
		|||
	void				reset_mesh() { m_mesh = std::make_shared<const TriangleMesh>(); }
 | 
			
		||||
    // Configuration parameters specific to an object model geometry or a modifier volume, 
 | 
			
		||||
    // overriding the global Slic3r settings and the ModelObject settings.
 | 
			
		||||
    ModelConfig  		config;
 | 
			
		||||
    ModelConfigObject	config;
 | 
			
		||||
 | 
			
		||||
    // List of mesh facets to be supported/unsupported.
 | 
			
		||||
    FacetsAnnotation    m_supported_facets;
 | 
			
		||||
| 
						 | 
				
			
			@ -634,8 +649,9 @@ private:
 | 
			
		|||
	}
 | 
			
		||||
	template<class Archive> void load(Archive &ar) {
 | 
			
		||||
		bool has_convex_hull;
 | 
			
		||||
        ar(name, source, m_mesh, m_type, m_material_id, m_transformation,
 | 
			
		||||
           m_is_splittable, has_convex_hull, m_supported_facets, m_seam_facets);
 | 
			
		||||
        ar(name, source, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull);
 | 
			
		||||
        cereal::load_by_value(ar, m_supported_facets);
 | 
			
		||||
        cereal::load_by_value(ar, m_seam_facets);
 | 
			
		||||
        cereal::load_by_value(ar, config);
 | 
			
		||||
		assert(m_mesh);
 | 
			
		||||
		if (has_convex_hull) {
 | 
			
		||||
| 
						 | 
				
			
			@ -648,8 +664,9 @@ private:
 | 
			
		|||
	}
 | 
			
		||||
	template<class Archive> void save(Archive &ar) const {
 | 
			
		||||
		bool has_convex_hull = m_convex_hull.get() != nullptr;
 | 
			
		||||
        ar(name, source, m_mesh, m_type, m_material_id, m_transformation,
 | 
			
		||||
           m_is_splittable, has_convex_hull, m_supported_facets, m_seam_facets);
 | 
			
		||||
        ar(name, source, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull);
 | 
			
		||||
        cereal::save_by_value(ar, m_supported_facets);
 | 
			
		||||
        cereal::save_by_value(ar, m_seam_facets);
 | 
			
		||||
        cereal::save_by_value(ar, config);
 | 
			
		||||
		if (has_convex_hull)
 | 
			
		||||
			cereal::save_optional(ar, m_convex_hull);
 | 
			
		||||
| 
						 | 
				
			
			@ -935,7 +952,7 @@ void check_model_ids_equal(const Model &model1, const Model &model2);
 | 
			
		|||
namespace cereal
 | 
			
		||||
{
 | 
			
		||||
	template <class Archive> struct specialize<Archive, Slic3r::ModelVolume, cereal::specialization::member_load_save> {};
 | 
			
		||||
	template <class Archive> struct specialize<Archive, Slic3r::ModelConfig, cereal::specialization::member_serialize> {};
 | 
			
		||||
	template <class Archive> struct specialize<Archive, Slic3r::ModelConfigObject, cereal::specialization::member_serialize> {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif /* slic3r_Model_hpp_ */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,6 +17,8 @@ ObjectID wipe_tower_instance_id()
 | 
			
		|||
    return mine.id();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t ObjectWithTimestamp::s_last_timestamp = 1;
 | 
			
		||||
 | 
			
		||||
} // namespace Slic3r
 | 
			
		||||
 | 
			
		||||
// CEREAL_REGISTER_TYPE(Slic3r::ObjectBase)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -49,12 +49,14 @@ private:
 | 
			
		|||
class ObjectBase
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    using Timestamp = uint64_t;
 | 
			
		||||
 | 
			
		||||
    ObjectID     		id() const { return m_id; }
 | 
			
		||||
    // Return an optional timestamp of this object.
 | 
			
		||||
    // If the timestamp returned is non-zero, then the serialization framework will
 | 
			
		||||
    // only save this object on the Undo/Redo stack if the timestamp is different
 | 
			
		||||
    // from the timestmap of the object at the top of the Undo / Redo stack.
 | 
			
		||||
    virtual uint64_t	timestamp() const { return 0; }
 | 
			
		||||
    virtual Timestamp	timestamp() const { return 0; }
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    // Constructors to be only called by derived classes.
 | 
			
		||||
| 
						 | 
				
			
			@ -91,6 +93,42 @@ private:
 | 
			
		|||
  	template<class Archive> static void load_and_construct(Archive & ar, cereal::construct<ObjectBase> &construct) { ObjectID id; ar(id); construct(id); }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class ObjectWithTimestamp : public ObjectBase
 | 
			
		||||
{
 | 
			
		||||
protected:
 | 
			
		||||
    // Constructors to be only called by derived classes.
 | 
			
		||||
    // Default constructor to assign a new timestamp unique to this object's history.
 | 
			
		||||
	ObjectWithTimestamp() = default;
 | 
			
		||||
    // Constructor with ignored int parameter to assign an invalid ID, to be replaced
 | 
			
		||||
    // by an existing ID copied from elsewhere.
 | 
			
		||||
    ObjectWithTimestamp(int) : ObjectBase(-1) {}
 | 
			
		||||
	// The class tree will have virtual tables and type information.
 | 
			
		||||
	virtual ~ObjectWithTimestamp() = default;
 | 
			
		||||
 | 
			
		||||
    // Resetting timestamp to 1 indicates the object is in its initial (cleared) state.
 | 
			
		||||
    // To be called by the derived class's clear() method.
 | 
			
		||||
    void                reset_timestamp() { m_timestamp = 1; }
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    // Return an optional timestamp of this object.
 | 
			
		||||
    // If the timestamp returned is non-zero, then the serialization framework will
 | 
			
		||||
    // only save this object on the Undo/Redo stack if the timestamp is different
 | 
			
		||||
    // from the timestmap of the object at the top of the Undo / Redo stack.
 | 
			
		||||
    Timestamp	        timestamp() const throw() override { return m_timestamp; }
 | 
			
		||||
    bool 				timestamp_matches(const ObjectWithTimestamp &rhs) const throw() { return m_timestamp == rhs.m_timestamp; }
 | 
			
		||||
    bool 				object_id_and_timestamp_match(const ObjectWithTimestamp &rhs) const throw() { return this->id() == rhs.id() && m_timestamp == rhs.m_timestamp; }
 | 
			
		||||
    void 				touch() { m_timestamp = ++ s_last_timestamp; }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	// The first timestamp is non-zero, as zero timestamp means the timestamp is not reliable.
 | 
			
		||||
	Timestamp 			m_timestamp { 1 };
 | 
			
		||||
    static Timestamp    s_last_timestamp;
 | 
			
		||||
	
 | 
			
		||||
	friend class cereal::access;
 | 
			
		||||
	friend class Slic3r::UndoRedo::StackImpl;
 | 
			
		||||
	template<class Archive> void serialize(Archive &ar) { ar(m_timestamp); }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Unique object / instance ID for the wipe tower.
 | 
			
		||||
extern ObjectID wipe_tower_object_id();
 | 
			
		||||
extern ObjectID wipe_tower_instance_id();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -403,9 +403,11 @@ 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;
 | 
			
		||||
		static_cast<DynamicPrintConfig&>(mv_dst.config) = static_cast<const DynamicPrintConfig&>(mv_src.config);
 | 
			
		||||
        mv_dst.m_supported_facets = mv_src.m_supported_facets;
 | 
			
		||||
        mv_dst.m_seam_facets = mv_src.m_seam_facets;
 | 
			
		||||
		mv_dst.config.assign_config(mv_src.config);
 | 
			
		||||
        if (! mv_dst.m_supported_facets.timestamp_matches(mv_src.m_supported_facets))
 | 
			
		||||
            mv_dst.m_supported_facets = mv_src.m_supported_facets;
 | 
			
		||||
        if (! mv_dst.m_seam_facets.timestamp_matches(mv_src.m_seam_facets))
 | 
			
		||||
            mv_dst.m_seam_facets = mv_src.m_seam_facets;
 | 
			
		||||
        //FIXME what to do with the materials?
 | 
			
		||||
        // mv_dst.m_material_id = mv_src.m_material_id;
 | 
			
		||||
        ++ i_src;
 | 
			
		||||
| 
						 | 
				
			
			@ -644,7 +646,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
 | 
			
		|||
            m_ranges.reserve(in.size());
 | 
			
		||||
            // Input ranges are sorted lexicographically. First range trims the other ranges.
 | 
			
		||||
            coordf_t last_z = 0;
 | 
			
		||||
            for (const std::pair<const t_layer_height_range, DynamicPrintConfig> &range : in)
 | 
			
		||||
            for (const std::pair<const t_layer_height_range, ModelConfig> &range : in)
 | 
			
		||||
				if (range.first.second > last_z) {
 | 
			
		||||
                    coordf_t min_z = std::max(range.first.first, 0.);
 | 
			
		||||
                    if (min_z > last_z + EPSILON) {
 | 
			
		||||
| 
						 | 
				
			
			@ -652,7 +654,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
 | 
			
		|||
                        last_z = min_z;
 | 
			
		||||
                    }
 | 
			
		||||
                    if (range.first.second > last_z + EPSILON) {
 | 
			
		||||
						const DynamicPrintConfig* cfg = &range.second;
 | 
			
		||||
						const DynamicPrintConfig *cfg = &range.second.get();
 | 
			
		||||
                        m_ranges.emplace_back(t_layer_height_range(last_z, range.first.second), cfg);
 | 
			
		||||
                        last_z = range.first.second;
 | 
			
		||||
                    }
 | 
			
		||||
| 
						 | 
				
			
			@ -845,8 +847,8 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
 | 
			
		|||
        bool support_blockers_differ    = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_BLOCKER);
 | 
			
		||||
        bool support_enforcers_differ   = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_ENFORCER);
 | 
			
		||||
        if (model_parts_differ || modifiers_differ || 
 | 
			
		||||
            model_object.origin_translation         != model_object_new.origin_translation   ||
 | 
			
		||||
            model_object.layer_height_profile       != model_object_new.layer_height_profile ||
 | 
			
		||||
            model_object.origin_translation != model_object_new.origin_translation   ||
 | 
			
		||||
            ! model_object.layer_height_profile.timestamp_matches(model_object_new.layer_height_profile) ||
 | 
			
		||||
            ! layer_height_ranges_equal(model_object.layer_config_ranges, model_object_new.layer_config_ranges, model_object_new.layer_height_profile.empty())) {
 | 
			
		||||
            // The very first step (the slicing step) is invalidated. One may freely remove all associated PrintObjects.
 | 
			
		||||
            auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id()));
 | 
			
		||||
| 
						 | 
				
			
			@ -874,9 +876,9 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
 | 
			
		|||
        }
 | 
			
		||||
        if (! model_parts_differ && ! modifiers_differ) {
 | 
			
		||||
            // Synchronize Object's config.
 | 
			
		||||
            bool object_config_changed = model_object.config != model_object_new.config;
 | 
			
		||||
            bool object_config_changed = ! model_object.config.timestamp_matches(model_object_new.config);
 | 
			
		||||
			if (object_config_changed)
 | 
			
		||||
				static_cast<DynamicPrintConfig&>(model_object.config) = static_cast<const DynamicPrintConfig&>(model_object_new.config);
 | 
			
		||||
				model_object.config.assign_config(model_object_new.config);
 | 
			
		||||
            if (! object_diff.empty() || object_config_changed || num_extruders_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()));
 | 
			
		||||
| 
						 | 
				
			
			@ -1577,7 +1579,7 @@ void Print::auto_assign_extruders(ModelObject* model_object) const
 | 
			
		|||
        ModelVolume *volume = model_object->volumes[volume_id];
 | 
			
		||||
        //FIXME Vojtech: This assigns an extruder ID even to a modifier volume, if it has a material assigned.
 | 
			
		||||
        if ((volume->is_model_part() || volume->is_modifier()) && ! volume->material_id().empty() && ! volume->config.has("extruder"))
 | 
			
		||||
            volume->config.opt<ConfigOptionInt>("extruder", true)->value = int(volume_id + 1);
 | 
			
		||||
            volume->config.set("extruder", int(volume_id + 1));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3718,6 +3718,8 @@ void DynamicPrintAndCLIConfig::handle_legacy(t_config_option_key &opt_key, std::
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint64_t ModelConfig::s_last_timestamp = 1;
 | 
			
		||||
 | 
			
		||||
static Points to_points(const std::vector<Vec2d> &dpts)
 | 
			
		||||
{
 | 
			
		||||
    Points pts; pts.reserve(dpts.size());
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1369,6 +1369,96 @@ Points get_bed_shape(const DynamicPrintConfig &cfg);
 | 
			
		|||
Points get_bed_shape(const PrintConfig &cfg);
 | 
			
		||||
Points get_bed_shape(const SLAPrinterConfig &cfg);
 | 
			
		||||
 | 
			
		||||
// ModelConfig is a wrapper around DynamicPrintConfig with an addition of a timestamp.
 | 
			
		||||
// Each change of ModelConfig is tracked by assigning a new timestamp from a global counter.
 | 
			
		||||
// The counter is used for faster synchronization of the background slicing thread
 | 
			
		||||
// with the front end by skipping synchronization of equal config dictionaries. 
 | 
			
		||||
// The global counter is also used for avoiding unnecessary serialization of config 
 | 
			
		||||
// dictionaries when taking an Undo snapshot.
 | 
			
		||||
//
 | 
			
		||||
// The global counter is NOT thread safe, therefore it is recommended to use ModelConfig from
 | 
			
		||||
// the main thread only.
 | 
			
		||||
// 
 | 
			
		||||
// As there is a global counter and it is being increased with each change to any ModelConfig,
 | 
			
		||||
// if two ModelConfig dictionaries differ, they should differ with their timestamp as well.
 | 
			
		||||
// Therefore copying the ModelConfig including its timestamp is safe as there is no harm
 | 
			
		||||
// in having multiple ModelConfig with equal timestamps as long as their dictionaries are equal.
 | 
			
		||||
//
 | 
			
		||||
// The timestamp is used by the Undo/Redo stack. As zero timestamp means invalid timestamp
 | 
			
		||||
// to the Undo/Redo stack (zero timestamp means the Undo/Redo stack needs to serialize and
 | 
			
		||||
// compare serialized data for differences), zero timestamp shall never be used.
 | 
			
		||||
// Timestamp==1 shall only be used for empty dictionaries.
 | 
			
		||||
class ModelConfig
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    void         clear() { m_data.clear(); m_timestamp = 1; }
 | 
			
		||||
 | 
			
		||||
    // Modification of the ModelConfig is not thread safe due to the global timestamp counter!
 | 
			
		||||
    // Don't call modification methods from the back-end!
 | 
			
		||||
    void         assign_config(const ModelConfig &rhs) {
 | 
			
		||||
        if (m_timestamp != rhs.m_timestamp) {
 | 
			
		||||
            m_data      = rhs.m_data;
 | 
			
		||||
            m_timestamp = rhs.m_timestamp;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    void         assign_config(ModelConfig &&rhs) {
 | 
			
		||||
        if (m_timestamp != rhs.m_timestamp) {
 | 
			
		||||
            m_data      = std::move(rhs.m_data);
 | 
			
		||||
            m_timestamp = rhs.m_timestamp;
 | 
			
		||||
            rhs.clear();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    // Assign methods don't assign if src==dst to not having to bump the timestamp in case they are equal.
 | 
			
		||||
    void         assign_config(const DynamicPrintConfig &rhs)  { if (m_data != rhs) { m_data = rhs; this->touch(); } }
 | 
			
		||||
    void         assign_config(DynamicPrintConfig &&rhs)       { if (m_data != rhs) { m_data = std::move(rhs); this->touch(); } }
 | 
			
		||||
    void         apply(const ModelConfig &other, bool ignore_nonexistent = false) { this->apply(other.get(), ignore_nonexistent); }
 | 
			
		||||
    void         apply(const ConfigBase &other, bool ignore_nonexistent = false) { m_data.apply_only(other, other.keys(), ignore_nonexistent); this->touch(); }
 | 
			
		||||
    void         apply_only(const ModelConfig &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) { this->apply_only(other.get(), keys, ignore_nonexistent); }
 | 
			
		||||
    void         apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) { m_data.apply_only(other, keys, ignore_nonexistent); this->touch(); }
 | 
			
		||||
    bool         set_key_value(const std::string &opt_key, ConfigOption *opt) { bool out = m_data.set_key_value(opt_key, opt); this->touch(); return out; }
 | 
			
		||||
    template<typename T>
 | 
			
		||||
    void         set(const std::string &opt_key, T value) { m_data.set(opt_key, value, true); this->touch(); }
 | 
			
		||||
    void         set_deserialize(const t_config_option_key &opt_key, const std::string &str, bool append = false)
 | 
			
		||||
        { m_data.set_deserialize(opt_key, str, append); this->touch(); }
 | 
			
		||||
    bool         erase(const t_config_option_key &opt_key) { bool out = m_data.erase(opt_key); if (out) this->touch(); return out; }
 | 
			
		||||
 | 
			
		||||
    // Getters are thread safe.
 | 
			
		||||
    // The following implicit conversion breaks the Cereal serialization.
 | 
			
		||||
//    operator const DynamicPrintConfig&() const throw() { return this->get(); }
 | 
			
		||||
    const DynamicPrintConfig&   get() const throw() { return m_data; }
 | 
			
		||||
    bool                        empty() const throw() { return m_data.empty(); }
 | 
			
		||||
    size_t                      size() const throw() { return m_data.size(); }
 | 
			
		||||
    auto                        cbegin() const { return m_data.cbegin(); }
 | 
			
		||||
    auto                        cend() const { return m_data.cend(); }
 | 
			
		||||
    t_config_option_keys        keys() const { return m_data.keys(); }
 | 
			
		||||
    bool                        has(const t_config_option_key &opt_key) const { return m_data.has(opt_key); }
 | 
			
		||||
    const ConfigOption*         option(const t_config_option_key &opt_key) const { return m_data.option(opt_key); }
 | 
			
		||||
    int                         opt_int(const t_config_option_key &opt_key) const { return m_data.opt_int(opt_key); }
 | 
			
		||||
    int                         extruder() const { return opt_int("extruder"); }
 | 
			
		||||
    double                      opt_float(const t_config_option_key &opt_key) const { return m_data.opt_float(opt_key); }
 | 
			
		||||
    std::string                 opt_serialize(const t_config_option_key &opt_key) const { return m_data.opt_serialize(opt_key); }
 | 
			
		||||
 | 
			
		||||
    // Return an optional timestamp of this object.
 | 
			
		||||
    // If the timestamp returned is non-zero, then the serialization framework will
 | 
			
		||||
    // only save this object on the Undo/Redo stack if the timestamp is different
 | 
			
		||||
    // from the timestmap of the object at the top of the Undo / Redo stack.
 | 
			
		||||
    virtual uint64_t    timestamp() const throw() { return m_timestamp; }
 | 
			
		||||
    bool                timestamp_matches(const ModelConfig &rhs) const throw() { return m_timestamp == rhs.m_timestamp; }
 | 
			
		||||
    // Not thread safe! Should not be called from other than the main thread!
 | 
			
		||||
    void                touch() { m_timestamp = ++ s_last_timestamp; }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    friend class cereal::access;
 | 
			
		||||
    template<class Archive> void serialize(Archive& ar) { ar(m_timestamp); ar(m_data); }
 | 
			
		||||
 | 
			
		||||
    uint64_t                    m_timestamp { 1 };
 | 
			
		||||
    DynamicPrintConfig          m_data;
 | 
			
		||||
 | 
			
		||||
    static uint64_t             s_last_timestamp;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template<typename CONFIG> void normalize_and_apply_config(CONFIG& dst, const ModelConfig& src) { normalize_and_apply_config(dst, src.get()); }
 | 
			
		||||
 | 
			
		||||
} // namespace Slic3r
 | 
			
		||||
 | 
			
		||||
// Serialization through the Cereal library
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1592,13 +1592,13 @@ SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig& full
 | 
			
		|||
				print_config,
 | 
			
		||||
				region_config_from_model_volume(default_region_config, nullptr, *model_volume, num_extruders),
 | 
			
		||||
				object_extruders);
 | 
			
		||||
			for (const std::pair<const t_layer_height_range, DynamicPrintConfig> &range_and_config : model_object.layer_config_ranges)
 | 
			
		||||
			for (const std::pair<const t_layer_height_range, ModelConfig> &range_and_config : model_object.layer_config_ranges)
 | 
			
		||||
				if (range_and_config.second.has("perimeter_extruder") ||
 | 
			
		||||
					range_and_config.second.has("infill_extruder") ||
 | 
			
		||||
					range_and_config.second.has("solid_infill_extruder"))
 | 
			
		||||
					PrintRegion::collect_object_printing_extruders(
 | 
			
		||||
						print_config,
 | 
			
		||||
						region_config_from_model_volume(default_region_config, &range_and_config.second, *model_volume, num_extruders),
 | 
			
		||||
						region_config_from_model_volume(default_region_config, &range_and_config.second.get(), *model_volume, num_extruders),
 | 
			
		||||
						object_extruders);
 | 
			
		||||
		}
 | 
			
		||||
    sort_remove_duplicates(object_extruders);
 | 
			
		||||
| 
						 | 
				
			
			@ -1626,7 +1626,7 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c
 | 
			
		|||
 | 
			
		||||
    if (layer_height_profile.empty()) {
 | 
			
		||||
        // use the constructor because the assignement is crashing on ASAN OsX
 | 
			
		||||
        layer_height_profile = std::vector<coordf_t>(model_object.layer_height_profile);
 | 
			
		||||
        layer_height_profile = std::vector<coordf_t>(model_object.layer_height_profile.get());
 | 
			
		||||
//        layer_height_profile = model_object.layer_height_profile;
 | 
			
		||||
        updated = true;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -2872,7 +2872,7 @@ void PrintObject::project_and_append_custom_facets(
 | 
			
		|||
 | 
			
		||||
        // Now append the collected polygons to respective layers.
 | 
			
		||||
        for (auto& trg : projections_of_triangles) {
 | 
			
		||||
            int layer_id = trg.first_layer_id;
 | 
			
		||||
            int layer_id = int(trg.first_layer_id);
 | 
			
		||||
            for (const LightPolygon& poly : trg.polygons) {
 | 
			
		||||
                if (layer_id >= int(expolys.size()))
 | 
			
		||||
                    break; // part of triangle could be projected above top layer
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -395,9 +395,9 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, DynamicPrintConfig con
 | 
			
		|||
                model_object.assign_copy(model_object_new);
 | 
			
		||||
            } else {
 | 
			
		||||
                // Synchronize Object's config.
 | 
			
		||||
                bool object_config_changed = model_object.config != model_object_new.config;
 | 
			
		||||
                bool object_config_changed = ! model_object.config.timestamp_matches(model_object_new.config);
 | 
			
		||||
                if (object_config_changed)
 | 
			
		||||
                    static_cast<DynamicPrintConfig&>(model_object.config) = static_cast<const DynamicPrintConfig&>(model_object_new.config);
 | 
			
		||||
                    model_object.config.assign_config(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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -170,24 +170,15 @@ SlicingParameters SlicingParameters::create_from_config(
 | 
			
		|||
    return params;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<std::pair<t_layer_height_range, coordf_t>> layer_height_ranges(const t_layer_config_ranges &config_ranges)
 | 
			
		||||
{
 | 
			
		||||
	std::vector<std::pair<t_layer_height_range, coordf_t>> out;
 | 
			
		||||
	out.reserve(config_ranges.size());
 | 
			
		||||
	for (const auto &kvp : config_ranges)
 | 
			
		||||
		out.emplace_back(kvp.first, kvp.second.option("layer_height")->getFloat());
 | 
			
		||||
	return out;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Convert layer_config_ranges to layer_height_profile. Both are referenced to z=0, meaning the raft layers are not accounted for
 | 
			
		||||
// in the height profile and the printed object may be lifted by the raft thickness at the time of the G-code generation.
 | 
			
		||||
std::vector<coordf_t> layer_height_profile_from_ranges(
 | 
			
		||||
	const SlicingParameters 	&slicing_params,
 | 
			
		||||
	const t_layer_config_ranges &layer_config_ranges)                           // #ys_FIXME_experiment
 | 
			
		||||
	const t_layer_config_ranges &layer_config_ranges)
 | 
			
		||||
{
 | 
			
		||||
    // 1) If there are any height ranges, trim one by the other to make them non-overlapping. Insert the 1st layer if fixed.
 | 
			
		||||
    std::vector<std::pair<t_layer_height_range,coordf_t>> ranges_non_overlapping;
 | 
			
		||||
    ranges_non_overlapping.reserve(layer_config_ranges.size() * 4);             // #ys_FIXME_experiment
 | 
			
		||||
    ranges_non_overlapping.reserve(layer_config_ranges.size() * 4);
 | 
			
		||||
    if (slicing_params.first_object_layer_height_fixed())
 | 
			
		||||
        ranges_non_overlapping.push_back(std::pair<t_layer_height_range,coordf_t>(
 | 
			
		||||
            t_layer_height_range(0., slicing_params.first_object_layer_height), 
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,6 +17,7 @@ namespace Slic3r
 | 
			
		|||
 | 
			
		||||
class PrintConfig;
 | 
			
		||||
class PrintObjectConfig;
 | 
			
		||||
class ModelConfig;
 | 
			
		||||
class ModelObject;
 | 
			
		||||
class DynamicPrintConfig;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -128,9 +129,7 @@ inline bool equal_layering(const SlicingParameters &sp1, const SlicingParameters
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
typedef std::pair<coordf_t,coordf_t> t_layer_height_range;
 | 
			
		||||
typedef std::map<t_layer_height_range, DynamicPrintConfig> t_layer_config_ranges;
 | 
			
		||||
 | 
			
		||||
extern std::vector<std::pair<t_layer_height_range, coordf_t>> layer_height_ranges(const t_layer_config_ranges &config_ranges);
 | 
			
		||||
typedef std::map<t_layer_height_range, ModelConfig> t_layer_config_ranges;
 | 
			
		||||
 | 
			
		||||
extern std::vector<coordf_t> layer_height_profile_from_ranges(
 | 
			
		||||
    const SlicingParameters     &slicing_params,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,6 +2,7 @@
 | 
			
		|||
#include "ConfigManipulation.hpp"
 | 
			
		||||
#include "I18N.hpp"
 | 
			
		||||
#include "GUI_App.hpp"
 | 
			
		||||
#include "libslic3r/Model.hpp"
 | 
			
		||||
#include "libslic3r/PresetBundle.hpp"
 | 
			
		||||
 | 
			
		||||
#include <wx/msgdlg.h>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,9 +10,11 @@
 | 
			
		|||
 | 
			
		||||
#include "libslic3r/PrintConfig.hpp"
 | 
			
		||||
#include "Field.hpp"
 | 
			
		||||
//#include <boost-1_70/boost/any.hpp>
 | 
			
		||||
 | 
			
		||||
namespace Slic3r {
 | 
			
		||||
 | 
			
		||||
class ModelConfig;
 | 
			
		||||
 | 
			
		||||
namespace GUI {
 | 
			
		||||
 | 
			
		||||
class ConfigManipulation
 | 
			
		||||
| 
						 | 
				
			
			@ -24,13 +26,13 @@ class ConfigManipulation
 | 
			
		|||
    std::function<Field* (const std::string&, int opt_index)>   get_field = nullptr;
 | 
			
		||||
    // callback to propagation of changed value, if needed 
 | 
			
		||||
    std::function<void(const std::string&, const boost::any&)>  cb_value_change = nullptr;
 | 
			
		||||
    DynamicPrintConfig* local_config = nullptr;
 | 
			
		||||
    ModelConfig* local_config = nullptr;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    ConfigManipulation(std::function<void()> load_config,
 | 
			
		||||
        std::function<Field* (const std::string&, int opt_index)> get_field,
 | 
			
		||||
        std::function<void(const std::string&, const boost::any&)>  cb_value_change,
 | 
			
		||||
        DynamicPrintConfig* local_config = nullptr) :
 | 
			
		||||
        ModelConfig* local_config = nullptr) :
 | 
			
		||||
        load_config(load_config),
 | 
			
		||||
        get_field(get_field),
 | 
			
		||||
        cb_value_change(cb_value_change),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -511,7 +511,7 @@ void GLCanvas3D::LayersEditing::adaptive_layer_height_profile(GLCanvas3D& canvas
 | 
			
		|||
{
 | 
			
		||||
    this->update_slicing_parameters();
 | 
			
		||||
    m_layer_height_profile = layer_height_profile_adaptive(*m_slicing_parameters, *m_model_object, quality_factor);
 | 
			
		||||
    const_cast<ModelObject*>(m_model_object)->layer_height_profile = m_layer_height_profile;
 | 
			
		||||
    const_cast<ModelObject*>(m_model_object)->layer_height_profile.set(m_layer_height_profile);
 | 
			
		||||
    m_layers_texture.valid = false;
 | 
			
		||||
    canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -520,7 +520,7 @@ void GLCanvas3D::LayersEditing::smooth_layer_height_profile(GLCanvas3D& canvas,
 | 
			
		|||
{
 | 
			
		||||
    this->update_slicing_parameters();
 | 
			
		||||
    m_layer_height_profile = smooth_height_profile(m_layer_height_profile, *m_slicing_parameters, smoothing_params);
 | 
			
		||||
    const_cast<ModelObject*>(m_model_object)->layer_height_profile = m_layer_height_profile;
 | 
			
		||||
    const_cast<ModelObject*>(m_model_object)->layer_height_profile.set(m_layer_height_profile);
 | 
			
		||||
    m_layers_texture.valid = false;
 | 
			
		||||
    canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -560,7 +560,7 @@ void GLCanvas3D::LayersEditing::accept_changes(GLCanvas3D& canvas)
 | 
			
		|||
    if (last_object_id >= 0) {
 | 
			
		||||
        if (m_layer_height_profile_modified) {
 | 
			
		||||
            wxGetApp().plater()->take_snapshot(_(L("Variable layer height - Manual edit")));
 | 
			
		||||
            const_cast<ModelObject*>(m_model_object)->layer_height_profile = m_layer_height_profile;
 | 
			
		||||
            const_cast<ModelObject*>(m_model_object)->layer_height_profile.set(m_layer_height_profile);
 | 
			
		||||
			canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -153,7 +153,7 @@ wxSizer* ObjectLayers::create_layer(const t_layer_height_range& range, PlusMinus
 | 
			
		|||
    
 | 
			
		||||
void ObjectLayers::create_layers_list()
 | 
			
		||||
{
 | 
			
		||||
    for (const auto layer : m_object->layer_config_ranges)
 | 
			
		||||
    for (const auto &layer : m_object->layer_config_ranges)
 | 
			
		||||
    {
 | 
			
		||||
        const t_layer_height_range& range = layer.first;
 | 
			
		||||
        auto del_btn = new PlusMinusButton(m_parent, m_bmp_delete, range);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -468,7 +468,7 @@ int ObjectList::get_selected_obj_idx() const
 | 
			
		|||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DynamicPrintConfig& ObjectList::get_item_config(const wxDataViewItem& item) const 
 | 
			
		||||
ModelConfig& ObjectList::get_item_config(const wxDataViewItem& item) const 
 | 
			
		||||
{
 | 
			
		||||
    assert(item);
 | 
			
		||||
    const ItemType type = m_objects_model->GetItemType(item);
 | 
			
		||||
| 
						 | 
				
			
			@ -492,10 +492,10 @@ void ObjectList::update_extruder_values_for_items(const size_t max_extruder)
 | 
			
		|||
        auto object = (*m_objects)[i];
 | 
			
		||||
        wxString extruder;
 | 
			
		||||
        if (!object->config.has("extruder") ||
 | 
			
		||||
            size_t(object->config.option<ConfigOptionInt>("extruder")->value) > max_extruder)
 | 
			
		||||
            size_t(object->config.extruder()) > max_extruder)
 | 
			
		||||
            extruder = _(L("default"));
 | 
			
		||||
        else
 | 
			
		||||
            extruder = wxString::Format("%d", object->config.option<ConfigOptionInt>("extruder")->value);
 | 
			
		||||
            extruder = wxString::Format("%d", object->config.extruder());
 | 
			
		||||
 | 
			
		||||
        m_objects_model->SetExtruder(extruder, item);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -504,10 +504,10 @@ void ObjectList::update_extruder_values_for_items(const size_t max_extruder)
 | 
			
		|||
                item = m_objects_model->GetItemByVolumeId(i, id);
 | 
			
		||||
                if (!item) continue;
 | 
			
		||||
                if (!object->volumes[id]->config.has("extruder") ||
 | 
			
		||||
                    size_t(object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value) > max_extruder)
 | 
			
		||||
                    size_t(object->volumes[id]->config.extruder()) > max_extruder)
 | 
			
		||||
                    extruder = _(L("default"));
 | 
			
		||||
                else
 | 
			
		||||
                    extruder = wxString::Format("%d", object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value); 
 | 
			
		||||
                    extruder = wxString::Format("%d", object->volumes[id]->config.extruder()); 
 | 
			
		||||
 | 
			
		||||
                m_objects_model->SetExtruder(extruder, item);
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			@ -767,8 +767,7 @@ void ObjectList::copy_settings_to_clipboard()
 | 
			
		|||
    if (m_objects_model->GetItemType(item) & itSettings)
 | 
			
		||||
        item = m_objects_model->GetParent(item);
 | 
			
		||||
 | 
			
		||||
    DynamicPrintConfig& config_cache = m_clipboard.get_config_cache();
 | 
			
		||||
    config_cache = get_item_config(item);
 | 
			
		||||
    m_clipboard.get_config_cache() = get_item_config(item).get();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ObjectList::paste_settings_into_list()
 | 
			
		||||
| 
						 | 
				
			
			@ -799,7 +798,7 @@ void ObjectList::paste_settings_into_list()
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    // Add settings item for object/sub-object and show them 
 | 
			
		||||
    show_settings(add_settings_item(item, m_config));
 | 
			
		||||
    show_settings(add_settings_item(item, &m_config->get()));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ObjectList::paste_volumes_into_list(int obj_idx, const ModelVolumePtrs& volumes)
 | 
			
		||||
| 
						 | 
				
			
			@ -818,8 +817,8 @@ void ObjectList::paste_volumes_into_list(int obj_idx, const ModelVolumePtrs& vol
 | 
			
		|||
    {
 | 
			
		||||
        const wxDataViewItem& vol_item = m_objects_model->AddVolumeChild(object_item, wxString::FromUTF8(volume->name.c_str()), volume->type(), 
 | 
			
		||||
            volume->get_mesh_errors_count()>0 ,
 | 
			
		||||
            volume->config.has("extruder") ? volume->config.option<ConfigOptionInt>("extruder")->value : 0);
 | 
			
		||||
        add_settings_item(vol_item, &volume->config);
 | 
			
		||||
            volume->config.has("extruder") ? volume->config.extruder() : 0);
 | 
			
		||||
        add_settings_item(vol_item, &volume->config.get());
 | 
			
		||||
        items.Add(vol_item);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1480,7 +1479,7 @@ void ObjectList::get_settings_choice(const wxString& category_name)
 | 
			
		|||
    // Add settings item for object/sub-object and show them 
 | 
			
		||||
    if (!(item_type & (itObject | itVolume | itLayer)))
 | 
			
		||||
        item = m_objects_model->GetTopParent(item);
 | 
			
		||||
    show_settings(add_settings_item(item, m_config));
 | 
			
		||||
    show_settings(add_settings_item(item, &m_config->get()));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ObjectList::get_freq_settings_choice(const wxString& bundle_name)
 | 
			
		||||
| 
						 | 
				
			
			@ -1537,7 +1536,7 @@ void ObjectList::get_freq_settings_choice(const wxString& bundle_name)
 | 
			
		|||
    // Add settings item for object/sub-object and show them 
 | 
			
		||||
    if (!(item_type & (itObject | itVolume | itLayer)))
 | 
			
		||||
        item = m_objects_model->GetTopParent(item);
 | 
			
		||||
    show_settings(add_settings_item(item, m_config));
 | 
			
		||||
    show_settings(add_settings_item(item, &m_config->get()));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ObjectList::show_settings(const wxDataViewItem settings_item)
 | 
			
		||||
| 
						 | 
				
			
			@ -1821,9 +1820,8 @@ void ObjectList::append_menu_item_change_extruder(wxMenu* menu)
 | 
			
		|||
 | 
			
		||||
    int initial_extruder = -1; // negative value for multiple object/part selection
 | 
			
		||||
    if (sels.Count()==1) {
 | 
			
		||||
        DynamicPrintConfig& config = get_item_config(sels[0]);
 | 
			
		||||
        initial_extruder = !config.has("extruder") ? 0 : 
 | 
			
		||||
                            config.option<ConfigOptionInt>("extruder")->value;
 | 
			
		||||
        const ModelConfig &config = get_item_config(sels[0]);
 | 
			
		||||
        initial_extruder = config.has("extruder") ? config.extruder() : 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (int i = 0; i <= extruders_cnt; i++)
 | 
			
		||||
| 
						 | 
				
			
			@ -2320,9 +2318,7 @@ void ObjectList::del_settings_from_config(const wxDataViewItem& parent_item)
 | 
			
		|||
 | 
			
		||||
    take_snapshot(_(L("Delete Settings")));
 | 
			
		||||
 | 
			
		||||
    int extruder = -1;
 | 
			
		||||
    if (m_config->has("extruder"))
 | 
			
		||||
        extruder = m_config->option<ConfigOptionInt>("extruder")->value;
 | 
			
		||||
    int extruder = m_config->has("extruder") ? m_config->extruder() : -1;
 | 
			
		||||
 | 
			
		||||
    coordf_t layer_height = 0.0;
 | 
			
		||||
    if (is_layer_settings)
 | 
			
		||||
| 
						 | 
				
			
			@ -2459,11 +2455,10 @@ void ObjectList::split()
 | 
			
		|||
        const wxDataViewItem& vol_item = m_objects_model->AddVolumeChild(parent, from_u8(volume->name),
 | 
			
		||||
            volume->is_modifier() ? ModelVolumeType::PARAMETER_MODIFIER : ModelVolumeType::MODEL_PART,
 | 
			
		||||
            volume->get_mesh_errors_count()>0,
 | 
			
		||||
            volume->config.has("extruder") ?
 | 
			
		||||
            volume->config.option<ConfigOptionInt>("extruder")->value : 0,
 | 
			
		||||
            volume->config.has("extruder") ? volume->config.extruder() : 0,
 | 
			
		||||
            false);
 | 
			
		||||
        // add settings to the part, if it has those
 | 
			
		||||
        add_settings_item(vol_item, &volume->config);
 | 
			
		||||
        add_settings_item(vol_item, &volume->config.get());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    model_object->input_file.clear();
 | 
			
		||||
| 
						 | 
				
			
			@ -2579,7 +2574,7 @@ void ObjectList::merge(bool to_multipart_object)
 | 
			
		|||
        Model* model = (*m_objects)[0]->get_model();
 | 
			
		||||
        ModelObject* new_object = model->add_object();
 | 
			
		||||
        new_object->name = _u8L("Merged");
 | 
			
		||||
        DynamicPrintConfig* config = &new_object->config;
 | 
			
		||||
        ModelConfig &config = new_object->config;
 | 
			
		||||
 | 
			
		||||
        for (int obj_idx : obj_idxs)
 | 
			
		||||
        {
 | 
			
		||||
| 
						 | 
				
			
			@ -2616,8 +2611,8 @@ void ObjectList::merge(bool to_multipart_object)
 | 
			
		|||
            }
 | 
			
		||||
 | 
			
		||||
            // merge settings
 | 
			
		||||
            auto new_opt_keys = config->keys();
 | 
			
		||||
            const DynamicPrintConfig& from_config = object->config;
 | 
			
		||||
            auto new_opt_keys = config.keys();
 | 
			
		||||
            const ModelConfig& from_config = object->config;
 | 
			
		||||
            auto opt_keys = from_config.keys();
 | 
			
		||||
 | 
			
		||||
            for (auto& opt_key : opt_keys) {
 | 
			
		||||
| 
						 | 
				
			
			@ -2628,7 +2623,7 @@ void ObjectList::merge(bool to_multipart_object)
 | 
			
		|||
                        // get it from default config values
 | 
			
		||||
                        option = DynamicPrintConfig::new_from_defaults_keys({ opt_key })->option(opt_key);
 | 
			
		||||
                    }
 | 
			
		||||
                    config->set_key_value(opt_key, option->clone());
 | 
			
		||||
                    config.set_key_value(opt_key, option->clone());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            // save extruder value if it was set
 | 
			
		||||
| 
						 | 
				
			
			@ -2695,7 +2690,7 @@ void ObjectList::layers_editing()
 | 
			
		|||
        // set some default value
 | 
			
		||||
        if (ranges.empty()) {
 | 
			
		||||
            take_snapshot(_(L("Add Layers")));
 | 
			
		||||
            ranges[{ 0.0f, 2.0f }] = get_default_layer_config(obj_idx);
 | 
			
		||||
            ranges[{ 0.0f, 2.0f }].assign_config(get_default_layer_config(obj_idx));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // create layer root item
 | 
			
		||||
| 
						 | 
				
			
			@ -3011,8 +3006,7 @@ void ObjectList::add_object_to_list(size_t obj_idx, bool call_selection_changed)
 | 
			
		|||
    auto model_object = (*m_objects)[obj_idx];
 | 
			
		||||
    const wxString& item_name = from_u8(model_object->name);
 | 
			
		||||
    const auto item = m_objects_model->Add(item_name,
 | 
			
		||||
                      !model_object->config.has("extruder") ? 0 :
 | 
			
		||||
                      model_object->config.option<ConfigOptionInt>("extruder")->value,
 | 
			
		||||
                      model_object->config.has("extruder") ? model_object->config.extruder() : 0,
 | 
			
		||||
                      get_mesh_errors_count(obj_idx) > 0);
 | 
			
		||||
 | 
			
		||||
    // add volumes to the object
 | 
			
		||||
| 
						 | 
				
			
			@ -3022,10 +3016,9 @@ void ObjectList::add_object_to_list(size_t obj_idx, bool call_selection_changed)
 | 
			
		|||
                from_u8(volume->name),
 | 
			
		||||
                volume->type(),
 | 
			
		||||
                volume->get_mesh_errors_count()>0,
 | 
			
		||||
                !volume->config.has("extruder") ? 0 :
 | 
			
		||||
                volume->config.option<ConfigOptionInt>("extruder")->value,
 | 
			
		||||
                volume->config.has("extruder") ? volume->config.extruder() : 0,
 | 
			
		||||
                false);
 | 
			
		||||
            add_settings_item(vol_item, &volume->config);
 | 
			
		||||
            add_settings_item(vol_item, &volume->config.get());
 | 
			
		||||
        }
 | 
			
		||||
        Expand(item);
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -3045,7 +3038,7 @@ void ObjectList::add_object_to_list(size_t obj_idx, bool call_selection_changed)
 | 
			
		|||
        m_objects_model->SetPrintableState(model_object->instances[0]->printable ? piPrintable : piUnprintable, obj_idx);
 | 
			
		||||
 | 
			
		||||
    // add settings to the object, if it has those
 | 
			
		||||
    add_settings_item(item, &model_object->config);
 | 
			
		||||
    add_settings_item(item, &model_object->config.get());
 | 
			
		||||
 | 
			
		||||
    // Add layers if it has
 | 
			
		||||
    add_layer_root_item(item);
 | 
			
		||||
| 
						 | 
				
			
			@ -3123,7 +3116,7 @@ void ObjectList::delete_from_model_and_list(const std::vector<ItemForDelete>& it
 | 
			
		|||
                if ((*m_objects)[item->obj_idx]->volumes.size() == 1 && 
 | 
			
		||||
                    (*m_objects)[item->obj_idx]->config.has("extruder"))
 | 
			
		||||
                {
 | 
			
		||||
                    const wxString extruder = wxString::Format("%d", (*m_objects)[item->obj_idx]->config.option<ConfigOptionInt>("extruder")->value);
 | 
			
		||||
                    const wxString extruder = wxString::Format("%d", (*m_objects)[item->obj_idx]->config.extruder());
 | 
			
		||||
                    m_objects_model->SetExtruder(extruder, m_objects_model->GetItemById(item->obj_idx));
 | 
			
		||||
                }
 | 
			
		||||
                wxGetApp().plater()->canvas3D()->ensure_on_bed(item->obj_idx);
 | 
			
		||||
| 
						 | 
				
			
			@ -3289,7 +3282,7 @@ void ObjectList::add_layer_range_after_current(const t_layer_height_range curren
 | 
			
		|||
 | 
			
		||||
    const wxDataViewItem layers_item = GetSelection();
 | 
			
		||||
 | 
			
		||||
    t_layer_config_ranges& ranges = object(obj_idx)->layer_config_ranges;
 | 
			
		||||
    auto& ranges = object(obj_idx)->layer_config_ranges;
 | 
			
		||||
    auto it_range = ranges.find(current_range);
 | 
			
		||||
    assert(it_range != ranges.end());
 | 
			
		||||
    if (it_range == ranges.end())
 | 
			
		||||
| 
						 | 
				
			
			@ -3305,7 +3298,7 @@ void ObjectList::add_layer_range_after_current(const t_layer_height_range curren
 | 
			
		|||
        changed = true;
 | 
			
		||||
 | 
			
		||||
        const t_layer_height_range new_range = { current_range.second, current_range.second + 2. };
 | 
			
		||||
        ranges[new_range] = get_default_layer_config(obj_idx);
 | 
			
		||||
        ranges[new_range].assign_config(get_default_layer_config(obj_idx));
 | 
			
		||||
        add_layer_item(new_range, layers_item);
 | 
			
		||||
    }
 | 
			
		||||
    else if (const std::pair<coordf_t, coordf_t> &next_range = it_next_range->first; current_range.second <= next_range.first)
 | 
			
		||||
| 
						 | 
				
			
			@ -3342,7 +3335,7 @@ void ObjectList::add_layer_range_after_current(const t_layer_height_range curren
 | 
			
		|||
                    add_layer_item(new_range, layers_item, layer_idx);
 | 
			
		||||
 | 
			
		||||
                    new_range = { current_range.second, middle_layer_z };
 | 
			
		||||
                    ranges[new_range] = get_default_layer_config(obj_idx);
 | 
			
		||||
                    ranges[new_range].assign_config(get_default_layer_config(obj_idx));
 | 
			
		||||
                    add_layer_item(new_range, layers_item, layer_idx);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			@ -3353,7 +3346,7 @@ void ObjectList::add_layer_range_after_current(const t_layer_height_range curren
 | 
			
		|||
                changed = true;
 | 
			
		||||
 | 
			
		||||
                const t_layer_height_range new_range = { current_range.second, next_range.first };
 | 
			
		||||
                ranges[new_range] = get_default_layer_config(obj_idx);
 | 
			
		||||
                ranges[new_range].assign_config(get_default_layer_config(obj_idx));
 | 
			
		||||
                add_layer_item(new_range, layers_item, layer_idx);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -3379,7 +3372,7 @@ wxString ObjectList::can_add_new_range_after_current(const t_layer_height_range
 | 
			
		|||
        // This should not happen.
 | 
			
		||||
        return "ObjectList assert";
 | 
			
		||||
 | 
			
		||||
    t_layer_config_ranges& ranges = object(obj_idx)->layer_config_ranges;
 | 
			
		||||
    auto& ranges = object(obj_idx)->layer_config_ranges;
 | 
			
		||||
    auto it_range = ranges.find(current_range);
 | 
			
		||||
    assert(it_range != ranges.end());
 | 
			
		||||
    if (it_range == ranges.end())
 | 
			
		||||
| 
						 | 
				
			
			@ -3418,7 +3411,7 @@ void ObjectList::add_layer_item(const t_layer_height_range& range,
 | 
			
		|||
    const int obj_idx = m_objects_model->GetObjectIdByItem(layers_item);
 | 
			
		||||
    if (obj_idx < 0) return;
 | 
			
		||||
 | 
			
		||||
    const DynamicPrintConfig& config = object(obj_idx)->layer_config_ranges[range];
 | 
			
		||||
    const DynamicPrintConfig& config = object(obj_idx)->layer_config_ranges[range].get();
 | 
			
		||||
    if (!config.has("extruder"))
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -3438,7 +3431,7 @@ bool ObjectList::edit_layer_range(const t_layer_height_range& range, coordf_t la
 | 
			
		|||
    if (obj_idx < 0) 
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
    DynamicPrintConfig* config = &object(obj_idx)->layer_config_ranges[range];
 | 
			
		||||
    ModelConfig* config = &object(obj_idx)->layer_config_ranges[range];
 | 
			
		||||
    if (fabs(layer_height - config->opt_float("layer_height")) < EPSILON)
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -3467,12 +3460,14 @@ bool ObjectList::edit_layer_range(const t_layer_height_range& range, const t_lay
 | 
			
		|||
 | 
			
		||||
    const ItemType sel_type = m_objects_model->GetItemType(GetSelection());
 | 
			
		||||
 | 
			
		||||
    t_layer_config_ranges& ranges = object(obj_idx)->layer_config_ranges;
 | 
			
		||||
    auto& ranges = object(obj_idx)->layer_config_ranges;
 | 
			
		||||
 | 
			
		||||
    const DynamicPrintConfig config = ranges[range];
 | 
			
		||||
    {
 | 
			
		||||
        ModelConfig config = std::move(ranges[range]);
 | 
			
		||||
        ranges.erase(range);
 | 
			
		||||
        ranges[new_range] = std::move(config);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ranges.erase(range);
 | 
			
		||||
    ranges[new_range] = config;
 | 
			
		||||
    changed_object(obj_idx);
 | 
			
		||||
    
 | 
			
		||||
    wxDataViewItem root_item = m_objects_model->GetLayerRootItem(m_objects_model->GetItemById(obj_idx));
 | 
			
		||||
| 
						 | 
				
			
			@ -4040,7 +4035,7 @@ void ObjectList::change_part_type()
 | 
			
		|||
    }
 | 
			
		||||
    else if (!settings_item && 
 | 
			
		||||
              (new_type == ModelVolumeType::MODEL_PART || new_type == ModelVolumeType::PARAMETER_MODIFIER)) {
 | 
			
		||||
        add_settings_item(item, &volume->config);
 | 
			
		||||
        add_settings_item(item, &volume->config.get());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -4065,14 +4060,14 @@ void ObjectList::update_and_show_object_settings_item()
 | 
			
		|||
    if (!item) return;
 | 
			
		||||
 | 
			
		||||
    const wxDataViewItem& obj_item = m_objects_model->IsSettingsItem(item) ? m_objects_model->GetParent(item) : item;
 | 
			
		||||
    select_item(add_settings_item(obj_item, &get_item_config(obj_item)));
 | 
			
		||||
    select_item(add_settings_item(obj_item, &get_item_config(obj_item).get()));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Update settings item for item had it
 | 
			
		||||
void ObjectList::update_settings_item_and_selection(wxDataViewItem item, wxDataViewItemArray& selections)
 | 
			
		||||
{
 | 
			
		||||
    const wxDataViewItem old_settings_item = m_objects_model->GetSettingsItem(item);
 | 
			
		||||
    const wxDataViewItem new_settings_item = add_settings_item(item, &get_item_config(item));
 | 
			
		||||
    const wxDataViewItem new_settings_item = add_settings_item(item, &get_item_config(item).get());
 | 
			
		||||
 | 
			
		||||
    if (!new_settings_item && old_settings_item)
 | 
			
		||||
        m_objects_model->Delete(old_settings_item);
 | 
			
		||||
| 
						 | 
				
			
			@ -4489,19 +4484,19 @@ void ObjectList::set_extruder_for_selected_items(const int extruder) const
 | 
			
		|||
 | 
			
		||||
    for (const wxDataViewItem& item : sels)
 | 
			
		||||
    {
 | 
			
		||||
        DynamicPrintConfig& config = get_item_config(item);
 | 
			
		||||
        ModelConfig& config = get_item_config(item);
 | 
			
		||||
        
 | 
			
		||||
        if (config.has("extruder")) {
 | 
			
		||||
            if (extruder == 0)
 | 
			
		||||
                config.erase("extruder");
 | 
			
		||||
            else
 | 
			
		||||
                config.option<ConfigOptionInt>("extruder")->value = extruder;
 | 
			
		||||
                config.set("extruder", extruder);
 | 
			
		||||
        }
 | 
			
		||||
        else if (extruder > 0)
 | 
			
		||||
            config.set_key_value("extruder", new ConfigOptionInt(extruder));
 | 
			
		||||
 | 
			
		||||
        const wxString extruder_str = extruder == 0 ? wxString (_(L("default"))) : 
 | 
			
		||||
                                      wxString::Format("%d", config.option<ConfigOptionInt>("extruder")->value);
 | 
			
		||||
                                      wxString::Format("%d", config.extruder());
 | 
			
		||||
 | 
			
		||||
        auto const type = m_objects_model->GetItemType(item);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,6 +24,7 @@ class MenuWithSeparators;
 | 
			
		|||
namespace Slic3r {
 | 
			
		||||
class ConfigOptionsGroup;
 | 
			
		||||
class DynamicPrintConfig;
 | 
			
		||||
class ModelConfig;
 | 
			
		||||
class ModelObject;
 | 
			
		||||
class ModelVolume;
 | 
			
		||||
class TriangleMesh;
 | 
			
		||||
| 
						 | 
				
			
			@ -39,9 +40,9 @@ typedef std::map< std::string, std::vector< std::pair<std::string, std::string>
 | 
			
		|||
 | 
			
		||||
typedef std::vector<ModelVolume*> ModelVolumePtrs;
 | 
			
		||||
 | 
			
		||||
typedef double                                              coordf_t;
 | 
			
		||||
typedef std::pair<coordf_t, coordf_t>                       t_layer_height_range;
 | 
			
		||||
typedef std::map<t_layer_height_range, DynamicPrintConfig>  t_layer_config_ranges;
 | 
			
		||||
typedef double                                       coordf_t;
 | 
			
		||||
typedef std::pair<coordf_t, coordf_t>                t_layer_height_range;
 | 
			
		||||
typedef std::map<t_layer_height_range, ModelConfig>  t_layer_config_ranges;
 | 
			
		||||
 | 
			
		||||
namespace GUI {
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -165,7 +166,7 @@ private:
 | 
			
		|||
    wxMenuItem* m_menu_item_split_instances { nullptr };
 | 
			
		||||
 | 
			
		||||
    ObjectDataViewModel         *m_objects_model{ nullptr };
 | 
			
		||||
    DynamicPrintConfig          *m_config {nullptr};
 | 
			
		||||
    ModelConfig                 *m_config {nullptr};
 | 
			
		||||
    std::vector<ModelObject*>   *m_objects{ nullptr };
 | 
			
		||||
 | 
			
		||||
    wxBitmapComboBox            *m_extruder_editor { nullptr };
 | 
			
		||||
| 
						 | 
				
			
			@ -210,7 +211,7 @@ public:
 | 
			
		|||
    std::map<std::string, wxBitmap> CATEGORY_ICON;
 | 
			
		||||
 | 
			
		||||
    ObjectDataViewModel*        GetModel() const    { return m_objects_model; }
 | 
			
		||||
    DynamicPrintConfig*         config() const      { return m_config; }
 | 
			
		||||
    ModelConfig*                config() const      { return m_config; }
 | 
			
		||||
    std::vector<ModelObject*>*  objects() const     { return m_objects; }
 | 
			
		||||
 | 
			
		||||
    ModelObject*                object(const int obj_idx) const ;
 | 
			
		||||
| 
						 | 
				
			
			@ -320,7 +321,7 @@ public:
 | 
			
		|||
    wxPoint             get_mouse_position_in_control() const { return wxGetMousePosition() - this->GetScreenPosition(); }
 | 
			
		||||
    wxBoxSizer*         get_sizer() {return  m_sizer;}
 | 
			
		||||
    int                 get_selected_obj_idx() const;
 | 
			
		||||
    DynamicPrintConfig& get_item_config(const wxDataViewItem& item) const;
 | 
			
		||||
    ModelConfig&        get_item_config(const wxDataViewItem& item) const;
 | 
			
		||||
    SettingsBundle      get_item_settings_bundle(const DynamicPrintConfig* config, const bool is_object_settings);
 | 
			
		||||
 | 
			
		||||
    void                changed_object(const int obj_idx = -1) const;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -82,7 +82,7 @@ bool ObjectSettings::update_settings_list()
 | 
			
		|||
        return false;
 | 
			
		||||
 | 
			
		||||
    const bool is_object_settings = objects_model->GetItemType(objects_model->GetParent(item)) == itObject;
 | 
			
		||||
	SettingsBundle cat_options = objects_ctrl->get_item_settings_bundle(config, is_object_settings);
 | 
			
		||||
	SettingsBundle cat_options = objects_ctrl->get_item_settings_bundle(&config->get(), is_object_settings);
 | 
			
		||||
 | 
			
		||||
    if (!cat_options.empty())
 | 
			
		||||
    {
 | 
			
		||||
| 
						 | 
				
			
			@ -176,7 +176,7 @@ bool ObjectSettings::update_settings_list()
 | 
			
		|||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ObjectSettings::add_missed_options(DynamicPrintConfig* config_to, const DynamicPrintConfig& config_from)
 | 
			
		||||
bool ObjectSettings::add_missed_options(ModelConfig* config_to, const DynamicPrintConfig& config_from)
 | 
			
		||||
{
 | 
			
		||||
    bool is_added = false;
 | 
			
		||||
    if (wxGetApp().plater()->printer_technology() == ptFFF)
 | 
			
		||||
| 
						 | 
				
			
			@ -193,7 +193,7 @@ bool ObjectSettings::add_missed_options(DynamicPrintConfig* config_to, const Dyn
 | 
			
		|||
    return is_added;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ObjectSettings::update_config_values(DynamicPrintConfig* config)
 | 
			
		||||
void ObjectSettings::update_config_values(ModelConfig* config)
 | 
			
		||||
{
 | 
			
		||||
    const auto objects_model        = wxGetApp().obj_list()->GetModel();
 | 
			
		||||
    const auto item                 = wxGetApp().obj_list()->GetSelection();
 | 
			
		||||
| 
						 | 
				
			
			@ -250,14 +250,12 @@ void ObjectSettings::update_config_values(DynamicPrintConfig* config)
 | 
			
		|||
    {
 | 
			
		||||
        const int obj_idx = objects_model->GetObjectIdByItem(item);
 | 
			
		||||
        assert(obj_idx >= 0);
 | 
			
		||||
        DynamicPrintConfig* obj_config = &wxGetApp().model().objects[obj_idx]->config;
 | 
			
		||||
 | 
			
		||||
        main_config.apply(*obj_config, true);
 | 
			
		||||
        main_config.apply(wxGetApp().model().objects[obj_idx]->config.get(), true);
 | 
			
		||||
        printer_technology == ptFFF  ?  config_manipulation.update_print_fff_config(&main_config) :
 | 
			
		||||
                                        config_manipulation.update_print_sla_config(&main_config) ;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    main_config.apply(*config, true);
 | 
			
		||||
    main_config.apply(config->get(), true);
 | 
			
		||||
    printer_technology == ptFFF  ?  config_manipulation.update_print_fff_config(&main_config) :
 | 
			
		||||
                                    config_manipulation.update_print_sla_config(&main_config) ;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,6 +10,7 @@ class wxBoxSizer;
 | 
			
		|||
 | 
			
		||||
namespace Slic3r {
 | 
			
		||||
class DynamicPrintConfig;
 | 
			
		||||
class ModelConfig;
 | 
			
		||||
namespace GUI {
 | 
			
		||||
class ConfigOptionsGroup;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -52,8 +53,8 @@ public:
 | 
			
		|||
     * Example: if Infill is set to 100%, and Fill Pattern is missed in config_to,
 | 
			
		||||
     * we should add fill_pattern to avoid endless loop in update
 | 
			
		||||
     */
 | 
			
		||||
    bool        add_missed_options(DynamicPrintConfig *config_to, const DynamicPrintConfig &config_from);
 | 
			
		||||
    void        update_config_values(DynamicPrintConfig*config);
 | 
			
		||||
    bool        add_missed_options(ModelConfig *config_to, const DynamicPrintConfig &config_from);
 | 
			
		||||
    void        update_config_values(ModelConfig *config);
 | 
			
		||||
    void        UpdateAndShow(const bool show) override;
 | 
			
		||||
    void        msw_rescale();
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -466,7 +466,7 @@ GLGizmoHollow::get_config_options(const std::vector<std::string>& keys) const
 | 
			
		|||
    if (! mo)
 | 
			
		||||
        return out;
 | 
			
		||||
 | 
			
		||||
    const DynamicPrintConfig& object_cfg = mo->config;
 | 
			
		||||
    const DynamicPrintConfig& object_cfg = mo->config.get();
 | 
			
		||||
    const DynamicPrintConfig& print_cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config;
 | 
			
		||||
    std::unique_ptr<DynamicPrintConfig> default_cfg = nullptr;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -556,7 +556,7 @@ RENDER_AGAIN:
 | 
			
		|||
        auto opts = get_config_options({"hollowing_enable"});
 | 
			
		||||
        m_enable_hollowing = static_cast<const ConfigOptionBool*>(opts[0].first)->value;
 | 
			
		||||
        if (m_imgui->checkbox(m_desc["enable"], m_enable_hollowing)) {
 | 
			
		||||
            mo->config.opt<ConfigOptionBool>("hollowing_enable", true)->value = m_enable_hollowing;
 | 
			
		||||
            mo->config.set("hollowing_enable", m_enable_hollowing);
 | 
			
		||||
            wxGetApp().obj_list()->update_and_show_object_settings_item();
 | 
			
		||||
            config_changed = true;
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -618,14 +618,14 @@ RENDER_AGAIN:
 | 
			
		|||
    }
 | 
			
		||||
    if (slider_edited || slider_released) {
 | 
			
		||||
        if (slider_released) {
 | 
			
		||||
            mo->config.opt<ConfigOptionFloat>("hollowing_min_thickness", true)->value = m_offset_stash;
 | 
			
		||||
            mo->config.opt<ConfigOptionFloat>("hollowing_quality", true)->value = m_quality_stash;
 | 
			
		||||
            mo->config.opt<ConfigOptionFloat>("hollowing_closing_distance", true)->value = m_closing_d_stash;
 | 
			
		||||
            mo->config.set("hollowing_min_thickness", m_offset_stash);
 | 
			
		||||
            mo->config.set("hollowing_quality", m_quality_stash);
 | 
			
		||||
            mo->config.set("hollowing_closing_distance", m_closing_d_stash);
 | 
			
		||||
            Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Hollowing parameter change")));
 | 
			
		||||
        }
 | 
			
		||||
        mo->config.opt<ConfigOptionFloat>("hollowing_min_thickness", true)->value = offset;
 | 
			
		||||
        mo->config.opt<ConfigOptionFloat>("hollowing_quality", true)->value = quality;
 | 
			
		||||
        mo->config.opt<ConfigOptionFloat>("hollowing_closing_distance", true)->value = closing_d;
 | 
			
		||||
        mo->config.set("hollowing_min_thickness", offset);
 | 
			
		||||
        mo->config.set("hollowing_quality", quality);
 | 
			
		||||
        mo->config.set("hollowing_closing_distance", closing_d);
 | 
			
		||||
        if (slider_released) {
 | 
			
		||||
            wxGetApp().obj_list()->update_and_show_object_settings_item();
 | 
			
		||||
            config_changed = true;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -546,7 +546,7 @@ std::vector<const ConfigOption*> GLGizmoSlaSupports::get_config_options(const st
 | 
			
		|||
    if (! mo)
 | 
			
		||||
        return out;
 | 
			
		||||
 | 
			
		||||
    const DynamicPrintConfig& object_cfg = mo->config;
 | 
			
		||||
    const DynamicPrintConfig& object_cfg = mo->config.get();
 | 
			
		||||
    const DynamicPrintConfig& print_cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config;
 | 
			
		||||
    std::unique_ptr<DynamicPrintConfig> default_cfg = nullptr;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -753,15 +753,15 @@ RENDER_AGAIN:
 | 
			
		|||
            m_density_stash = density;
 | 
			
		||||
        }
 | 
			
		||||
        if (slider_edited) {
 | 
			
		||||
            mo->config.opt<ConfigOptionFloat>("support_points_minimal_distance", true)->value = minimal_point_distance;
 | 
			
		||||
            mo->config.opt<ConfigOptionInt>("support_points_density_relative", true)->value = (int)density;
 | 
			
		||||
            mo->config.set("support_points_minimal_distance", minimal_point_distance);
 | 
			
		||||
            mo->config.set("support_points_density_relative", (int)density);
 | 
			
		||||
        }
 | 
			
		||||
        if (slider_released) {
 | 
			
		||||
            mo->config.opt<ConfigOptionFloat>("support_points_minimal_distance", true)->value = m_minimal_point_distance_stash;
 | 
			
		||||
            mo->config.opt<ConfigOptionInt>("support_points_density_relative", true)->value = (int)m_density_stash;
 | 
			
		||||
            mo->config.set("support_points_minimal_distance", m_minimal_point_distance_stash);
 | 
			
		||||
            mo->config.set("support_points_density_relative", (int)m_density_stash);
 | 
			
		||||
            Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Support parameter change")));
 | 
			
		||||
            mo->config.opt<ConfigOptionFloat>("support_points_minimal_distance", true)->value = minimal_point_distance;
 | 
			
		||||
            mo->config.opt<ConfigOptionInt>("support_points_density_relative", true)->value = (int)density;
 | 
			
		||||
            mo->config.set("support_points_minimal_distance", minimal_point_distance);
 | 
			
		||||
            mo->config.set("support_points_density_relative", (int)density);
 | 
			
		||||
            wxGetApp().obj_list()->update_and_show_object_settings_item();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -414,7 +414,7 @@ Option ConfigOptionsGroup::get_option(const std::string& opt_key, int opt_index
 | 
			
		|||
	m_opt_map.emplace(opt_id, pair);
 | 
			
		||||
 | 
			
		||||
	if (m_show_modified_btns) // fill group and category values just fro options from Settings Tab 
 | 
			
		||||
	    wxGetApp().sidebar().get_searcher().add_key(opt_id, title, config_category);
 | 
			
		||||
	    wxGetApp().sidebar().get_searcher().add_key(opt_id, title, this->config_category());
 | 
			
		||||
 | 
			
		||||
	return Option(*m_config->def()->get(opt_key), opt_id);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -430,13 +430,11 @@ void ConfigOptionsGroup::on_change_OG(const t_config_option_key& opt_id, const b
 | 
			
		|||
			return;
 | 
			
		||||
		}		
 | 
			
		||||
 | 
			
		||||
		auto itOption = it->second;
 | 
			
		||||
		std::string opt_key = itOption.first;
 | 
			
		||||
		int opt_index = itOption.second;
 | 
			
		||||
		auto 				itOption  = it->second;
 | 
			
		||||
		const std::string  &opt_key   = itOption.first;
 | 
			
		||||
		int 			    opt_index = itOption.second;
 | 
			
		||||
 | 
			
		||||
		auto option = m_options.at(opt_id).opt;
 | 
			
		||||
 | 
			
		||||
		change_opt_value(*m_config, opt_key, value, opt_index == -1 ? 0 : opt_index);
 | 
			
		||||
		this->change_opt_value(opt_key, value, opt_index == -1 ? 0 : opt_index);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	OptionsGroup::on_change_OG(opt_id, value); 
 | 
			
		||||
| 
						 | 
				
			
			@ -470,7 +468,7 @@ void ConfigOptionsGroup::back_to_config_value(const DynamicPrintConfig& config,
 | 
			
		|||
		     opt_key == "bed_shape"				|| opt_key == "filament_ramming_parameters" || 
 | 
			
		||||
		     opt_key == "compatible_printers"	|| opt_key == "compatible_prints" ) {
 | 
			
		||||
        value = get_config_value(config, opt_key);
 | 
			
		||||
        change_opt_value(*m_config, opt_key, value);
 | 
			
		||||
        this->change_opt_value(opt_key, value);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
	else
 | 
			
		||||
| 
						 | 
				
			
			@ -789,6 +787,15 @@ Field* ConfigOptionsGroup::get_fieldc(const t_config_option_key& opt_key, int op
 | 
			
		|||
	return opt_id.empty() ? nullptr : get_field(opt_id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Change an option on m_config, possibly call ModelConfig::touch().
 | 
			
		||||
void ConfigOptionsGroup::change_opt_value(const t_config_option_key& opt_key, const boost::any& value, int opt_index /*= 0*/)
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
	Slic3r::GUI::change_opt_value(const_cast<DynamicPrintConfig&>(*m_config), opt_key, value, opt_index);
 | 
			
		||||
	if (m_modelconfig)
 | 
			
		||||
		m_modelconfig->touch();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ogStaticText::SetText(const wxString& value, bool wrap/* = true*/)
 | 
			
		||||
{
 | 
			
		||||
	SetLabel(value);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -221,18 +221,18 @@ protected:
 | 
			
		|||
 | 
			
		||||
class ConfigOptionsGroup: public OptionsGroup {
 | 
			
		||||
public:
 | 
			
		||||
	ConfigOptionsGroup(	wxWindow* parent, const wxString& title, DynamicPrintConfig* _config = nullptr, 
 | 
			
		||||
	ConfigOptionsGroup(	wxWindow* parent, const wxString& title, DynamicPrintConfig* config = nullptr, 
 | 
			
		||||
						bool is_tab_opt = false, column_t extra_clmn = nullptr) :
 | 
			
		||||
		OptionsGroup(parent, title, is_tab_opt, extra_clmn), m_config(_config) {}
 | 
			
		||||
		OptionsGroup(parent, title, is_tab_opt, extra_clmn), m_config(config) {}
 | 
			
		||||
	ConfigOptionsGroup(	wxWindow* parent, const wxString& title, ModelConfig* config, 
 | 
			
		||||
						bool is_tab_opt = false, column_t extra_clmn = nullptr) :
 | 
			
		||||
		OptionsGroup(parent, title, is_tab_opt, extra_clmn), m_config(&config->get()), m_modelconfig(config) {}
 | 
			
		||||
 | 
			
		||||
    /// reference to libslic3r config, non-owning pointer (?).
 | 
			
		||||
    DynamicPrintConfig*		m_config {nullptr};
 | 
			
		||||
    bool					m_full_labels {0};
 | 
			
		||||
	t_opt_map				m_opt_map;
 | 
			
		||||
	const std::string& config_category() const throw() { return m_config_category; }
 | 
			
		||||
	const t_opt_map&   opt_map() const throw() { return m_opt_map; }
 | 
			
		||||
 | 
			
		||||
    std::string             config_category;
 | 
			
		||||
 | 
			
		||||
    void        set_config(DynamicPrintConfig* config) { m_config = config; }
 | 
			
		||||
	void 		set_config_category(const std::string &category) { this->m_config_category = category; }
 | 
			
		||||
    void        set_config(DynamicPrintConfig* config) { m_config = config; m_modelconfig = nullptr; }
 | 
			
		||||
	Option		get_option(const std::string& opt_key, int opt_index = -1);
 | 
			
		||||
	Line		create_single_option_line(const std::string& title, int idx = -1) /*const*/{
 | 
			
		||||
		Option option = get_option(title, idx);
 | 
			
		||||
| 
						 | 
				
			
			@ -266,6 +266,20 @@ public:
 | 
			
		|||
	// return option value from config 
 | 
			
		||||
	boost::any	get_config_value(const DynamicPrintConfig& config, const std::string& opt_key, int opt_index = -1);
 | 
			
		||||
	Field*		get_fieldc(const t_config_option_key& opt_key, int opt_index);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    // Reference to libslic3r config or ModelConfig::get(), non-owning pointer.
 | 
			
		||||
    // The reference is const, so that the spots which modify m_config are clearly
 | 
			
		||||
    // demarcated by const_cast and m_config_changed_callback is called afterwards.
 | 
			
		||||
    const DynamicPrintConfig*	m_config {nullptr};
 | 
			
		||||
    // If the config is modelconfig, then ModelConfig::touch() has to be called after value change.
 | 
			
		||||
    ModelConfig*				m_modelconfig { nullptr };
 | 
			
		||||
	bool						m_full_labels{ 0 };
 | 
			
		||||
	t_opt_map					m_opt_map;
 | 
			
		||||
    std::string             	m_config_category;
 | 
			
		||||
 | 
			
		||||
    // Change an option on m_config, possibly call ModelConfig::touch().
 | 
			
		||||
	void 	change_opt_value(const t_config_option_key& opt_key, const boost::any& value, int opt_index = 0);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//  Static text shown among the options.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1364,7 +1364,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;
 | 
			
		||||
		static_cast<DynamicPrintConfig&>(dst_object->config) = static_cast<const DynamicPrintConfig&>(src_object->config);
 | 
			
		||||
		dst_object->config.assign_config(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->sla_drain_holes      = src_object->sla_drain_holes;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -675,8 +675,8 @@ void Tab::update_changed_tree_ui()
 | 
			
		|||
            {
 | 
			
		||||
                if (!sys_page && modified_page)
 | 
			
		||||
                    break;
 | 
			
		||||
                for (t_opt_map::iterator it = group->m_opt_map.begin(); it != group->m_opt_map.end(); ++it) {
 | 
			
		||||
                    const std::string& opt_key = it->first;
 | 
			
		||||
                for (const auto &kvp : group->opt_map()) {
 | 
			
		||||
                    const std::string& opt_key = kvp.first;
 | 
			
		||||
                    get_sys_and_mod_flags(opt_key, sys_page, modified_page);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			@ -764,7 +764,7 @@ void Tab::on_roll_back_value(const bool to_sys /*= true*/)
 | 
			
		|||
                        is_empty ? m_compatible_prints.btn->Disable() : m_compatible_prints.btn->Enable();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                for (auto kvp : group->m_opt_map) {
 | 
			
		||||
                for (const auto &kvp : group->opt_map()) {
 | 
			
		||||
                    const std::string& opt_key = kvp.first;
 | 
			
		||||
                    if ((m_options_list[opt_key] & os) == 0)
 | 
			
		||||
                        to_sys ? group->back_to_sys_value(opt_key) : group->back_to_initial_value(opt_key);
 | 
			
		||||
| 
						 | 
				
			
			@ -3791,7 +3791,7 @@ ConfigOptionsGroupShp Page::new_optgroup(const wxString& title, int noncommon_la
 | 
			
		|||
 | 
			
		||||
    //! config_ have to be "right"
 | 
			
		||||
    ConfigOptionsGroupShp optgroup = std::make_shared<ConfigOptionsGroup>(this, title, m_config, true, extra_column);
 | 
			
		||||
    optgroup->config_category = m_title.ToStdString();
 | 
			
		||||
    optgroup->set_config_category(m_title.ToStdString());
 | 
			
		||||
    if (noncommon_label_width >= 0)
 | 
			
		||||
        optgroup->label_width = noncommon_label_width;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,7 +17,7 @@
 | 
			
		|||
#define CEREAL_FUTURE_EXPERIMENTAL
 | 
			
		||||
#include <cereal/archives/adapters.hpp>
 | 
			
		||||
 | 
			
		||||
#include <libslic3r/Config.hpp>
 | 
			
		||||
#include <libslic3r/PrintConfig.hpp>
 | 
			
		||||
#include <libslic3r/ObjectID.hpp>
 | 
			
		||||
#include <libslic3r/Utils.hpp>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue