Merge remote-tracking branch 'remotes/origin/vb_undo_redo'

This commit is contained in:
bubnikv 2019-07-15 11:34:18 +02:00
commit ab7ecc1819
60 changed files with 2799 additions and 578 deletions

View file

@ -161,4 +161,12 @@ inline bool empty(const BoundingBox3Base<VT> &bb)
} // namespace Slic3r
// Serialization through the Cereal library
namespace cereal {
template<class Archive> void serialize(Archive& archive, Slic3r::BoundingBox &bb) { archive(bb.min, bb.max, bb.defined); }
template<class Archive> void serialize(Archive& archive, Slic3r::BoundingBox3 &bb) { archive(bb.min, bb.max, bb.defined); }
template<class Archive> void serialize(Archive& archive, Slic3r::BoundingBoxf &bb) { archive(bb.min, bb.max, bb.defined); }
template<class Archive> void serialize(Archive& archive, Slic3r::BoundingBoxf3 &bb) { archive(bb.min, bb.max, bb.defined); }
}
#endif

View file

@ -113,6 +113,8 @@ add_library(libslic3r STATIC
MultiPoint.cpp
MultiPoint.hpp
MutablePriorityQueue.hpp
ObjectID.cpp
ObjectID.hpp
PerimeterGenerator.cpp
PerimeterGenerator.hpp
PlaceholderParser.cpp
@ -188,6 +190,7 @@ target_include_directories(libslic3r PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${LIBNE
target_link_libraries(libslic3r
libnest2d
admesh
cereal
libigl
miniz
boost_libs

View file

@ -209,6 +209,51 @@ std::vector<std::string> ConfigOptionDef::cli_args(const std::string &key) const
return args;
}
ConfigOption* ConfigOptionDef::create_empty_option() const
{
switch (this->type) {
case coFloat: return new ConfigOptionFloat();
case coFloats: return new ConfigOptionFloats();
case coInt: return new ConfigOptionInt();
case coInts: return new ConfigOptionInts();
case coString: return new ConfigOptionString();
case coStrings: return new ConfigOptionStrings();
case coPercent: return new ConfigOptionPercent();
case coPercents: return new ConfigOptionPercents();
case coFloatOrPercent: return new ConfigOptionFloatOrPercent();
case coPoint: return new ConfigOptionPoint();
case coPoints: return new ConfigOptionPoints();
case coPoint3: return new ConfigOptionPoint3();
// case coPoint3s: return new ConfigOptionPoint3s();
case coBool: return new ConfigOptionBool();
case coBools: return new ConfigOptionBools();
case coEnum: return new ConfigOptionEnumGeneric(this->enum_keys_map);
default: throw std::runtime_error(std::string("Unknown option type for option ") + this->label);
}
}
ConfigOption* ConfigOptionDef::create_default_option() const
{
if (this->default_value)
return (this->default_value->type() == coEnum) ?
// Special case: For a DynamicConfig, convert a templated enum to a generic enum.
new ConfigOptionEnumGeneric(this->enum_keys_map, this->default_value->getInt()) :
this->default_value->clone();
return this->create_empty_option();
}
// Assignment of the serialization IDs is not thread safe. The Defs shall be initialized from the main thread!
ConfigOptionDef* ConfigDef::add(const t_config_option_key &opt_key, ConfigOptionType type)
{
static size_t serialization_key_ordinal_last = 0;
ConfigOptionDef *opt = &this->options[opt_key];
opt->opt_key = opt_key;
opt->type = type;
opt->serialization_key_ordinal = ++ serialization_key_ordinal_last;
this->by_serialization_key_ordinal[opt->serialization_key_ordinal] = opt;
return opt;
}
std::string ConfigOptionDef::nocli = "~~~noCLI";
std::ostream& ConfigDef::print_cli_help(std::ostream& out, bool show_defaults, std::function<bool(const ConfigOptionDef &)> filter) const
@ -358,7 +403,7 @@ t_config_option_keys ConfigBase::equal(const ConfigBase &other) const
return equal;
}
std::string ConfigBase::serialize(const t_config_option_key &opt_key) const
std::string ConfigBase::opt_serialize(const t_config_option_key &opt_key) const
{
const ConfigOption* opt = this->option(opt_key);
assert(opt != nullptr);
@ -469,7 +514,7 @@ void ConfigBase::setenv_() const
for (size_t i = 0; i < envname.size(); ++i)
envname[i] = (envname[i] <= 'z' && envname[i] >= 'a') ? envname[i]-('a'-'A') : envname[i];
boost::nowide::setenv(envname.c_str(), this->serialize(*it).c_str(), 1);
boost::nowide::setenv(envname.c_str(), this->opt_serialize(*it).c_str(), 1);
}
}
@ -593,16 +638,16 @@ void ConfigBase::save(const std::string &file) const
c.open(file, std::ios::out | std::ios::trunc);
c << "# " << Slic3r::header_slic3r_generated() << std::endl;
for (const std::string &opt_key : this->keys())
c << opt_key << " = " << this->serialize(opt_key) << std::endl;
c << opt_key << " = " << this->opt_serialize(opt_key) << std::endl;
c.close();
}
bool DynamicConfig::operator==(const DynamicConfig &rhs) const
{
t_options_map::const_iterator it1 = this->options.begin();
t_options_map::const_iterator it1_end = this->options.end();
t_options_map::const_iterator it2 = rhs.options.begin();
t_options_map::const_iterator it2_end = rhs.options.end();
auto it1 = this->options.begin();
auto it1_end = this->options.end();
auto it2 = rhs.options.begin();
auto it2_end = rhs.options.end();
for (; it1 != it1_end && it2 != it2_end; ++ it1, ++ it2)
if (it1->first != it2->first || *it1->second != *it2->second)
// key or value differ
@ -612,10 +657,10 @@ bool DynamicConfig::operator==(const DynamicConfig &rhs) const
ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key, bool create)
{
t_options_map::iterator it = options.find(opt_key);
auto it = options.find(opt_key);
if (it != options.end())
// Option was found.
return it->second;
return it->second.get();
if (! create)
// Option was not found and a new option shall not be created.
return nullptr;
@ -628,34 +673,8 @@ ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key, bool cre
// throw std::runtime_error(std::string("Invalid option name: ") + opt_key);
// Let the parent decide what to do if the opt_key is not defined by this->def().
return nullptr;
ConfigOption *opt = nullptr;
if (optdef->default_value) {
opt = (optdef->default_value->type() == coEnum) ?
// Special case: For a DynamicConfig, convert a templated enum to a generic enum.
new ConfigOptionEnumGeneric(optdef->enum_keys_map, optdef->default_value->getInt()) :
optdef->default_value->clone();
} else {
switch (optdef->type) {
case coFloat: opt = new ConfigOptionFloat(); break;
case coFloats: opt = new ConfigOptionFloats(); break;
case coInt: opt = new ConfigOptionInt(); break;
case coInts: opt = new ConfigOptionInts(); break;
case coString: opt = new ConfigOptionString(); break;
case coStrings: opt = new ConfigOptionStrings(); break;
case coPercent: opt = new ConfigOptionPercent(); break;
case coPercents: opt = new ConfigOptionPercents(); break;
case coFloatOrPercent: opt = new ConfigOptionFloatOrPercent(); break;
case coPoint: opt = new ConfigOptionPoint(); break;
case coPoints: opt = new ConfigOptionPoints(); break;
case coPoint3: opt = new ConfigOptionPoint3(); break;
// case coPoint3s: opt = new ConfigOptionPoint3s(); break;
case coBool: opt = new ConfigOptionBool(); break;
case coBools: opt = new ConfigOptionBools(); break;
case coEnum: opt = new ConfigOptionEnumGeneric(optdef->enum_keys_map); break;
default: throw std::runtime_error(std::string("Unknown option type for option ") + opt_key);
}
}
this->options[opt_key] = opt;
ConfigOption *opt = optdef->create_default_option();
this->options.emplace_hint(it, opt_key, std::unique_ptr<ConfigOption>(opt));
return opt;
}
@ -802,3 +821,64 @@ t_config_option_keys StaticConfig::keys() const
}
}
#include <cereal/types/polymorphic.hpp>
CEREAL_REGISTER_TYPE(Slic3r::ConfigOption)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionSingle<double>)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionSingle<int>)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionSingle<std::string>)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionSingle<Slic3r::Vec2d>)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionSingle<Slic3r::Vec3d>)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionSingle<bool>)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionVectorBase)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionVector<double>)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionVector<int>)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionVector<std::string>)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionVector<Slic3r::Vec2d>)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionVector<unsigned char>)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionFloat)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionFloats)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionInt)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionInts)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionString)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionStrings)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionPercent)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionPercents)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionFloatOrPercent)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionPoint)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionPoints)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionPoint3)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionBool)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionBools)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionEnumGeneric)
CEREAL_REGISTER_TYPE(Slic3r::ConfigBase)
CEREAL_REGISTER_TYPE(Slic3r::DynamicConfig)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOption, Slic3r::ConfigOptionSingle<double>)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOption, Slic3r::ConfigOptionSingle<int>)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOption, Slic3r::ConfigOptionSingle<std::string>)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOption, Slic3r::ConfigOptionSingle<Slic3r::Vec2d>)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOption, Slic3r::ConfigOptionSingle<Slic3r::Vec3d>)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOption, Slic3r::ConfigOptionSingle<bool>)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOption, Slic3r::ConfigOptionVectorBase)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVectorBase, Slic3r::ConfigOptionVector<double>)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVectorBase, Slic3r::ConfigOptionVector<int>)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVectorBase, Slic3r::ConfigOptionVector<std::string>)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVectorBase, Slic3r::ConfigOptionVector<Slic3r::Vec2d>)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVectorBase, Slic3r::ConfigOptionVector<unsigned char>)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionSingle<double>, Slic3r::ConfigOptionFloat)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVector<double>, Slic3r::ConfigOptionFloats)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionSingle<int>, Slic3r::ConfigOptionInt)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVector<int>, Slic3r::ConfigOptionInts)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionSingle<std::string>, Slic3r::ConfigOptionString)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVector<std::string>, Slic3r::ConfigOptionStrings)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionFloat, Slic3r::ConfigOptionPercent)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionFloats, Slic3r::ConfigOptionPercents)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionPercent, Slic3r::ConfigOptionFloatOrPercent)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionSingle<Slic3r::Vec2d>, Slic3r::ConfigOptionPoint)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVector<Slic3r::Vec2d>, Slic3r::ConfigOptionPoints)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionSingle<Slic3r::Vec3d>, Slic3r::ConfigOptionPoint3)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionSingle<bool>, Slic3r::ConfigOptionBool)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVector<unsigned char>, Slic3r::ConfigOptionBools)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionInt, Slic3r::ConfigOptionEnumGeneric)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigBase, Slic3r::DynamicConfig)

View file

@ -18,6 +18,9 @@
#include <boost/format.hpp>
#include <boost/property_tree/ptree.hpp>
#include <cereal/access.hpp>
#include <cereal/types/base_class.hpp>
namespace Slic3r {
// Name of the configuration option.
@ -152,6 +155,10 @@ public:
bool operator==(const T &rhs) const { return this->value == rhs; }
bool operator!=(const T &rhs) const { return this->value != rhs; }
private:
friend class cereal::access;
template<class Archive> void serialize(Archive & ar) { ar(this->value); }
};
// Value of a vector valued option (bools, ints, floats, strings, points)
@ -294,6 +301,10 @@ public:
bool operator==(const std::vector<T> &rhs) const { return this->values == rhs; }
bool operator!=(const std::vector<T> &rhs) const { return this->values != rhs; }
private:
friend class cereal::access;
template<class Archive> void serialize(Archive & ar) { ar(this->values); }
};
class ConfigOptionFloat : public ConfigOptionSingle<double>
@ -328,6 +339,10 @@ public:
this->set(opt);
return *this;
}
private:
friend class cereal::access;
template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionSingle<double>>(this)); }
};
class ConfigOptionFloats : public ConfigOptionVector<double>
@ -386,6 +401,10 @@ public:
this->set(opt);
return *this;
}
private:
friend class cereal::access;
template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionVector<double>>(this)); }
};
class ConfigOptionInt : public ConfigOptionSingle<int>
@ -422,6 +441,10 @@ public:
this->set(opt);
return *this;
}
private:
friend class cereal::access;
template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionSingle<int>>(this)); }
};
class ConfigOptionInts : public ConfigOptionVector<int>
@ -472,6 +495,10 @@ public:
}
return true;
}
private:
friend class cereal::access;
template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionVector<int>>(this)); }
};
class ConfigOptionString : public ConfigOptionSingle<std::string>
@ -496,6 +523,10 @@ public:
UNUSED(append);
return unescape_string_cstyle(str, this->value);
}
private:
friend class cereal::access;
template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionSingle<std::string>>(this)); }
};
// semicolon-separated strings
@ -530,6 +561,10 @@ public:
this->values.clear();
return unescape_strings_cstyle(str, this->values);
}
private:
friend class cereal::access;
template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionVector<std::string>>(this)); }
};
class ConfigOptionPercent : public ConfigOptionFloat
@ -562,6 +597,10 @@ public:
iss >> this->value;
return !iss.fail();
}
private:
friend class cereal::access;
template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionFloat>(this)); }
};
class ConfigOptionPercents : public ConfigOptionFloats
@ -616,6 +655,10 @@ public:
}
return true;
}
private:
friend class cereal::access;
template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionFloats>(this)); }
};
class ConfigOptionFloatOrPercent : public ConfigOptionPercent
@ -665,6 +708,10 @@ public:
iss >> this->value;
return !iss.fail();
}
private:
friend class cereal::access;
template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionPercent>(this), percent); }
};
class ConfigOptionPoint : public ConfigOptionSingle<Vec2d>
@ -695,6 +742,10 @@ public:
return sscanf(str.data(), " %lf , %lf %c", &this->value(0), &this->value(1), &dummy) == 2 ||
sscanf(str.data(), " %lf x %lf %c", &this->value(0), &this->value(1), &dummy) == 2;
}
private:
friend class cereal::access;
template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionSingle<Vec2d>>(this)); }
};
class ConfigOptionPoints : public ConfigOptionVector<Vec2d>
@ -754,8 +805,21 @@ public:
}
return true;
}
};
private:
friend class cereal::access;
template<class Archive> void save(Archive& archive) const {
size_t cnt = this->values.size();
archive(cnt);
archive.saveBinary((const char*)this->values.data(), sizeof(Vec2d) * cnt);
}
template<class Archive> void load(Archive& archive) {
size_t cnt;
archive(cnt);
this->values.assign(cnt, Vec2d());
archive.loadBinary((char*)this->values.data(), sizeof(Vec2d) * cnt);
}
};
class ConfigOptionPoint3 : public ConfigOptionSingle<Vec3d>
{
@ -787,6 +851,10 @@ public:
return sscanf(str.data(), " %lf , %lf , %lf %c", &this->value(0), &this->value(1), &this->value(2), &dummy) == 2 ||
sscanf(str.data(), " %lf x %lf x %lf %c", &this->value(0), &this->value(1), &this->value(2), &dummy) == 2;
}
private:
friend class cereal::access;
template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionSingle<Vec3d>>(this)); }
};
class ConfigOptionBool : public ConfigOptionSingle<bool>
@ -813,6 +881,10 @@ public:
this->value = (str.compare("1") == 0);
return true;
}
private:
friend class cereal::access;
template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionSingle<bool>>(this)); }
};
class ConfigOptionBools : public ConfigOptionVector<unsigned char>
@ -868,6 +940,10 @@ public:
}
return true;
}
private:
friend class cereal::access;
template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionVector<unsigned char>>(this)); }
};
// Map from an enum integer value to an enum name.
@ -1006,19 +1082,73 @@ public:
this->value = it->second;
return true;
}
private:
friend class cereal::access;
template<class Archive> void serialize(Archive& ar) { ar(cereal::base_class<ConfigOptionInt>(this)); }
};
// Definition of a configuration value for the purpose of GUI presentation, editing, value mapping and config file handling.
class ConfigOptionDef
{
public:
// Identifier of this option. It is stored here so that it is accessible through the by_serialization_key_ordinal map.
t_config_option_key opt_key;
// What type? bool, int, string etc.
ConfigOptionType type = coNone;
// Default value of this option. The default value object is owned by ConfigDef, it is released in its destructor.
Slic3r::clonable_ptr<const ConfigOption> default_value;
void set_default_value(const ConfigOption* ptr) { this->default_value = Slic3r::clonable_ptr<const ConfigOption>(ptr); }
template<typename T>
const T* get_default_value() const { return static_cast<const T*>(this->default_value.get()); }
void set_default_value(const ConfigOption* ptr) { this->default_value = Slic3r::clonable_ptr<const ConfigOption>(ptr); }
template<typename T> const T* get_default_value() const { return static_cast<const T*>(this->default_value.get()); }
// Create an empty option to be used as a base for deserialization of DynamicConfig.
ConfigOption* create_empty_option() const;
// Create a default option to be inserted into a DynamicConfig.
ConfigOption* create_default_option() const;
template<class Archive> ConfigOption* load_option_from_archive(Archive &archive) const {
switch (this->type) {
case coFloat: { auto opt = new ConfigOptionFloat(); archive(*opt); return opt; }
case coFloats: { auto opt = new ConfigOptionFloats(); archive(*opt); return opt; }
case coInt: { auto opt = new ConfigOptionInt(); archive(*opt); return opt; }
case coInts: { auto opt = new ConfigOptionInts(); archive(*opt); return opt; }
case coString: { auto opt = new ConfigOptionString(); archive(*opt); return opt; }
case coStrings: { auto opt = new ConfigOptionStrings(); archive(*opt); return opt; }
case coPercent: { auto opt = new ConfigOptionPercent(); archive(*opt); return opt; }
case coPercents: { auto opt = new ConfigOptionPercents(); archive(*opt); return opt; }
case coFloatOrPercent: { auto opt = new ConfigOptionFloatOrPercent(); archive(*opt); return opt; }
case coPoint: { auto opt = new ConfigOptionPoint(); archive(*opt); return opt; }
case coPoints: { auto opt = new ConfigOptionPoints(); archive(*opt); return opt; }
case coPoint3: { auto opt = new ConfigOptionPoint3(); archive(*opt); return opt; }
case coBool: { auto opt = new ConfigOptionBool(); archive(*opt); return opt; }
case coBools: { auto opt = new ConfigOptionBools(); archive(*opt); return opt; }
case coEnum: { auto opt = new ConfigOptionEnumGeneric(this->enum_keys_map); archive(*opt); return opt; }
default: throw std::runtime_error(std::string("ConfigOptionDef::load_option_from_archive(): Unknown option type for option ") + this->opt_key);
}
}
template<class Archive> ConfigOption* save_option_to_archive(Archive &archive, const ConfigOption *opt) const {
switch (this->type) {
case coFloat: archive(*static_cast<const ConfigOptionFloat*>(opt)); break;
case coFloats: archive(*static_cast<const ConfigOptionFloats*>(opt)); break;
case coInt: archive(*static_cast<const ConfigOptionInt*>(opt)); break;
case coInts: archive(*static_cast<const ConfigOptionInts*>(opt)); break;
case coString: archive(*static_cast<const ConfigOptionString*>(opt)); break;
case coStrings: archive(*static_cast<const ConfigOptionStrings*>(opt)); break;
case coPercent: archive(*static_cast<const ConfigOptionPercent*>(opt)); break;
case coPercents: archive(*static_cast<const ConfigOptionPercents*>(opt)); break;
case coFloatOrPercent: archive(*static_cast<const ConfigOptionFloatOrPercent*>(opt)); break;
case coPoint: archive(*static_cast<const ConfigOptionPoint*>(opt)); break;
case coPoints: archive(*static_cast<const ConfigOptionPoints*>(opt)); break;
case coPoint3: archive(*static_cast<const ConfigOptionPoint3*>(opt)); break;
case coBool: archive(*static_cast<const ConfigOptionBool*>(opt)); break;
case coBools: archive(*static_cast<const ConfigOptionBools*>(opt)); break;
case coEnum: archive(*static_cast<const ConfigOptionEnumGeneric*>(opt)); break;
default: throw std::runtime_error(std::string("ConfigOptionDef::save_option_to_archive(): Unknown option type for option ") + this->opt_key);
}
// Make the compiler happy, shut up the warnings.
return nullptr;
}
// Usually empty.
// Special values - "i_enum_open", "f_enum_open" to provide combo box for int or float selection,
@ -1088,6 +1218,9 @@ public:
return false;
}
// 0 is an invalid key.
size_t serialization_key_ordinal = 0;
// Returns the alternative CLI arguments for the given option.
// If there are no cli arguments defined, use the key and replace underscores with dashes.
std::vector<std::string> cli_args(const std::string &key) const;
@ -1107,7 +1240,8 @@ typedef std::map<t_config_option_key, ConfigOptionDef> t_optiondef_map;
class ConfigDef
{
public:
t_optiondef_map options;
t_optiondef_map options;
std::map<size_t, const ConfigOptionDef*> by_serialization_key_ordinal;
bool has(const t_config_option_key &opt_key) const { return this->options.count(opt_key) > 0; }
const ConfigOptionDef* get(const t_config_option_key &opt_key) const {
@ -1128,11 +1262,7 @@ public:
std::function<bool(const ConfigOptionDef &)> filter = [](const ConfigOptionDef &){ return true; }) const;
protected:
ConfigOptionDef* add(const t_config_option_key &opt_key, ConfigOptionType type) {
ConfigOptionDef* opt = &this->options[opt_key];
opt->type = type;
return opt;
}
ConfigOptionDef* add(const t_config_option_key &opt_key, ConfigOptionType type);
};
// An abstract configuration store.
@ -1201,7 +1331,7 @@ public:
bool equals(const ConfigBase &other) const { return this->diff(other).empty(); }
t_config_option_keys diff(const ConfigBase &other) const;
t_config_option_keys equal(const ConfigBase &other) const;
std::string serialize(const t_config_option_key &opt_key) const;
std::string opt_serialize(const t_config_option_key &opt_key) const;
// Set a configuration value from a string, it will call an overridable handle_legacy()
// to resolve renamed and removed configuration keys.
bool set_deserialize(const t_config_option_key &opt_key, const std::string &str, bool append = false);
@ -1239,7 +1369,7 @@ public:
assert(this->def() == nullptr || this->def() == rhs.def());
this->clear();
for (const auto &kvp : rhs.options)
this->options[kvp.first] = kvp.second->clone();
this->options[kvp.first].reset(kvp.second->clone());
return *this;
}
@ -1262,15 +1392,13 @@ public:
for (const auto &kvp : rhs.options) {
auto it = this->options.find(kvp.first);
if (it == this->options.end())
this->options[kvp.first] = kvp.second->clone();
this->options[kvp.first].reset(kvp.second->clone());
else {
assert(it->second->type() == kvp.second->type());
if (it->second->type() == kvp.second->type())
*it->second = *kvp.second;
else {
delete it->second;
it->second = kvp.second->clone();
}
else
it->second.reset(kvp.second->clone());
}
}
return *this;
@ -1281,14 +1409,13 @@ public:
DynamicConfig& operator+=(DynamicConfig &&rhs)
{
assert(this->def() == nullptr || this->def() == rhs.def());
for (const auto &kvp : rhs.options) {
for (auto &kvp : rhs.options) {
auto it = this->options.find(kvp.first);
if (it == this->options.end()) {
this->options[kvp.first] = kvp.second;
this->options.insert(std::make_pair(kvp.first, std::move(kvp.second)));
} else {
assert(it->second->type() == kvp.second->type());
delete it->second;
it->second = kvp.second;
it->second = std::move(kvp.second);
}
}
rhs.options.clear();
@ -1305,8 +1432,6 @@ public:
void clear()
{
for (auto &opt : this->options)
delete opt.second;
this->options.clear();
}
@ -1315,7 +1440,6 @@ public:
auto it = this->options.find(opt_key);
if (it == this->options.end())
return false;
delete it->second;
this->options.erase(it);
return true;
}
@ -1340,11 +1464,10 @@ public:
{
auto it = this->options.find(opt_key);
if (it == this->options.end()) {
this->options[opt_key] = opt;
this->options[opt_key].reset(opt);
return true;
} else {
delete it->second;
it->second = opt;
it->second.reset(opt);
return false;
}
}
@ -1374,12 +1497,15 @@ public:
void read_cli(const std::vector<std::string> &tokens, t_config_option_keys* extra, t_config_option_keys* keys = nullptr);
bool read_cli(int argc, char** argv, t_config_option_keys* extra, t_config_option_keys* keys = nullptr);
typedef std::map<t_config_option_key,ConfigOption*> t_options_map;
t_options_map::const_iterator cbegin() const { return options.cbegin(); }
t_options_map::const_iterator cend() const { return options.cend(); }
std::map<t_config_option_key, std::unique_ptr<ConfigOption>>::const_iterator cbegin() const { return options.cbegin(); }
std::map<t_config_option_key, std::unique_ptr<ConfigOption>>::const_iterator cend() const { return options.cend(); }
size_t size() const { return options.size(); }
private:
t_options_map options;
std::map<t_config_option_key, std::unique_ptr<ConfigOption>> options;
friend class cereal::access;
template<class Archive> void serialize(Archive &ar) { ar(options); }
};
/// Configuration store with a static definition of configuration values.

View file

@ -2134,7 +2134,7 @@ namespace Slic3r {
const DynamicPrintConfig& config = range.second;
for (const std::string& opt_key : config.keys())
{
pt::ptree& opt_tree = range_tree.add("option", config.serialize(opt_key));
pt::ptree& opt_tree = range_tree.add("option", config.opt_serialize(opt_key));
opt_tree.put("<xmlattr>.opt_key", opt_key);
}
}
@ -2216,7 +2216,7 @@ namespace Slic3r {
for (const std::string &key : config.keys())
if (key != "compatible_printers")
out += "; " + key + " = " + config.serialize(key) + "\n";
out += "; " + key + " = " + config.opt_serialize(key) + "\n";
if (!out.empty())
{
@ -2250,7 +2250,7 @@ namespace Slic3r {
// stores object's config data
for (const std::string& key : obj->config.keys())
{
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << OBJECT_TYPE << "\" " << KEY_ATTR << "=\"" << key << "\" " << VALUE_ATTR << "=\"" << obj->config.serialize(key) << "\"/>\n";
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << OBJECT_TYPE << "\" " << KEY_ATTR << "=\"" << key << "\" " << VALUE_ATTR << "=\"" << obj->config.opt_serialize(key) << "\"/>\n";
}
for (const ModelVolume* volume : obj_metadata.second.object->volumes)
@ -2280,7 +2280,7 @@ namespace Slic3r {
// stores volume's config data
for (const std::string& key : volume->config.keys())
{
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << key << "\" " << VALUE_ATTR << "=\"" << volume->config.serialize(key) << "\"/>\n";
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << key << "\" " << VALUE_ATTR << "=\"" << volume->config.opt_serialize(key) << "\"/>\n";
}
stream << " </" << VOLUME_TAG << ">\n";

View file

@ -901,7 +901,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
std::string str_config = "\n";
for (const std::string &key : config->keys())
if (key != "compatible_printers")
str_config += "; " + key + " = " + config->serialize(key) + "\n";
str_config += "; " + key + " = " + config->opt_serialize(key) + "\n";
stream << "<metadata type=\"" << SLIC3R_CONFIG_TYPE << "\">" << xml_escape(str_config) << "</metadata>\n";
}
@ -913,7 +913,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
for (const auto &attr : material.second->attributes)
stream << " <metadata type=\"" << attr.first << "\">" << attr.second << "</metadata>\n";
for (const std::string &key : material.second->config.keys())
stream << " <metadata type=\"slic3r." << key << "\">" << material.second->config.serialize(key) << "</metadata>\n";
stream << " <metadata type=\"slic3r." << key << "\">" << material.second->config.opt_serialize(key) << "</metadata>\n";
stream << " </material>\n";
}
std::string instances;
@ -921,7 +921,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
ModelObject *object = model->objects[object_id];
stream << " <object id=\"" << object_id << "\">\n";
for (const std::string &key : object->config.keys())
stream << " <metadata type=\"slic3r." << key << "\">" << object->config.serialize(key) << "</metadata>\n";
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;
@ -933,10 +933,8 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
stream << ";" << layer_height_profile[i];
stream << "\n </metadata>\n";
}
//FIXME Store the layer height ranges (ModelObject::layer_height_ranges)
// #ys_FIXME_experiment : Try to export layer config range
// Export layer height ranges including the layer range specific config overrides.
const t_layer_config_ranges& config_ranges = object->layer_config_ranges;
if (!config_ranges.empty())
{
@ -950,7 +948,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
stream << range.first.first << ";" << range.first.second << "</metadata>\n";
for (const std::string& key : range.second.keys())
stream << " <metadata type=\"slic3r." << key << "\">" << range.second.serialize(key) << "</metadata>\n";
stream << " <metadata type=\"slic3r." << key << "\">" << range.second.opt_serialize(key) << "</metadata>\n";
stream << " </range>\n";
layer_counter++;
@ -1005,7 +1003,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
else
stream << " <volume materialid=\"" << volume->material_id() << "\">\n";
for (const std::string &key : volume->config.keys())
stream << " <metadata type=\"slic3r." << key << "\">" << volume->config.serialize(key) << "</metadata>\n";
stream << " <metadata type=\"slic3r." << key << "\">" << volume->config.opt_serialize(key) << "</metadata>\n";
if (!volume->name.empty())
stream << " <metadata type=\"name\">" << xml_escape(volume->name) << "</metadata>\n";
if (volume->is_modifier())

View file

@ -1809,7 +1809,7 @@ void GCode::append_full_config(const Print& print, std::string& str)
const StaticPrintConfig *cfg = configs[i];
for (const std::string &key : cfg->keys())
if (key != "compatible_printers")
str += "; " + key + " = " + cfg->serialize(key) + "\n";
str += "; " + key + " = " + cfg->opt_serialize(key) + "\n";
}
const DynamicConfig &full_config = print.placeholder_parser().config();
for (const char *key : {

View file

@ -7,6 +7,9 @@
#include "Polygon.hpp"
#include "Polyline.hpp"
// Serialization through the Cereal library
#include <cereal/access.hpp>
#include "boost/polygon/voronoi.hpp"
using boost::polygon::voronoi_builder;
using boost::polygon::voronoi_diagram;
@ -263,6 +266,17 @@ public:
// as possible in least squares norm in regard to the 8 corners of bbox.
// Bounding box is expected to be centered around zero in all axes.
static Transformation volume_to_bed_transformation(const Transformation& instance_transformation, const BoundingBoxf3& bbox);
private:
friend class cereal::access;
template<class Archive> void serialize(Archive & ar) { ar(m_offset, m_rotation, m_scaling_factor, m_mirror); }
explicit Transformation(int) : m_dirty(true) {}
template <class Archive> static void load_and_construct(Archive &ar, cereal::construct<Transformation> &construct)
{
// Calling a private constructor with special "int" parameter to indicate that no construction is necessary.
construct(1);
ar(construct.ptr()->m_offset, construct.ptr()->m_rotation, construct.ptr()->m_scaling_factor, construct.ptr()->m_mirror);
}
};
// Rotation when going from the first coordinate system with rotation rot_xyz_from applied

View file

@ -128,7 +128,7 @@ void Layer::make_perimeters()
&& config.external_perimeter_speed == other_config.external_perimeter_speed
&& config.gap_fill_speed == other_config.gap_fill_speed
&& config.overhangs == other_config.overhangs
&& config.serialize("perimeter_extrusion_width").compare(other_config.serialize("perimeter_extrusion_width")) == 0
&& config.opt_serialize("perimeter_extrusion_width") == other_config.opt_serialize("perimeter_extrusion_width")
&& config.thin_walls == other_config.thin_walls
&& config.external_perimeters_first == other_config.external_perimeters_first) {
layerms.push_back(other_layerm);

View file

@ -22,21 +22,6 @@ namespace Slic3r {
unsigned int Model::s_auto_extruder_id = 1;
size_t ModelBase::s_last_id = 0;
// Unique object / instance ID for the wipe tower.
ModelID wipe_tower_object_id()
{
static ModelBase mine;
return mine.id();
}
ModelID wipe_tower_instance_id()
{
static ModelBase mine;
return mine.id();
}
Model& Model::assign_copy(const Model &rhs)
{
this->copy_id(rhs);
@ -87,6 +72,19 @@ void Model::assign_new_unique_ids_recursive()
model_object->assign_new_unique_ids_recursive();
}
void Model::update_links_bottom_up_recursive()
{
for (std::pair<const t_model_material_id, ModelMaterial*> &kvp : this->materials)
kvp.second->set_model(this);
for (ModelObject *model_object : this->objects) {
model_object->set_model(this);
for (ModelInstance *model_instance : model_object->instances)
model_instance->set_model_object(model_object);
for (ModelVolume *model_volume : model_object->volumes)
model_volume->set_model_object(model_object);
}
}
Model Model::read_from_file(const std::string &input_file, DynamicPrintConfig *config, bool add_default_instances)
{
Model model;
@ -221,7 +219,7 @@ bool Model::delete_object(ModelObject* object)
return false;
}
bool Model::delete_object(ModelID id)
bool Model::delete_object(ObjectID id)
{
if (id.id != 0) {
size_t idx = 0;
@ -622,11 +620,15 @@ ModelObject::~ModelObject()
// maintains the m_model pointer
ModelObject& ModelObject::assign_copy(const ModelObject &rhs)
{
this->copy_id(rhs);
assert(this->id().invalid() || this->id() == rhs.id());
assert(this->config.id().invalid() || this->config.id() == rhs.config.id());
this->copy_id(rhs);
this->name = rhs.name;
this->input_file = rhs.input_file;
// Copies the config's ID
this->config = rhs.config;
assert(this->config.id() == rhs.config.id());
this->sla_support_points = rhs.sla_support_points;
this->sla_points_status = rhs.sla_points_status;
this->layer_config_ranges = rhs.layer_config_ranges; // #ys_FIXME_experiment
@ -658,11 +660,14 @@ ModelObject& ModelObject::assign_copy(const ModelObject &rhs)
// maintains the m_model pointer
ModelObject& ModelObject::assign_copy(ModelObject &&rhs)
{
assert(this->id().invalid());
this->copy_id(rhs);
this->name = std::move(rhs.name);
this->input_file = std::move(rhs.input_file);
// Moves the config's ID
this->config = std::move(rhs.config);
assert(this->config.id() == rhs.config.id());
this->sla_support_points = std::move(rhs.sla_support_points);
this->sla_points_status = std::move(rhs.sla_points_status);
this->layer_config_ranges = std::move(rhs.layer_config_ranges); // #ys_FIXME_experiment
@ -1070,11 +1075,11 @@ void ModelObject::mirror(Axis axis)
}
// This method could only be called before the meshes of this ModelVolumes are not shared!
void ModelObject::scale_mesh(const Vec3d &versor)
void ModelObject::scale_mesh_after_creation(const Vec3d &versor)
{
for (ModelVolume *v : this->volumes)
{
v->scale_geometry(versor);
v->scale_geometry_after_creation(versor);
v->set_offset(versor.cwiseProduct(v->get_offset()));
}
this->invalidate_bounding_box();
@ -1191,13 +1196,19 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b
if (keep_upper && upper_mesh.facets_count() > 0) {
ModelVolume* vol = upper->add_volume(upper_mesh);
vol->name = volume->name;
vol->config = volume->config;
// Don't copy the config's ID.
static_cast<DynamicPrintConfig&>(vol->config) = static_cast<const DynamicPrintConfig&>(volume->config);
assert(vol->config.id().valid());
assert(vol->config.id() != volume->config.id());
vol->set_material(volume->material_id(), *volume->material());
}
if (keep_lower && lower_mesh.facets_count() > 0) {
ModelVolume* vol = lower->add_volume(lower_mesh);
vol->name = volume->name;
vol->config = volume->config;
// Don't copy the config's ID.
static_cast<DynamicPrintConfig&>(vol->config) = static_cast<const DynamicPrintConfig&>(volume->config);
assert(vol->config.id().valid());
assert(vol->config.id() != volume->config.id());
vol->set_material(volume->material_id(), *volume->material());
// Compute the lower part instances' bounding boxes to figure out where to place
@ -1272,7 +1283,10 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
// XXX: this seems to be the only real usage of m_model, maybe refactor this so that it's not needed?
ModelObject* new_object = m_model->add_object();
new_object->name = this->name;
new_object->config = this->config;
// Don't copy the config's ID.
static_cast<DynamicPrintConfig&>(new_object->config) = static_cast<const DynamicPrintConfig&>(this->config);
assert(new_object->config.id().valid());
assert(new_object->config.id() != this->config.id());
new_object->instances.reserve(this->instances.size());
for (const ModelInstance *model_instance : this->instances)
new_object->add_instance(*model_instance);
@ -1565,9 +1579,9 @@ void ModelVolume::center_geometry_after_creation()
if (!shift.isApprox(Vec3d::Zero()))
{
if (m_mesh)
m_mesh->translate(-(float)shift(0), -(float)shift(1), -(float)shift(2));
const_cast<TriangleMesh*>(m_mesh.get())->translate(-(float)shift(0), -(float)shift(1), -(float)shift(2));
if (m_convex_hull)
m_convex_hull->translate(-(float)shift(0), -(float)shift(1), -(float)shift(2));
const_cast<TriangleMesh*>(m_convex_hull.get())->translate(-(float)shift(0), -(float)shift(1), -(float)shift(2));
translate(shift);
}
}
@ -1720,10 +1734,10 @@ void ModelVolume::mirror(Axis axis)
}
// This method could only be called before the meshes of this ModelVolumes are not shared!
void ModelVolume::scale_geometry(const Vec3d& versor)
void ModelVolume::scale_geometry_after_creation(const Vec3d& versor)
{
m_mesh->scale(versor);
m_convex_hull->scale(versor);
const_cast<TriangleMesh*>(m_mesh.get())->scale(versor);
const_cast<TriangleMesh*>(m_convex_hull.get())->scale(versor);
}
void ModelVolume::transform_this_mesh(const Transform3d &mesh_trafo, bool fix_left_handed)
@ -1867,21 +1881,26 @@ bool model_volume_list_changed(const ModelObject &model_object_old, const ModelO
// Verify whether the IDs of Model / ModelObject / ModelVolume / ModelInstance / ModelMaterial are valid and unique.
void check_model_ids_validity(const Model &model)
{
std::set<ModelID> ids;
auto check = [&ids](ModelID id) {
assert(id.id > 0);
std::set<ObjectID> ids;
auto check = [&ids](ObjectID id) {
assert(id.valid());
assert(ids.find(id) == ids.end());
ids.insert(id);
};
for (const ModelObject *model_object : model.objects) {
check(model_object->id());
for (const ModelVolume *model_volume : model_object->volumes)
check(model_object->config.id());
for (const ModelVolume *model_volume : model_object->volumes) {
check(model_volume->id());
check(model_volume->config.id());
}
for (const ModelInstance *model_instance : model_object->instances)
check(model_instance->id());
}
for (const auto mm : model.materials)
for (const auto mm : model.materials) {
check(mm.second->id());
check(mm.second->config.id());
}
}
void check_model_ids_equal(const Model &model1, const Model &model2)
@ -1892,10 +1911,13 @@ void check_model_ids_equal(const Model &model1, const Model &model2)
const ModelObject &model_object1 = *model1.objects[idx_model];
const ModelObject &model_object2 = * model2.objects[idx_model];
assert(model_object1.id() == model_object2.id());
assert(model_object1.config.id() == model_object2.config.id());
assert(model_object1.volumes.size() == model_object2.volumes.size());
assert(model_object1.instances.size() == model_object2.instances.size());
for (size_t i = 0; i < model_object1.volumes.size(); ++ i)
for (size_t i = 0; i < model_object1.volumes.size(); ++ i) {
assert(model_object1.volumes[i]->id() == model_object2.volumes[i]->id());
assert(model_object1.volumes[i]->config.id() == model_object2.volumes[i]->config.id());
}
for (size_t i = 0; i < model_object1.instances.size(); ++ i)
assert(model_object1.instances[i]->id() == model_object2.instances[i]->id());
}
@ -1906,9 +1928,22 @@ void check_model_ids_equal(const Model &model1, const Model &model2)
for (; it1 != model1.materials.end(); ++ it1, ++ it2) {
assert(it1->first == it2->first); // compare keys
assert(it1->second->id() == it2->second->id());
assert(it1->second->config.id() == it2->second->config.id());
}
}
}
#endif /* NDEBUG */
}
#if 0
CEREAL_REGISTER_TYPE(Slic3r::ModelObject)
CEREAL_REGISTER_TYPE(Slic3r::ModelVolume)
CEREAL_REGISTER_TYPE(Slic3r::ModelInstance)
CEREAL_REGISTER_TYPE(Slic3r::Model)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ObjectBase, Slic3r::ModelObject)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ObjectBase, Slic3r::ModelVolume)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ObjectBase, Slic3r::ModelInstance)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ObjectBase, Slic3r::Model)
#endif

View file

@ -2,19 +2,20 @@
#define slic3r_Model_hpp_
#include "libslic3r.h"
#include "PrintConfig.hpp"
#include "Geometry.hpp"
#include "Layer.hpp"
#include "ObjectID.hpp"
#include "Point.hpp"
#include "TriangleMesh.hpp"
#include "PrintConfig.hpp"
#include "Slicing.hpp"
#include "SLA/SLACommon.hpp"
#include "TriangleMesh.hpp"
#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "Geometry.hpp"
#include <libslic3r/SLA/SLACommon.hpp>
namespace Slic3r {
@ -26,6 +27,38 @@ class ModelVolume;
class Print;
class SLAPrint;
namespace UndoRedo {
class StackImpl;
}
class ModelConfig : public ObjectBase, public DynamicPrintConfig
{
private:
friend class cereal::access;
friend class UndoRedo::StackImpl;
friend class ModelObject;
friend class ModelVolume;
friend class ModelMaterial;
// Constructors to be only called by derived classes.
// Default constructor to assign a unique ID.
explicit ModelConfig() {}
// Constructor with ignored int parameter to assign an invalid ID, to be replaced
// by an existing ID copied from elsewhere.
explicit ModelConfig(int) : ObjectBase(-1) {}
// Copy constructor copies the ID.
explicit ModelConfig(const ModelConfig &cfg) : ObjectBase(-1), DynamicPrintConfig(cfg) { this->copy_id(cfg); }
// Move constructor copies the ID.
explicit ModelConfig(ModelConfig &&cfg) : ObjectBase(-1), DynamicPrintConfig(std::move(cfg)) { this->copy_id(cfg); }
ModelConfig& operator=(const ModelConfig &rhs) = default;
ModelConfig& operator=(ModelConfig &&rhs) = default;
template<class Archive> void serialize(Archive &ar) {
ar(cereal::base_class<DynamicPrintConfig>(this));
}
};
typedef std::string t_model_material_id;
typedef std::string t_model_material_attribute;
typedef std::map<t_model_material_attribute, std::string> t_model_material_attributes;
@ -35,74 +68,13 @@ typedef std::vector<ModelObject*> ModelObjectPtrs;
typedef std::vector<ModelVolume*> ModelVolumePtrs;
typedef std::vector<ModelInstance*> ModelInstancePtrs;
// Unique identifier of a Model, ModelObject, ModelVolume, ModelInstance or ModelMaterial.
// Used to synchronize the front end (UI) with the back end (BackgroundSlicingProcess / Print / PrintObject)
// Valid IDs are strictly positive (non zero).
// It is declared as an object, as some compilers (notably msvcc) consider a typedef size_t equivalent to size_t
// for parameter overload.
struct ModelID
{
ModelID(size_t id) : id(id) {}
bool operator==(const ModelID &rhs) const { return this->id == rhs.id; }
bool operator!=(const ModelID &rhs) const { return this->id != rhs.id; }
bool operator< (const ModelID &rhs) const { return this->id < rhs.id; }
bool operator> (const ModelID &rhs) const { return this->id > rhs.id; }
bool operator<=(const ModelID &rhs) const { return this->id <= rhs.id; }
bool operator>=(const ModelID &rhs) const { return this->id >= rhs.id; }
bool valid() const { return id != 0; }
size_t id;
};
// Unique object / instance ID for the wipe tower.
extern ModelID wipe_tower_object_id();
extern ModelID wipe_tower_instance_id();
// Base for Model, ModelObject, ModelVolume, ModelInstance or ModelMaterial to provide a unique ID
// to synchronize the front end (UI) with the back end (BackgroundSlicingProcess / Print / PrintObject).
// Achtung! The s_last_id counter is not thread safe, so it is expected, that the ModelBase derived instances
// are only instantiated from the main thread.
class ModelBase
{
public:
ModelID id() const { return m_id; }
protected:
// Constructors to be only called by derived classes.
// Default constructor to assign a unique ID.
ModelBase() : m_id(generate_new_id()) {}
// Constructor with ignored int parameter to assign an invalid ID, to be replaced
// by an existing ID copied from elsewhere.
ModelBase(int) : m_id(ModelID(0)) {}
// Use with caution!
void set_new_unique_id() { m_id = generate_new_id(); }
void set_invalid_id() { m_id = 0; }
// Use with caution!
void copy_id(const ModelBase &rhs) { m_id = rhs.id(); }
// Override this method if a ModelBase derived class owns other ModelBase derived instances.
void assign_new_unique_ids_recursive() { this->set_new_unique_id(); }
private:
ModelID m_id;
static inline ModelID generate_new_id() { return ModelID(++ s_last_id); }
static size_t s_last_id;
friend ModelID wipe_tower_object_id();
friend ModelID wipe_tower_instance_id();
};
#define MODELBASE_DERIVED_COPY_MOVE_CLONE(TYPE) \
#define OBJECTBASE_DERIVED_COPY_MOVE_CLONE(TYPE) \
/* Copy a model, copy the IDs. The Print::apply() will call the TYPE::copy() method */ \
/* to make a private copy for background processing. */ \
static TYPE* new_copy(const TYPE &rhs) { return new TYPE(rhs); } \
static TYPE* new_copy(TYPE &&rhs) { return new TYPE(std::move(rhs)); } \
static TYPE make_copy(const TYPE &rhs) { return TYPE(rhs); } \
static TYPE make_copy(TYPE &&rhs) { return TYPE(std::move(rhs)); } \
static TYPE* new_copy(const TYPE &rhs) { auto *ret = new TYPE(rhs); assert(ret->id() == rhs.id()); return ret; } \
static TYPE* new_copy(TYPE &&rhs) { auto *ret = new TYPE(std::move(rhs)); assert(ret->id() == rhs.id()); return ret; } \
static TYPE make_copy(const TYPE &rhs) { TYPE ret(rhs); assert(ret.id() == rhs.id()); return ret; } \
static TYPE make_copy(TYPE &&rhs) { TYPE ret(std::move(rhs)); assert(ret.id() == rhs.id()); return ret; } \
TYPE& assign_copy(const TYPE &rhs); \
TYPE& assign_copy(TYPE &&rhs); \
/* Copy a TYPE, generate new IDs. The front end will use this call. */ \
@ -110,52 +82,62 @@ private:
/* Default constructor assigning an invalid ID. */ \
auto obj = new TYPE(-1); \
obj->assign_clone(rhs); \
assert(obj->id().valid() && obj->id() != rhs.id()); \
return obj; \
} \
TYPE make_clone(const TYPE &rhs) { \
/* Default constructor assigning an invalid ID. */ \
TYPE obj(-1); \
obj.assign_clone(rhs); \
assert(obj.id().valid() && obj.id() != rhs.id()); \
return obj; \
} \
TYPE& assign_clone(const TYPE &rhs) { \
this->assign_copy(rhs); \
assert(this->id().valid() && this->id() == rhs.id()); \
this->assign_new_unique_ids_recursive(); \
assert(this->id().valid() && this->id() != rhs.id()); \
return *this; \
}
#define MODELBASE_DERIVED_PRIVATE_COPY_MOVE(TYPE) \
private: \
/* Private constructor with an unused int parameter will create a TYPE instance with an invalid ID. */ \
explicit TYPE(int) : ModelBase(-1) {}; \
void assign_new_unique_ids_recursive();
// Material, which may be shared across multiple ModelObjects of a single Model.
class ModelMaterial : public ModelBase
class ModelMaterial final : public ObjectBase
{
public:
// Attributes are defined by the AMF file format, but they don't seem to be used by Slic3r for any purpose.
t_model_material_attributes attributes;
// Dynamic configuration storage for the object specific configuration values, overriding the global configuration.
DynamicPrintConfig config;
ModelConfig config;
Model* get_model() const { return m_model; }
void apply(const t_model_material_attributes &attributes)
{ this->attributes.insert(attributes.begin(), attributes.end()); }
protected:
friend class Model;
// Constructor, which assigns a new unique ID.
ModelMaterial(Model *model) : m_model(model) {}
// Copy constructor copies the ID and m_model!
ModelMaterial(const ModelMaterial &rhs) = default;
void set_model(Model *model) { m_model = model; }
private:
// Parent, owning this material.
Model *m_model;
ModelMaterial() = delete;
// To be accessed by the Model.
friend class Model;
// Constructor, which assigns a new unique ID to the material and to its config.
ModelMaterial(Model *model) : m_model(model) { assert(this->id().valid()); }
// Copy constructor copies the IDs of the ModelMaterial and its config, and m_model!
ModelMaterial(const ModelMaterial &rhs) = default;
void set_model(Model *model) { m_model = model; }
void set_new_unique_id() { ObjectBase::set_new_unique_id(); this->config.set_new_unique_id(); }
// To be accessed by the serialization and Undo/Redo code.
friend class cereal::access;
friend class UndoRedo::StackImpl;
// Create an object for deserialization, don't allocate IDs for ModelMaterial and its config.
ModelMaterial() : ObjectBase(-1), config(-1), m_model(nullptr) { assert(this->id().invalid()); assert(this->config.id().invalid()); }
template<class Archive> void serialize(Archive &ar) {
assert(this->id().invalid()); assert(this->config.id().invalid());
ar(attributes, config);
// assert(this->id().valid()); assert(this->config.id().valid());
}
// Disabled methods.
ModelMaterial(ModelMaterial &&rhs) = delete;
ModelMaterial& operator=(const ModelMaterial &rhs) = delete;
ModelMaterial& operator=(ModelMaterial &&rhs) = delete;
@ -165,9 +147,8 @@ private:
// 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,
// different rotation and different uniform scaling.
class ModelObject : public ModelBase
class ModelObject final : public ObjectBase
{
friend class Model;
public:
std::string name;
std::string input_file; // XXX: consider fs::path
@ -178,7 +159,7 @@ public:
// ModelVolumes are owned by this ModelObject.
ModelVolumePtrs volumes;
// Configuration parameters specific to a single ModelObject, overriding the global Slic3r settings.
DynamicPrintConfig config;
ModelConfig config;
// Variation of a layer thickness for spans of Z coordinates + 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.
@ -264,7 +245,7 @@ public:
void mirror(Axis axis);
// This method could only be called before the meshes of this ModelVolumes are not shared!
void scale_mesh(const Vec3d& versor);
void scale_mesh_after_creation(const Vec3d& versor);
size_t materials_count() const;
size_t facets_count() const;
@ -288,31 +269,53 @@ public:
std::string get_export_filename() const;
// Get full stl statistics for all object's meshes
// Get full stl statistics for all object's meshes
stl_stats get_object_stl_stats() const;
// Get count of errors in the mesh( or all object's meshes, if volume index isn't defined)
// Get count of errors in the mesh( or all object's meshes, if volume index isn't defined)
int get_mesh_errors_count(const int vol_idx = -1) const;
protected:
friend class Print;
friend class SLAPrint;
// Called by Print::apply() to set the model pointer after making a copy.
void set_model(Model *model) { m_model = model; }
private:
ModelObject(Model *model) : m_model(model), origin_translation(Vec3d::Zero()),
m_bounding_box_valid(false), m_raw_bounding_box_valid(false), m_raw_mesh_bounding_box_valid(false) {}
~ModelObject();
friend class Model;
// This constructor assigns new ID to this ModelObject and its config.
explicit ModelObject(Model *model) : m_model(model), origin_translation(Vec3d::Zero()),
m_bounding_box_valid(false), m_raw_bounding_box_valid(false), m_raw_mesh_bounding_box_valid(false)
{ assert(this->id().valid()); }
explicit ModelObject(int) : ObjectBase(-1), config(-1), m_model(nullptr), origin_translation(Vec3d::Zero()), m_bounding_box_valid(false), m_raw_bounding_box_valid(false), m_raw_mesh_bounding_box_valid(false)
{ assert(this->id().invalid()); assert(this->config.id().invalid()); }
~ModelObject();
void assign_new_unique_ids_recursive() override;
/* To be able to return an object from own copy / clone methods. Hopefully the compiler will do the "Copy elision" */
/* (Omits copy and move(since C++11) constructors, resulting in zero - copy pass - by - value semantics). */
ModelObject(const ModelObject &rhs) : ModelBase(-1), m_model(rhs.m_model) { this->assign_copy(rhs); }
explicit ModelObject(ModelObject &&rhs) : ModelBase(-1) { this->assign_copy(std::move(rhs)); }
ModelObject& operator=(const ModelObject &rhs) { this->assign_copy(rhs); m_model = rhs.m_model; return *this; }
ModelObject& operator=(ModelObject &&rhs) { this->assign_copy(std::move(rhs)); m_model = rhs.m_model; return *this; }
// To be able to return an object from own copy / clone methods. Hopefully the compiler will do the "Copy elision"
// (Omits copy and move(since C++11) constructors, resulting in zero - copy pass - by - value semantics).
ModelObject(const ModelObject &rhs) : ObjectBase(-1), config(-1), m_model(rhs.m_model) {
assert(this->id().invalid()); assert(this->config.id().invalid()); assert(rhs.id() != rhs.config.id());
this->assign_copy(rhs);
assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id());
assert(this->id() == rhs.id()); assert(this->config.id() == rhs.config.id());
}
explicit ModelObject(ModelObject &&rhs) : ObjectBase(-1), config(-1) {
assert(this->id().invalid()); assert(this->config.id().invalid()); assert(rhs.id() != rhs.config.id());
this->assign_copy(std::move(rhs));
assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id());
assert(this->id() == rhs.id()); assert(this->config.id() == rhs.config.id());
}
ModelObject& operator=(const ModelObject &rhs) {
this->assign_copy(rhs);
m_model = rhs.m_model;
assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id());
assert(this->id() == rhs.id()); assert(this->config.id() == rhs.config.id());
return *this;
}
ModelObject& operator=(ModelObject &&rhs) {
this->assign_copy(std::move(rhs));
m_model = rhs.m_model;
assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id());
assert(this->id() == rhs.id()); assert(this->config.id() == rhs.config.id());
return *this;
}
void set_new_unique_id() { ObjectBase::set_new_unique_id(); this->config.set_new_unique_id(); }
MODELBASE_DERIVED_COPY_MOVE_CLONE(ModelObject)
MODELBASE_DERIVED_PRIVATE_COPY_MOVE(ModelObject)
OBJECTBASE_DERIVED_COPY_MOVE_CLONE(ModelObject)
// Parent object, owning this ModelObject. Set to nullptr here, so the macros above will have it initialized.
Model *m_model = nullptr;
@ -323,7 +326,25 @@ private:
mutable BoundingBoxf3 m_raw_bounding_box;
mutable bool m_raw_bounding_box_valid;
mutable BoundingBoxf3 m_raw_mesh_bounding_box;
mutable bool m_raw_mesh_bounding_box_valid;
mutable bool m_raw_mesh_bounding_box_valid;
// Called by Print::apply() to set the model pointer after making a copy.
friend class Print;
friend class SLAPrint;
void set_model(Model *model) { m_model = model; }
// Undo / Redo through the cereal serialization library
friend class cereal::access;
friend class UndoRedo::StackImpl;
// Used for deserialization -> Don't allocate any IDs for the ModelObject or its config.
ModelObject() : ObjectBase(-1), config(-1), m_model(nullptr), m_bounding_box_valid(false), m_raw_bounding_box_valid(false), m_raw_mesh_bounding_box_valid(false) {
assert(this->id().invalid()); assert(this->config.id().invalid());
}
template<class Archive> void serialize(Archive &ar) {
ar(cereal::base_class<ObjectBase>(this));
ar(name, input_file, instances, volumes, config, layer_config_ranges, layer_height_profile, sla_support_points, sla_points_status, origin_translation,
m_bounding_box, m_bounding_box_valid, m_raw_bounding_box, m_raw_bounding_box_valid, m_raw_mesh_bounding_box, m_raw_mesh_bounding_box_valid);
}
};
// Declared outside of ModelVolume, so it could be forward declared.
@ -337,20 +358,20 @@ enum class ModelVolumeType : int {
// An object STL, or a modifier volume, over which a different set of parameters shall be applied.
// ModelVolume instances are owned by a ModelObject.
class ModelVolume : public ModelBase
class ModelVolume final : public ObjectBase
{
public:
std::string name;
// The triangular model.
const TriangleMesh& mesh() const { return *m_mesh.get(); }
void set_mesh(const TriangleMesh &mesh) { m_mesh = std::make_shared<TriangleMesh>(mesh); }
void set_mesh(TriangleMesh &&mesh) { m_mesh = std::make_shared<TriangleMesh>(std::move(mesh)); }
void set_mesh(std::shared_ptr<TriangleMesh> &mesh) { m_mesh = mesh; }
void set_mesh(std::unique_ptr<TriangleMesh> &&mesh) { m_mesh = std::move(mesh); }
void reset_mesh() { m_mesh = std::make_shared<TriangleMesh>(); }
void set_mesh(const TriangleMesh &mesh) { m_mesh = std::make_shared<const TriangleMesh>(mesh); }
void set_mesh(TriangleMesh &&mesh) { m_mesh = std::make_shared<const TriangleMesh>(std::move(mesh)); }
void set_mesh(std::shared_ptr<const TriangleMesh> &mesh) { m_mesh = mesh; }
void set_mesh(std::unique_ptr<const TriangleMesh> &&mesh) { m_mesh = std::move(mesh); }
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.
DynamicPrintConfig config;
ModelConfig config;
// A parent object owning this modifier volume.
ModelObject* get_object() const { return this->object; };
@ -385,7 +406,7 @@ public:
void mirror(Axis axis);
// This method could only be called before the meshes of this ModelVolumes are not shared!
void scale_geometry(const Vec3d& versor);
void scale_geometry_after_creation(const Vec3d& versor);
// Translates the mesh and the convex hull so that the origin of their vertices is in the center of this volume's bounding box.
// Attention! This method may only be called just after ModelVolume creation! It must not be called once the TriangleMesh of this ModelVolume is shared!
@ -431,66 +452,88 @@ public:
const Transform3d& get_matrix(bool dont_translate = false, bool dont_rotate = false, bool dont_scale = false, bool dont_mirror = false) const { return m_transformation.get_matrix(dont_translate, dont_rotate, dont_scale, dont_mirror); }
using ModelBase::set_new_unique_id;
void set_new_unique_id() { ObjectBase::set_new_unique_id(); this->config.set_new_unique_id(); }
protected:
friend class Print;
friend class SLAPrint;
friend class Model;
friend class ModelObject;
// Copies IDs of both the ModelVolume and its config.
explicit ModelVolume(const ModelVolume &rhs) = default;
void set_model_object(ModelObject *model_object) { object = model_object; }
void assign_new_unique_ids_recursive() override { ObjectBase::set_new_unique_id(); config.set_new_unique_id(); }
void transform_this_mesh(const Transform3d& t, bool fix_left_handed);
void transform_this_mesh(const Matrix3d& m, bool fix_left_handed);
private:
// Parent object owning this ModelVolume.
ModelObject* object;
ModelObject* object;
// The triangular model.
std::shared_ptr<TriangleMesh> m_mesh;
std::shared_ptr<const TriangleMesh> m_mesh;
// Is it an object to be printed, or a modifier volume?
ModelVolumeType m_type;
t_model_material_id m_material_id;
ModelVolumeType m_type;
t_model_material_id m_material_id;
// The convex hull of this model's mesh.
std::shared_ptr<TriangleMesh> m_convex_hull;
Geometry::Transformation m_transformation;
std::shared_ptr<const TriangleMesh> m_convex_hull;
Geometry::Transformation m_transformation;
// flag to optimize the checking if the volume is splittable
// -1 -> is unknown value (before first cheking)
// 0 -> is not splittable
// 1 -> is splittable
mutable int m_is_splittable{ -1 };
mutable int m_is_splittable{ -1 };
ModelVolume(ModelObject *object, const TriangleMesh &mesh) : m_mesh(new TriangleMesh(mesh)), m_type(ModelVolumeType::MODEL_PART), object(object)
{
assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id());
if (mesh.stl.stats.number_of_facets > 1)
calculate_convex_hull();
}
ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull) :
m_mesh(new TriangleMesh(std::move(mesh))), m_convex_hull(new TriangleMesh(std::move(convex_hull))), m_type(ModelVolumeType::MODEL_PART), object(object) {}
m_mesh(new TriangleMesh(std::move(mesh))), m_convex_hull(new TriangleMesh(std::move(convex_hull))), m_type(ModelVolumeType::MODEL_PART), object(object) {
assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id());
}
// Copying an existing volume, therefore this volume will get a copy of the ID assigned.
ModelVolume(ModelObject *object, const ModelVolume &other) :
ModelBase(other), // copy the ID
ObjectBase(other),
name(other.name), m_mesh(other.m_mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation)
{
assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id());
assert(this->id() == other.id() && this->config.id() == other.config.id());
this->set_material_id(other.material_id());
}
// Providing a new mesh, therefore this volume will get a new unique ID assigned.
ModelVolume(ModelObject *object, const ModelVolume &other, const TriangleMesh &&mesh) :
name(other.name), m_mesh(new TriangleMesh(std::move(mesh))), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation)
{
assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id());
assert(this->id() != other.id() && this->config.id() == other.config.id());
this->set_material_id(other.material_id());
this->config.set_new_unique_id();
if (mesh.stl.stats.number_of_facets > 1)
calculate_convex_hull();
assert(this->config.id().valid()); assert(this->config.id() != other.config.id()); assert(this->id() != this->config.id());
}
ModelVolume& operator=(ModelVolume &rhs) = delete;
friend class cereal::access;
friend class UndoRedo::StackImpl;
// Used for deserialization, therefore no IDs are allocated.
ModelVolume() : ObjectBase(-1), config(-1), object(nullptr) {
assert(this->id().invalid()); assert(this->config.id().invalid());
}
template<class Archive> void serialize(Archive &ar) {
ar(name, config, m_mesh, m_type, m_material_id, m_convex_hull, m_transformation, m_is_splittable);
}
};
// A single instance of a ModelObject.
// Knows the affine transformation of an object.
class ModelInstance : public ModelBase
class ModelInstance final : public ObjectBase
{
public:
enum EPrintVolumeState : unsigned char
@ -556,6 +599,7 @@ public:
protected:
friend class Print;
friend class SLAPrint;
friend class Model;
friend class ModelObject;
explicit ModelInstance(const ModelInstance &rhs) = default;
@ -566,15 +610,22 @@ private:
ModelObject* object;
// Constructor, which assigns a new unique ID.
explicit ModelInstance(ModelObject *object) : object(object), print_volume_state(PVS_Inside) {}
explicit ModelInstance(ModelObject *object) : object(object), print_volume_state(PVS_Inside) { assert(this->id().valid()); }
// Constructor, which assigns a new unique ID.
explicit ModelInstance(ModelObject *object, const ModelInstance &other) :
m_transformation(other.m_transformation), object(object), print_volume_state(PVS_Inside) {}
m_transformation(other.m_transformation), object(object), print_volume_state(PVS_Inside) { assert(this->id().valid() && this->id() != other.id()); }
ModelInstance() = delete;
explicit ModelInstance(ModelInstance &&rhs) = delete;
ModelInstance& operator=(const ModelInstance &rhs) = delete;
ModelInstance& operator=(ModelInstance &&rhs) = delete;
friend class cereal::access;
friend class UndoRedo::StackImpl;
// Used for deserialization, therefore no IDs are allocated.
ModelInstance() : ObjectBase(-1), object(nullptr) { assert(this->id().invalid()); }
template<class Archive> void serialize(Archive &ar) {
ar(m_transformation, print_volume_state);
}
};
// The print bed content.
@ -582,7 +633,7 @@ private:
// and with multiple modifier meshes.
// A model groups multiple objects, each object having possibly multiple instances,
// all objects may share mutliple materials.
class Model : public ModelBase
class Model final : public ObjectBase
{
static unsigned int s_auto_extruder_id;
@ -594,17 +645,17 @@ public:
ModelObjectPtrs objects;
// Default constructor assigns a new ID to the model.
Model() {}
Model() { assert(this->id().valid()); }
~Model() { this->clear_objects(); this->clear_materials(); }
// To be able to return an object from own copy / clone methods. Hopefully the compiler will do the "Copy elision"
// (Omits copy and move(since C++11) constructors, resulting in zero - copy pass - by - value semantics).
Model(const Model &rhs) : ModelBase(-1) { this->assign_copy(rhs); }
explicit Model(Model &&rhs) : ModelBase(-1) { this->assign_copy(std::move(rhs)); }
Model& operator=(const Model &rhs) { this->assign_copy(rhs); return *this; }
Model& operator=(Model &&rhs) { this->assign_copy(std::move(rhs)); return *this; }
/* To be able to return an object from own copy / clone methods. Hopefully the compiler will do the "Copy elision" */
/* (Omits copy and move(since C++11) constructors, resulting in zero - copy pass - by - value semantics). */
Model(const Model &rhs) : ObjectBase(-1) { assert(this->id().invalid()); this->assign_copy(rhs); assert(this->id().valid()); assert(this->id() == rhs.id()); }
explicit Model(Model &&rhs) : ObjectBase(-1) { assert(this->id().invalid()); this->assign_copy(std::move(rhs)); assert(this->id().valid()); assert(this->id() == rhs.id()); }
Model& operator=(const Model &rhs) { this->assign_copy(rhs); assert(this->id().valid()); assert(this->id() == rhs.id()); return *this; }
Model& operator=(Model &&rhs) { this->assign_copy(std::move(rhs)); assert(this->id().valid()); assert(this->id() == rhs.id()); return *this; }
MODELBASE_DERIVED_COPY_MOVE_CLONE(Model)
OBJECTBASE_DERIVED_COPY_MOVE_CLONE(Model)
static Model read_from_file(const std::string &input_file, DynamicPrintConfig *config = nullptr, bool add_default_instances = true);
static Model read_from_archive(const std::string &input_file, DynamicPrintConfig *config, bool add_default_instances = true);
@ -615,7 +666,7 @@ public:
ModelObject* add_object(const char *name, const char *path, TriangleMesh &&mesh);
ModelObject* add_object(const ModelObject &other);
void delete_object(size_t idx);
bool delete_object(ModelID id);
bool delete_object(ObjectID id);
bool delete_object(ModelObject* object);
void clear_objects();
@ -633,24 +684,24 @@ public:
BoundingBoxf3 bounding_box() const;
// Set the print_volume_state of PrintObject::instances,
// return total number of printable objects.
unsigned int update_print_volume_state(const BoundingBoxf3 &print_volume);
unsigned int update_print_volume_state(const BoundingBoxf3 &print_volume);
// Returns true if any ModelObject was modified.
bool center_instances_around_point(const Vec2d &point);
void translate(coordf_t x, coordf_t y, coordf_t z) { for (ModelObject *o : this->objects) o->translate(x, y, z); }
TriangleMesh mesh() const;
bool arrange_objects(coordf_t dist, const BoundingBoxf* bb = NULL);
bool center_instances_around_point(const Vec2d &point);
void translate(coordf_t x, coordf_t y, coordf_t z) { for (ModelObject *o : this->objects) o->translate(x, y, z); }
TriangleMesh mesh() const;
bool arrange_objects(coordf_t dist, const BoundingBoxf* bb = NULL);
// Croaks if the duplicated objects do not fit the print bed.
void duplicate(size_t copies_num, coordf_t dist, const BoundingBoxf* bb = NULL);
void duplicate_objects(size_t copies_num, coordf_t dist, const BoundingBoxf* bb = NULL);
void duplicate_objects_grid(size_t x, size_t y, coordf_t dist);
void duplicate(size_t copies_num, coordf_t dist, const BoundingBoxf* bb = NULL);
void duplicate_objects(size_t copies_num, coordf_t dist, const BoundingBoxf* bb = NULL);
void duplicate_objects_grid(size_t x, size_t y, coordf_t dist);
bool looks_like_multipart_object() const;
void convert_multipart_object(unsigned int max_extruders);
bool looks_like_multipart_object() const;
void convert_multipart_object(unsigned int max_extruders);
// Ensures that the min z of the model is not negative
void adjust_min_z();
void adjust_min_z();
void print_info() const { for (const ModelObject *o : this->objects) o->print_info(); }
void print_info() const { for (const ModelObject *o : this->objects) o->print_info(); }
static unsigned int get_auto_extruder_id(unsigned int max_extruders);
static std::string get_auto_extruder_id_as_string(unsigned int max_extruders);
@ -662,11 +713,19 @@ public:
std::string propose_export_file_name_and_path(const std::string &new_extension) const;
private:
MODELBASE_DERIVED_PRIVATE_COPY_MOVE(Model)
explicit Model(int) : ObjectBase(-1) { assert(this->id().invalid()); };
void assign_new_unique_ids_recursive();
void update_links_bottom_up_recursive();
friend class cereal::access;
friend class UndoRedo::StackImpl;
template<class Archive> void serialize(Archive &ar) {
ar(materials, objects);
}
};
#undef MODELBASE_DERIVED_COPY_MOVE_CLONE
#undef MODELBASE_DERIVED_PRIVATE_COPY_MOVE
#undef OBJECTBASE_DERIVED_COPY_MOVE_CLONE
#undef OBJECTBASE_DERIVED_PRIVATE_COPY_MOVE
// 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.
@ -686,6 +745,6 @@ void check_model_ids_validity(const Model &model);
void check_model_ids_equal(const Model &model1, const Model &model2);
#endif /* NDEBUG */
}
} // namespace Slic3r
#endif
#endif /* slic3r_Model_hpp_ */

View file

@ -0,0 +1,22 @@
#include "ObjectID.hpp"
namespace Slic3r {
size_t ObjectBase::s_last_id = 0;
// Unique object / instance ID for the wipe tower.
ObjectID wipe_tower_object_id()
{
static ObjectBase mine;
return mine.id();
}
ObjectID wipe_tower_instance_id()
{
static ObjectBase mine;
return mine.id();
}
} // namespace Slic3r
// CEREAL_REGISTER_TYPE(Slic3r::ObjectBase)

View file

@ -0,0 +1,93 @@
#ifndef slic3r_ObjectID_hpp_
#define slic3r_ObjectID_hpp_
#include <cereal/access.hpp>
namespace Slic3r {
namespace UndoRedo {
class StackImpl;
};
// Unique identifier of a mutable object accross the application.
// Used to synchronize the front end (UI) with the back end (BackgroundSlicingProcess / Print / PrintObject)
// (for Model, ModelObject, ModelVolume, ModelInstance or ModelMaterial classes)
// and to serialize / deserialize an object onto the Undo / Redo stack.
// Valid IDs are strictly positive (non zero).
// It is declared as an object, as some compilers (notably msvcc) consider a typedef size_t equivalent to size_t
// for parameter overload.
class ObjectID
{
public:
ObjectID(size_t id) : id(id) {}
// Default constructor constructs an invalid ObjectID.
ObjectID() : id(0) {}
bool operator==(const ObjectID &rhs) const { return this->id == rhs.id; }
bool operator!=(const ObjectID &rhs) const { return this->id != rhs.id; }
bool operator< (const ObjectID &rhs) const { return this->id < rhs.id; }
bool operator> (const ObjectID &rhs) const { return this->id > rhs.id; }
bool operator<=(const ObjectID &rhs) const { return this->id <= rhs.id; }
bool operator>=(const ObjectID &rhs) const { return this->id >= rhs.id; }
bool valid() const { return id != 0; }
bool invalid() const { return id == 0; }
size_t id;
private:
friend class cereal::access;
template<class Archive> void serialize(Archive &ar) { ar(id); }
};
// Base for Model, ModelObject, ModelVolume, ModelInstance or ModelMaterial to provide a unique ID
// to synchronize the front end (UI) with the back end (BackgroundSlicingProcess / Print / PrintObject).
// Achtung! The s_last_id counter is not thread safe, so it is expected, that the ObjectBase derived instances
// are only instantiated from the main thread.
class ObjectBase
{
public:
ObjectID id() const { return m_id; }
protected:
// Constructors to be only called by derived classes.
// Default constructor to assign a unique ID.
ObjectBase() : m_id(generate_new_id()) {}
// Constructor with ignored int parameter to assign an invalid ID, to be replaced
// by an existing ID copied from elsewhere.
ObjectBase(int) : m_id(ObjectID(0)) {}
// The class tree will have virtual tables and type information.
virtual ~ObjectBase() {}
// Use with caution!
void set_new_unique_id() { m_id = generate_new_id(); }
void set_invalid_id() { m_id = 0; }
// Use with caution!
void copy_id(const ObjectBase &rhs) { m_id = rhs.id(); }
// Override this method if a ObjectBase derived class owns other ObjectBase derived instances.
virtual void assign_new_unique_ids_recursive() { this->set_new_unique_id(); }
private:
ObjectID m_id;
static inline ObjectID generate_new_id() { return ObjectID(++ s_last_id); }
static size_t s_last_id;
friend ObjectID wipe_tower_object_id();
friend ObjectID wipe_tower_instance_id();
friend class cereal::access;
friend class Slic3r::UndoRedo::StackImpl;
template<class Archive> void serialize(Archive &ar) { ar(m_id); }
ObjectBase(const ObjectID id) : m_id(id) {}
template<class Archive> static void load_and_construct(Archive & ar, cereal::construct<ObjectBase> &construct) { ObjectID id; ar(id); construct(id); }
};
// Unique object / instance ID for the wipe tower.
extern ObjectID wipe_tower_object_id();
extern ObjectID wipe_tower_instance_id();
} // namespace Slic3r
#endif /* slic3r_ObjectID_hpp_ */

View file

@ -62,8 +62,8 @@ inline Vec2i64 to_2d(const Vec3i64 &pt3) { return Vec2i64(pt3(0), pt3(1)); }
inline Vec2f to_2d(const Vec3f &pt3) { return Vec2f (pt3(0), pt3(1)); }
inline Vec2d to_2d(const Vec3d &pt3) { return Vec2d (pt3(0), pt3(1)); }
inline Vec3d to_3d(const Vec2d &v, double z) { return Vec3d(v(0), v(1), z); }
inline Vec3f to_3d(const Vec2f &v, float z) { return Vec3f(v(0), v(1), z); }
inline Vec3d to_3d(const Vec2d &v, double z) { return Vec3d(v(0), v(1), z); }
inline Vec3f to_3d(const Vec2f &v, float z) { return Vec3f(v(0), v(1), z); }
inline Vec3i64 to_3d(const Vec2i64 &v, float z) { return Vec3i64(int64_t(v(0)), int64_t(v(1)), int64_t(z)); }
inline Vec3crd to_3d(const Vec3crd &p, coord_t z) { return Vec3crd(p(0), p(1), z); }
@ -291,4 +291,21 @@ namespace boost { namespace polygon {
} }
// end Boost
// Serialization through the Cereal library
namespace cereal {
// template<class Archive> void serialize(Archive& archive, Slic3r::Vec2crd &v) { archive(v.x(), v.y()); }
// template<class Archive> void serialize(Archive& archive, Slic3r::Vec3crd &v) { archive(v.x(), v.y(), v.z()); }
template<class Archive> void serialize(Archive& archive, Slic3r::Vec2i &v) { archive(v.x(), v.y()); }
template<class Archive> void serialize(Archive& archive, Slic3r::Vec3i &v) { archive(v.x(), v.y(), v.z()); }
// template<class Archive> void serialize(Archive& archive, Slic3r::Vec2i64 &v) { archive(v.x(), v.y()); }
// template<class Archive> void serialize(Archive& archive, Slic3r::Vec3i64 &v) { archive(v.x(), v.y(), v.z()); }
template<class Archive> void serialize(Archive& archive, Slic3r::Vec2f &v) { archive(v.x(), v.y()); }
template<class Archive> void serialize(Archive& archive, Slic3r::Vec3f &v) { archive(v.x(), v.y(), v.z()); }
template<class Archive> void serialize(Archive& archive, Slic3r::Vec2d &v) { archive(v.x(), v.y()); }
template<class Archive> void serialize(Archive& archive, Slic3r::Vec3d &v) { archive(v.x(), v.y(), v.z()); }
template<class Archive> void load(Archive& archive, Slic3r::Matrix2f &m) { archive.loadBinary((char*)m.data(), sizeof(float) * 4); }
template<class Archive> void save(Archive& archive, Slic3r::Matrix2f &m) { archive.saveBinary((char*)m.data(), sizeof(float) * 4); }
}
#endif

View file

@ -392,7 +392,7 @@ static inline void model_volume_list_copy_configs(ModelObject &model_object_dst,
assert(mv_src.id() == mv_dst.id());
// Copy the ModelVolume data.
mv_dst.name = mv_src.name;
mv_dst.config = mv_src.config;
static_cast<DynamicPrintConfig&>(mv_dst.config) = static_cast<const DynamicPrintConfig&>(mv_src.config);
//FIXME what to do with the materials?
// mv_dst.m_material_id = mv_src.m_material_id;
++ i_src;
@ -587,10 +587,10 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
Moved,
Deleted,
};
ModelObjectStatus(ModelID id, Status status = Unknown) : id(id), status(status) {}
ModelID id;
Status status;
LayerRanges layer_ranges;
ModelObjectStatus(ObjectID id, Status status = Unknown) : id(id), status(status) {}
ObjectID id;
Status status;
LayerRanges layer_ranges;
// Search by id.
bool operator<(const ModelObjectStatus &rhs) const { return id < rhs.id; }
};
@ -695,9 +695,9 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
print_object(print_object),
trafo(print_object->trafo()),
status(status) {}
PrintObjectStatus(ModelID id) : id(id), print_object(nullptr), trafo(Transform3d::Identity()), status(Unknown) {}
PrintObjectStatus(ObjectID id) : id(id), print_object(nullptr), trafo(Transform3d::Identity()), status(Unknown) {}
// ID of the ModelObject & PrintObject
ModelID id;
ObjectID id;
// Pointer to the old PrintObject
PrintObject *print_object;
// Trafo generated with model_object->world_matrix(true)
@ -757,7 +757,7 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
// Synchronize Object's config.
bool object_config_changed = model_object.config != model_object_new.config;
if (object_config_changed)
model_object.config = model_object_new.config;
static_cast<DynamicPrintConfig&>(model_object.config) = static_cast<const DynamicPrintConfig&>(model_object_new.config);
if (! object_diff.empty() || object_config_changed) {
PrintObjectConfig new_config = PrintObject::object_config_from_model_object(m_default_object_config, model_object, num_extruders);
auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id()));

View file

@ -246,7 +246,7 @@ public:
struct TaskParams {
TaskParams() : single_model_object(0), single_model_instance_only(false), to_object_step(-1), to_print_step(-1) {}
// If non-empty, limit the processing to this ModelObject.
ModelID single_model_object;
ObjectID single_model_object;
// If set, only process single_model_object. Otherwise process everything, but single_model_object first.
bool single_model_instance_only;
// If non-negative, stop processing at the successive object step.

View file

@ -406,10 +406,13 @@ void PrintConfigDef::init_fff_params()
def->set_default_value(new ConfigOptionEnum<InfillPattern>(ipRectilinear));
def = this->add("bottom_fill_pattern", coEnum);
*def = *def_top_fill_pattern;
def->label = L("Bottom fill pattern");
def->category = L("Infill");
def->tooltip = L("Fill pattern for bottom infill. This only affects the bottom external visible layer, and not its adjacent solid shells.");
def->cli = "bottom-fill-pattern|external-fill-pattern|solid-fill-pattern";
def->enum_keys_map = &ConfigOptionEnum<InfillPattern>::get_enum_values();
def->enum_values = def_top_fill_pattern->enum_values;
def->aliases = def_top_fill_pattern->aliases;
def->set_default_value(new ConfigOptionEnum<InfillPattern>(ipRectilinear));
def = this->add("external_perimeter_extrusion_width", coFloatOrPercent);
@ -3250,3 +3253,7 @@ void DynamicPrintAndCLIConfig::handle_legacy(t_config_option_key &opt_key, std::
}
}
#include <cereal/types/polymorphic.hpp>
CEREAL_REGISTER_TYPE(Slic3r::DynamicPrintConfig)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::DynamicConfig, Slic3r::DynamicPrintConfig)

View file

@ -1218,6 +1218,8 @@ private:
this->options.insert(cli_actions_config_def.options.begin(), cli_actions_config_def.options.end());
this->options.insert(cli_transform_config_def.options.begin(), cli_transform_config_def.options.end());
this->options.insert(cli_misc_config_def.options.begin(), cli_misc_config_def.options.end());
for (const auto &kvp : this->options)
this->by_serialization_key_ordinal[kvp.second.serialization_key_ordinal] = &kvp.second;
}
// Do not release the default values, they are handled by print_config_def & cli_actions_config_def / cli_transform_config_def / cli_misc_config_def.
~PrintAndCLIConfigDef() { this->options.clear(); }
@ -1227,4 +1229,38 @@ private:
} // namespace Slic3r
// Serialization through the Cereal library
namespace cereal {
// Let cereal know that there are load / save non-member functions declared for DynamicPrintConfig, ignore serialize / load / save from parent class DynamicConfig.
template <class Archive> struct specialize<Archive, Slic3r::DynamicPrintConfig, cereal::specialization::non_member_load_save> {};
template<class Archive> void load(Archive& archive, Slic3r::DynamicPrintConfig &config)
{
size_t cnt;
archive(cnt);
config.clear();
for (size_t i = 0; i < cnt; ++ i) {
size_t serialization_key_ordinal;
archive(serialization_key_ordinal);
assert(serialization_key_ordinal > 0);
auto it = Slic3r::print_config_def.by_serialization_key_ordinal.find(serialization_key_ordinal);
assert(it != Slic3r::print_config_def.by_serialization_key_ordinal.end());
config.set_key_value(it->second->opt_key, it->second->load_option_from_archive(archive));
}
}
template<class Archive> void save(Archive& archive, const Slic3r::DynamicPrintConfig &config)
{
size_t cnt = config.size();
archive(cnt);
for (auto it = config.cbegin(); it != config.cend(); ++it) {
const Slic3r::ConfigOptionDef* optdef = Slic3r::print_config_def.get(it->first);
assert(optdef != nullptr);
assert(optdef->serialization_key_ordinal > 0);
archive(optdef->serialization_key_ordinal);
optdef->save_option_to_archive(archive, it->second.get());
}
}
}
#endif

View file

@ -43,6 +43,8 @@ struct SupportPoint {
bool operator==(const SupportPoint& sp) const { return (pos==sp.pos) && head_front_radius==sp.head_front_radius && is_new_island==sp.is_new_island; }
bool operator!=(const SupportPoint& sp) const { return !(sp == (*this)); }
template<class Archive> void serialize(Archive &ar) { ar(pos, head_front_radius, is_new_island); }
};
/// An index-triangle structure for libIGL functions. Also serves as an

View file

@ -211,8 +211,8 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf
Moved,
Deleted,
};
ModelObjectStatus(ModelID id, Status status = Unknown) : id(id), status(status) {}
ModelID id;
ModelObjectStatus(ObjectID id, Status status = Unknown) : id(id), status(status) {}
ObjectID id;
Status status;
// Search by id.
bool operator<(const ModelObjectStatus &rhs) const { return id < rhs.id; }
@ -315,9 +315,9 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf
print_object(print_object),
trafo(print_object->trafo()),
status(status) {}
PrintObjectStatus(ModelID id) : id(id), print_object(nullptr), trafo(Transform3d::Identity()), status(Unknown) {}
PrintObjectStatus(ObjectID id) : id(id), print_object(nullptr), trafo(Transform3d::Identity()), status(Unknown) {}
// ID of the ModelObject & PrintObject
ModelID id;
ObjectID id;
// Pointer to the old PrintObject
SLAPrintObject *print_object;
// Trafo generated with model_object->world_matrix(true)
@ -367,7 +367,7 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf
// Synchronize Object's config.
bool object_config_changed = model_object.config != model_object_new.config;
if (object_config_changed)
model_object.config = model_object_new.config;
static_cast<DynamicPrintConfig&>(model_object.config) = static_cast<const DynamicPrintConfig&>(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);

View file

@ -54,10 +54,10 @@ public:
bool is_left_handed() const { return m_left_handed; }
struct Instance {
Instance(ModelID instance_id, const Point &shift, float rotation) : instance_id(instance_id), shift(shift), rotation(rotation) {}
Instance(ObjectID instance_id, const Point &shift, float rotation) : instance_id(instance_id), shift(shift), rotation(rotation) {}
bool operator==(const Instance &rhs) const { return this->instance_id == rhs.instance_id && this->shift == rhs.shift && this->rotation == rhs.rotation; }
// ID of the corresponding ModelInstance.
ModelID instance_id;
ObjectID instance_id;
// Slic3r::Point objects in scaled G-code coordinates
Point shift;
// Rotation along the Z axis, in radians.

View file

@ -175,4 +175,9 @@ extern int generate_layer_height_texture(
}; // namespace Slic3r
namespace cereal
{
template<class Archive> void serialize(Archive& archive, Slic3r::t_layer_height_range &lhr) { archive(lhr.first, lhr.second); }
}
#endif /* slic3r_Slicing_hpp_ */

View file

@ -195,4 +195,24 @@ TriangleMesh make_sphere(double rho, double fa=(2*PI/360));
}
// Serialization through the Cereal library
#include <cereal/access.hpp>
namespace cereal {
template <class Archive> struct specialize<Archive, Slic3r::TriangleMesh, cereal::specialization::non_member_load_save> {};
template<class Archive> void load(Archive &archive, Slic3r::TriangleMesh &mesh) {
stl_file &stl = mesh.stl;
stl.stats.type = inmemory;
archive(stl.stats.number_of_facets, stl.stats.original_num_facets);
stl_allocate(&stl);
archive.loadBinary((char*)stl.facet_start.data(), stl.facet_start.size() * 50);
stl_get_size(&stl);
mesh.repair();
}
template<class Archive> void save(Archive &archive, const Slic3r::TriangleMesh &mesh) {
const stl_file& stl = mesh.stl;
archive(stl.stats.number_of_facets, stl.stats.original_num_facets);
archive.saveBinary((char*)stl.facet_start.data(), stl.facet_start.size() * 50);
}
}
#endif

View file

@ -182,7 +182,7 @@ class ScopeGuard
public:
typedef std::function<void()> Closure;
private:
bool committed;
// bool committed;
Closure closure;
public:

View file

@ -100,7 +100,10 @@
#include <tbb/task_scheduler_init.h>
#include <Eigen/Dense>
#include <Eigen/Geometry>
#include <Eigen/Geometry>
#include <cereal/access.hpp>
#include <cereal/types/base_class.hpp>
#include "BoundingBox.hpp"
#include "ClipperUtils.hpp"