mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-10-22 16:21:24 -06:00
Merge branch 'master' into fs_QuadricEdgeCollapse
This commit is contained in:
commit
89819c1c22
57 changed files with 797 additions and 415 deletions
|
@ -426,7 +426,19 @@ void ConfigBase::apply_only(const ConfigBase &other, const t_config_option_keys
|
|||
}
|
||||
}
|
||||
|
||||
// this will *ignore* options not present in both configs
|
||||
// Are the two configs equal? Ignoring options not present in both configs.
|
||||
bool ConfigBase::equals(const ConfigBase &other) const
|
||||
{
|
||||
for (const t_config_option_key &opt_key : this->keys()) {
|
||||
const ConfigOption *this_opt = this->option(opt_key);
|
||||
const ConfigOption *other_opt = other.option(opt_key);
|
||||
if (this_opt != nullptr && other_opt != nullptr && *this_opt != *other_opt)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns options differing in the two configs, ignoring options not present in both configs.
|
||||
t_config_option_keys ConfigBase::diff(const ConfigBase &other) const
|
||||
{
|
||||
t_config_option_keys diff;
|
||||
|
@ -439,6 +451,7 @@ t_config_option_keys ConfigBase::diff(const ConfigBase &other) const
|
|||
return diff;
|
||||
}
|
||||
|
||||
// Returns options being equal in the two configs, ignoring options not present in both configs.
|
||||
t_config_option_keys ConfigBase::equal(const ConfigBase &other) const
|
||||
{
|
||||
t_config_option_keys equal;
|
||||
|
@ -1190,6 +1203,65 @@ t_config_option_keys StaticConfig::keys() const
|
|||
return keys;
|
||||
}
|
||||
|
||||
// Iterate over the pairs of options with equal keys, call the fn.
|
||||
// Returns true on early exit by fn().
|
||||
template<typename Fn>
|
||||
static inline bool dynamic_config_iterate(const DynamicConfig &lhs, const DynamicConfig &rhs, Fn fn)
|
||||
{
|
||||
std::map<t_config_option_key, std::unique_ptr<ConfigOption>>::const_iterator i = lhs.cbegin();
|
||||
std::map<t_config_option_key, std::unique_ptr<ConfigOption>>::const_iterator j = rhs.cbegin();
|
||||
while (i != lhs.cend() && j != rhs.cend())
|
||||
if (i->first < j->first)
|
||||
++ i;
|
||||
else if (i->first > j->first)
|
||||
++ j;
|
||||
else {
|
||||
assert(i->first == j->first);
|
||||
if (fn(i->first, i->second.get(), j->second.get()))
|
||||
// Early exit by fn.
|
||||
return true;
|
||||
++ i;
|
||||
++ j;
|
||||
}
|
||||
// Finished to the end.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Are the two configs equal? Ignoring options not present in both configs.
|
||||
bool DynamicConfig::equals(const DynamicConfig &other) const
|
||||
{
|
||||
return ! dynamic_config_iterate(*this, other,
|
||||
[](const t_config_option_key & /* key */, const ConfigOption *l, const ConfigOption *r) { return *l != *r; });
|
||||
}
|
||||
|
||||
// Returns options differing in the two configs, ignoring options not present in both configs.
|
||||
t_config_option_keys DynamicConfig::diff(const DynamicConfig &other) const
|
||||
{
|
||||
t_config_option_keys diff;
|
||||
dynamic_config_iterate(*this, other,
|
||||
[&diff](const t_config_option_key &key, const ConfigOption *l, const ConfigOption *r) {
|
||||
if (*l != *r)
|
||||
diff.emplace_back(key);
|
||||
// Continue iterating.
|
||||
return false;
|
||||
});
|
||||
return diff;
|
||||
}
|
||||
|
||||
// Returns options being equal in the two configs, ignoring options not present in both configs.
|
||||
t_config_option_keys DynamicConfig::equal(const DynamicConfig &other) const
|
||||
{
|
||||
t_config_option_keys equal;
|
||||
dynamic_config_iterate(*this, other,
|
||||
[&equal](const t_config_option_key &key, const ConfigOption *l, const ConfigOption *r) {
|
||||
if (*l == *r)
|
||||
equal.emplace_back(key);
|
||||
// Continue iterating.
|
||||
return false;
|
||||
});
|
||||
return equal;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include <cereal/types/polymorphic.hpp>
|
||||
|
|
|
@ -1893,8 +1893,8 @@ public:
|
|||
// The configuration definition is static: It does not carry the actual configuration values,
|
||||
// but it carries the defaults of the configuration values.
|
||||
|
||||
ConfigBase() {}
|
||||
~ConfigBase() override {}
|
||||
ConfigBase() = default;
|
||||
~ConfigBase() override = default;
|
||||
|
||||
// Virtual overridables:
|
||||
public:
|
||||
|
@ -1953,8 +1953,11 @@ public:
|
|||
// An UnknownOptionException is thrown in case some option keys are not defined by this->def(),
|
||||
// or this ConfigBase is of a StaticConfig type and it does not support some of the keys, and ignore_nonexistent is not set.
|
||||
void apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false);
|
||||
bool equals(const ConfigBase &other) const { return this->diff(other).empty(); }
|
||||
// Are the two configs equal? Ignoring options not present in both configs.
|
||||
bool equals(const ConfigBase &other) const;
|
||||
// Returns options differing in the two configs, ignoring options not present in both configs.
|
||||
t_config_option_keys diff(const ConfigBase &other) const;
|
||||
// Returns options being equal in the two configs, ignoring options not present in both configs.
|
||||
t_config_option_keys equal(const ConfigBase &other) const;
|
||||
std::string opt_serialize(const t_config_option_key &opt_key) const;
|
||||
|
||||
|
@ -2022,12 +2025,12 @@ private:
|
|||
class DynamicConfig : public virtual ConfigBase
|
||||
{
|
||||
public:
|
||||
DynamicConfig() {}
|
||||
DynamicConfig() = default;
|
||||
DynamicConfig(const DynamicConfig &rhs) { *this = rhs; }
|
||||
DynamicConfig(DynamicConfig &&rhs) noexcept : options(std::move(rhs.options)) { rhs.options.clear(); }
|
||||
explicit DynamicConfig(const ConfigBase &rhs, const t_config_option_keys &keys);
|
||||
explicit DynamicConfig(const ConfigBase& rhs) : DynamicConfig(rhs, rhs.keys()) {}
|
||||
virtual ~DynamicConfig() override { clear(); }
|
||||
virtual ~DynamicConfig() override = default;
|
||||
|
||||
// Copy a content of one DynamicConfig to another DynamicConfig.
|
||||
// If rhs.def() is not null, then it has to be equal to this->def().
|
||||
|
@ -2144,6 +2147,13 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
// Are the two configs equal? Ignoring options not present in both configs.
|
||||
bool equals(const DynamicConfig &other) const;
|
||||
// Returns options differing in the two configs, ignoring options not present in both configs.
|
||||
t_config_option_keys diff(const DynamicConfig &other) const;
|
||||
// Returns options being equal in the two configs, ignoring options not present in both configs.
|
||||
t_config_option_keys equal(const DynamicConfig &other) const;
|
||||
|
||||
std::string& opt_string(const t_config_option_key &opt_key, bool create = false) { return this->option<ConfigOptionString>(opt_key, create)->value; }
|
||||
const std::string& opt_string(const t_config_option_key &opt_key) const { return const_cast<DynamicConfig*>(this)->opt_string(opt_key); }
|
||||
std::string& opt_string(const t_config_option_key &opt_key, unsigned int idx) { return this->option<ConfigOptionStrings>(opt_key)->get_at(idx); }
|
||||
|
|
|
@ -595,7 +595,7 @@ namespace Slic3r {
|
|||
|
||||
mz_zip_archive_file_stat stat;
|
||||
|
||||
m_name = boost::filesystem::path(filename).filename().stem().string();
|
||||
m_name = boost::filesystem::path(filename).stem().string();
|
||||
|
||||
// we first loop the entries to read from the archive the .model file only, in order to extract the version from it
|
||||
for (mz_uint i = 0; i < num_entries; ++i) {
|
||||
|
@ -1408,6 +1408,13 @@ namespace Slic3r {
|
|||
m_model->delete_object(model_object);
|
||||
}
|
||||
|
||||
if (m_version == 0) {
|
||||
// if the 3mf was not produced by PrusaSlicer and there is only one object,
|
||||
// set the object name to match the filename
|
||||
if (m_model->objects.size() == 1)
|
||||
m_model->objects.front()->name = m_name;
|
||||
}
|
||||
|
||||
// applies instances' matrices
|
||||
for (Instance& instance : m_instances) {
|
||||
if (instance.instance != nullptr && instance.instance->get_object() != nullptr)
|
||||
|
|
|
@ -424,7 +424,7 @@ void Model::convert_multipart_object(unsigned int max_extruders)
|
|||
|
||||
ModelObject* object = new ModelObject(this);
|
||||
object->input_file = this->objects.front()->input_file;
|
||||
object->name = this->objects.front()->name;
|
||||
object->name = boost::filesystem::path(this->objects.front()->input_file).stem().string();
|
||||
//FIXME copy the config etc?
|
||||
|
||||
unsigned int extruder_counter = 0;
|
||||
|
@ -439,7 +439,7 @@ void Model::convert_multipart_object(unsigned int max_extruders)
|
|||
int counter = 1;
|
||||
auto copy_volume = [o, max_extruders, &counter, &extruder_counter](ModelVolume *new_v) {
|
||||
assert(new_v != nullptr);
|
||||
new_v->name = o->name + "_" + std::to_string(counter++);
|
||||
new_v->name = (counter > 1) ? o->name + "_" + std::to_string(counter++) : o->name;
|
||||
new_v->config.set("extruder", auto_extruder_id(max_extruders, extruder_counter));
|
||||
return new_v;
|
||||
};
|
||||
|
|
|
@ -24,6 +24,7 @@ public:
|
|||
|
||||
PlaceholderParser(const DynamicConfig *external_config = nullptr);
|
||||
|
||||
void clear_config() { m_config.clear(); }
|
||||
// Return a list of keys, which should be changed in m_config from rhs.
|
||||
// This contains keys, which are found in rhs, but not in m_config.
|
||||
std::vector<std::string> config_diff(const DynamicPrintConfig &rhs);
|
||||
|
|
|
@ -1194,21 +1194,38 @@ inline t_config_option_keys deep_diff(const ConfigBase &config_this, const Confi
|
|||
return diff;
|
||||
}
|
||||
|
||||
static constexpr const std::initializer_list<const char*> optional_keys { "compatible_prints", "compatible_printers" };
|
||||
|
||||
bool PresetCollection::is_dirty(const Preset *edited, const Preset *reference)
|
||||
{
|
||||
if (edited != nullptr && reference != nullptr) {
|
||||
// Only compares options existing in both configs.
|
||||
if (! reference->config.equals(edited->config))
|
||||
return true;
|
||||
// The "compatible_printers" option key is handled differently from the others:
|
||||
// It is not mandatory. If the key is missing, it means it is compatible with any printer.
|
||||
// If the key exists and it is empty, it means it is compatible with no printer.
|
||||
for (auto &opt_key : optional_keys)
|
||||
if (reference->config.has(opt_key) != edited->config.has(opt_key))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::string> PresetCollection::dirty_options(const Preset *edited, const Preset *reference, const bool deep_compare /*= false*/)
|
||||
{
|
||||
std::vector<std::string> changed;
|
||||
if (edited != nullptr && reference != nullptr) {
|
||||
// Only compares options existing in both configs.
|
||||
changed = deep_compare ?
|
||||
deep_diff(edited->config, reference->config) :
|
||||
reference->config.diff(edited->config);
|
||||
// The "compatible_printers" option key is handled differently from the others:
|
||||
// It is not mandatory. If the key is missing, it means it is compatible with any printer.
|
||||
// If the key exists and it is empty, it means it is compatible with no printer.
|
||||
std::initializer_list<const char*> optional_keys { "compatible_prints", "compatible_printers" };
|
||||
for (auto &opt_key : optional_keys) {
|
||||
for (auto &opt_key : optional_keys)
|
||||
if (reference->config.has(opt_key) != edited->config.has(opt_key))
|
||||
changed.emplace_back(opt_key);
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
@ -1385,12 +1402,16 @@ const Preset& PrinterPresetCollection::default_preset_for(const DynamicPrintConf
|
|||
return this->default_preset((opt_printer_technology == nullptr || opt_printer_technology->value == ptFFF) ? 0 : 1);
|
||||
}
|
||||
|
||||
const Preset* PrinterPresetCollection::find_by_model_id(const std::string &model_id) const
|
||||
const Preset* PrinterPresetCollection::find_system_preset_by_model_and_variant(const std::string &model_id, const std::string& variant) const
|
||||
{
|
||||
if (model_id.empty()) { return nullptr; }
|
||||
|
||||
const auto it = std::find_if(cbegin(), cend(), [&](const Preset &preset) {
|
||||
return preset.config.opt_string("printer_model") == model_id;
|
||||
if (!preset.is_system || preset.config.opt_string("printer_model") != model_id)
|
||||
return false;
|
||||
if (variant.empty())
|
||||
return true;
|
||||
return preset.config.opt_string("printer_variant") == variant;
|
||||
});
|
||||
|
||||
return it != cend() ? &*it : nullptr;
|
||||
|
|
|
@ -463,7 +463,8 @@ public:
|
|||
size_t num_visible() const { return std::count_if(m_presets.begin(), m_presets.end(), [](const Preset &preset){return preset.is_visible;}); }
|
||||
|
||||
// Compare the content of get_selected_preset() with get_edited_preset() configs, return true if they differ.
|
||||
bool current_is_dirty() const { return ! this->current_dirty_options().empty(); }
|
||||
bool current_is_dirty() const
|
||||
{ return is_dirty(&this->get_edited_preset(), &this->get_selected_preset()); }
|
||||
// Compare the content of get_selected_preset() with get_edited_preset() configs, return the list of keys where they differ.
|
||||
std::vector<std::string> current_dirty_options(const bool deep_compare = false) const
|
||||
{ return dirty_options(&this->get_edited_preset(), &this->get_selected_preset(), deep_compare); }
|
||||
|
@ -472,10 +473,11 @@ public:
|
|||
{ return dirty_options(&this->get_edited_preset(), this->get_selected_preset_parent(), deep_compare); }
|
||||
|
||||
// Compare the content of get_saved_preset() with get_edited_preset() configs, return true if they differ.
|
||||
bool saved_is_dirty() const { return !this->saved_dirty_options().empty(); }
|
||||
bool saved_is_dirty() const
|
||||
{ return is_dirty(&this->get_edited_preset(), &this->get_saved_preset()); }
|
||||
// Compare the content of get_saved_preset() with get_edited_preset() configs, return the list of keys where they differ.
|
||||
std::vector<std::string> saved_dirty_options(const bool deep_compare = false) const
|
||||
{ return dirty_options(&this->get_edited_preset(), &this->get_saved_preset(), deep_compare); }
|
||||
// std::vector<std::string> saved_dirty_options() const
|
||||
// { return dirty_options(&this->get_edited_preset(), &this->get_saved_preset(), /* deep_compare */ false); }
|
||||
// Copy edited preset into saved preset.
|
||||
void update_saved_preset_from_current_preset() { m_saved_preset = m_edited_preset; }
|
||||
|
||||
|
@ -552,7 +554,8 @@ private:
|
|||
|
||||
size_t update_compatible_internal(const PresetWithVendorProfile &active_printer, const PresetWithVendorProfile *active_print, PresetSelectCompatibleType unselect_if_incompatible);
|
||||
public:
|
||||
static std::vector<std::string> dirty_options(const Preset *edited, const Preset *reference, const bool is_printer_type = false);
|
||||
static bool is_dirty(const Preset *edited, const Preset *reference);
|
||||
static std::vector<std::string> dirty_options(const Preset *edited, const Preset *reference, const bool deep_compare = false);
|
||||
private:
|
||||
// Type of this PresetCollection: TYPE_PRINT, TYPE_FILAMENT or TYPE_PRINTER.
|
||||
Preset::Type m_type;
|
||||
|
@ -592,7 +595,7 @@ public:
|
|||
|
||||
const Preset& default_preset_for(const DynamicPrintConfig &config) const override;
|
||||
|
||||
const Preset* find_by_model_id(const std::string &model_id) const;
|
||||
const Preset* find_system_preset_by_model_and_variant(const std::string &model_id, const std::string &variant) const;
|
||||
|
||||
private:
|
||||
PrinterPresetCollection() = default;
|
||||
|
|
|
@ -188,7 +188,8 @@ void PresetBundle::setup_directories()
|
|||
}
|
||||
}
|
||||
|
||||
PresetsConfigSubstitutions PresetBundle::load_presets(AppConfig &config, ForwardCompatibilitySubstitutionRule substitution_rule, const std::string &preferred_model_id)
|
||||
PresetsConfigSubstitutions PresetBundle::load_presets(AppConfig &config, ForwardCompatibilitySubstitutionRule substitution_rule,
|
||||
const PresetPreferences& preferred_selection/* = PresetPreferences()*/)
|
||||
{
|
||||
// First load the vendor specific system presets.
|
||||
PresetsConfigSubstitutions substitutions;
|
||||
|
@ -239,7 +240,8 @@ PresetsConfigSubstitutions PresetBundle::load_presets(AppConfig &config, Forward
|
|||
if (! errors_cummulative.empty())
|
||||
throw Slic3r::RuntimeError(errors_cummulative);
|
||||
|
||||
this->load_selections(config, preferred_model_id);
|
||||
// ysToDo : set prefered filament or sla_material (relates to print technology) and force o use of preffered printer model if it was added
|
||||
this->load_selections(config, preferred_selection);
|
||||
|
||||
return substitutions;
|
||||
}
|
||||
|
@ -441,7 +443,7 @@ void PresetBundle::load_installed_sla_materials(AppConfig &config)
|
|||
|
||||
// Load selections (current print, current filaments, current printer) from config.ini
|
||||
// This is done on application start up or after updates are applied.
|
||||
void PresetBundle::load_selections(AppConfig &config, const std::string &preferred_model_id)
|
||||
void PresetBundle::load_selections(AppConfig &config, const PresetPreferences& preferred_selection/* = PresetPreferences()*/)
|
||||
{
|
||||
// Update visibility of presets based on application vendor / model / variant configuration.
|
||||
this->load_installed_printers(config);
|
||||
|
@ -464,13 +466,21 @@ void PresetBundle::load_selections(AppConfig &config, const std::string &preferr
|
|||
// will be selected by the following call of this->update_compatible(PresetSelectCompatibleType::Always).
|
||||
|
||||
const Preset *initial_printer = printers.find_preset(initial_printer_profile_name);
|
||||
const Preset *preferred_printer = printers.find_by_model_id(preferred_model_id);
|
||||
const Preset *preferred_printer = printers.find_system_preset_by_model_and_variant(preferred_selection.printer_model_id, preferred_selection.printer_variant);
|
||||
printers.select_preset_by_name(
|
||||
(preferred_printer != nullptr && (initial_printer == nullptr || !initial_printer->is_visible)) ?
|
||||
(preferred_printer != nullptr /*&& (initial_printer == nullptr || !initial_printer->is_visible)*/) ?
|
||||
preferred_printer->name :
|
||||
initial_printer_profile_name,
|
||||
true);
|
||||
|
||||
// select preferred filament/sla_material profile if any exists and is visible
|
||||
if (!preferred_selection.filament.empty())
|
||||
if (auto it = filaments.find_preset_internal(preferred_selection.filament); it != filaments.end() && it->is_visible)
|
||||
initial_filament_profile_name = it->name;
|
||||
if (!preferred_selection.sla_material.empty())
|
||||
if (auto it = sla_materials.find_preset_internal(preferred_selection.sla_material); it != sla_materials.end() && it->is_visible)
|
||||
initial_sla_material_profile_name = it->name;
|
||||
|
||||
// Selects the profile, leaves it to -1 if the initial profile name is empty or if it was not found.
|
||||
prints.select_preset_by_name_strict(initial_print_profile_name);
|
||||
filaments.select_preset_by_name_strict(initial_filament_profile_name);
|
||||
|
@ -1467,7 +1477,7 @@ std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_configbundle(
|
|||
if (! active_print.empty())
|
||||
prints.select_preset_by_name(active_print, true);
|
||||
if (! active_sla_print.empty())
|
||||
sla_materials.select_preset_by_name(active_sla_print, true);
|
||||
sla_prints.select_preset_by_name(active_sla_print, true);
|
||||
if (! active_sla_material.empty())
|
||||
sla_materials.select_preset_by_name(active_sla_material, true);
|
||||
if (! active_printer.empty())
|
||||
|
|
|
@ -25,9 +25,18 @@ public:
|
|||
|
||||
void setup_directories();
|
||||
|
||||
struct PresetPreferences {
|
||||
std::string printer_model_id;// name of a preferred printer model
|
||||
std::string printer_variant; // name of a preferred printer variant
|
||||
std::string filament; // name of a preferred filament preset
|
||||
std::string sla_material; // name of a preferred sla_material preset
|
||||
};
|
||||
|
||||
// Load ini files of all types (print, filament, printer) from Slic3r::data_dir() / presets.
|
||||
// Load selections (current print, current filaments, current printer) from config.ini
|
||||
PresetsConfigSubstitutions load_presets(AppConfig &config, ForwardCompatibilitySubstitutionRule rule, const std::string &preferred_model_id = std::string());
|
||||
// select preferred presets, if any exist
|
||||
PresetsConfigSubstitutions load_presets(AppConfig &config, ForwardCompatibilitySubstitutionRule rule,
|
||||
const PresetPreferences& preferred_selection = PresetPreferences());
|
||||
|
||||
// Export selections (current print, current filaments, current printer) into config.ini
|
||||
void export_selections(AppConfig &config);
|
||||
|
@ -153,7 +162,7 @@ private:
|
|||
|
||||
// Load selections (current print, current filaments, current printer) from config.ini
|
||||
// This is done just once on application start up.
|
||||
void load_selections(AppConfig &config, const std::string &preferred_model_id = "");
|
||||
void load_selections(AppConfig &config, const PresetPreferences& preferred_selection = PresetPreferences());
|
||||
|
||||
// Load print, filament & printer presets from a config. If it is an external config, then the name is extracted from the external path.
|
||||
// and the external config is just referenced, not stored into user profile directory.
|
||||
|
|
|
@ -216,22 +216,25 @@ static t_config_option_keys print_config_diffs(
|
|||
const ConfigOption *opt_new_filament = std::binary_search(extruder_retract_keys.begin(), extruder_retract_keys.end(), opt_key) ? new_full_config.option(filament_prefix + opt_key) : nullptr;
|
||||
if (opt_new_filament != nullptr && ! opt_new_filament->is_nil()) {
|
||||
// An extruder retract override is available at some of the filament presets.
|
||||
if (*opt_old != *opt_new || opt_new->overriden_by(opt_new_filament)) {
|
||||
bool overriden = opt_new->overriden_by(opt_new_filament);
|
||||
if (overriden || *opt_old != *opt_new) {
|
||||
auto opt_copy = opt_new->clone();
|
||||
opt_copy->apply_override(opt_new_filament);
|
||||
if (*opt_old == *opt_copy)
|
||||
delete opt_copy;
|
||||
else {
|
||||
filament_overrides.set_key_value(opt_key, opt_copy);
|
||||
bool changed = *opt_old != *opt_copy;
|
||||
if (changed)
|
||||
print_diff.emplace_back(opt_key);
|
||||
}
|
||||
if (changed || overriden) {
|
||||
// filament_overrides will be applied to the placeholder parser, which layers these parameters over full_print_config.
|
||||
filament_overrides.set_key_value(opt_key, opt_copy);
|
||||
} else
|
||||
delete opt_copy;
|
||||
}
|
||||
} else if (*opt_new != *opt_old)
|
||||
print_diff.emplace_back(opt_key);
|
||||
}
|
||||
|
||||
return print_diff;
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare for storing of the full print config into new_full_config to be exported into the G-code and to be used by the PlaceholderParser.
|
||||
static t_config_option_keys full_print_config_diffs(const DynamicPrintConfig ¤t_full_config, const DynamicPrintConfig &new_full_config)
|
||||
|
@ -812,7 +815,7 @@ static PrintObjectRegions* generate_print_object_regions(
|
|||
layer_ranges_regions.push_back({ range.layer_height_range, range.config });
|
||||
}
|
||||
|
||||
const bool is_mm_painted = std::any_of(model_volumes.cbegin(), model_volumes.cend(), [](const ModelVolume *mv) { return mv->is_mm_painted(); });
|
||||
const bool is_mm_painted = num_extruders > 1 && std::any_of(model_volumes.cbegin(), model_volumes.cend(), [](const ModelVolume *mv) { return mv->is_mm_painted(); });
|
||||
update_volume_bboxes(layer_ranges_regions, out->cached_volume_ids, model_volumes, out->trafo_bboxes, is_mm_painted ? 0.f : std::max(0.f, xy_size_compensation));
|
||||
|
||||
std::vector<PrintRegion*> region_set;
|
||||
|
@ -928,6 +931,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
|||
bool num_extruders_changed = false;
|
||||
if (! full_config_diff.empty()) {
|
||||
update_apply_status(this->invalidate_step(psGCodeExport));
|
||||
m_placeholder_parser.clear_config();
|
||||
// Set the profile aliases for the PrintBase::output_filename()
|
||||
m_placeholder_parser.set("print_preset", new_full_config.option("print_settings_id")->clone());
|
||||
m_placeholder_parser.set("filament_preset", new_full_config.option("filament_settings_id")->clone());
|
||||
|
@ -939,6 +943,8 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
|||
// It is also safe to change m_config now after this->invalidate_state_by_config_options() call.
|
||||
m_config.apply_only(new_full_config, print_diff, true);
|
||||
//FIXME use move semantics once ConfigBase supports it.
|
||||
// Some filament_overrides may contain values different from new_full_config, but equal to m_config.
|
||||
// As long as these config options don't reallocate memory when copying, we are safe overriding a value, which is in use by a worker thread.
|
||||
m_config.apply(filament_overrides);
|
||||
// Handle changes to object config defaults
|
||||
m_default_object_config.apply_only(new_full_config, object_diff, true);
|
||||
|
@ -946,8 +952,8 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
|||
m_default_region_config.apply_only(new_full_config, region_diff, true);
|
||||
m_full_print_config = std::move(new_full_config);
|
||||
if (num_extruders != m_config.nozzle_diameter.size()) {
|
||||
num_extruders = m_config.nozzle_diameter.size();
|
||||
num_extruders_changed = true;
|
||||
num_extruders = m_config.nozzle_diameter.size();
|
||||
num_extruders_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1065,7 +1071,8 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
|||
// Check whether a model part volume was added or removed, their transformations or order changed.
|
||||
// Only volume IDs, volume types, transformation matrices and their order are checked, configuration and other parameters are NOT checked.
|
||||
bool solid_or_modifier_differ = model_volume_list_changed(model_object, model_object_new, solid_or_modifier_types) ||
|
||||
model_mmu_segmentation_data_changed(model_object, model_object_new);
|
||||
model_mmu_segmentation_data_changed(model_object, model_object_new) ||
|
||||
(model_object_new.is_mm_painted() && num_extruders_changed);
|
||||
bool supports_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_BLOCKER) ||
|
||||
model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_ENFORCER);
|
||||
bool layer_height_ranges_differ = ! layer_height_ranges_equal(model_object.layer_config_ranges, model_object_new.layer_config_ranges, model_object_new.layer_height_profile.empty());
|
||||
|
@ -1267,7 +1274,8 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
|||
print_object_regions->ref_cnt_inc();
|
||||
}
|
||||
std::vector<unsigned int> painting_extruders;
|
||||
if (const auto &volumes = print_object.model_object()->volumes;
|
||||
if (const auto &volumes = print_object.model_object()->volumes;
|
||||
num_extruders > 1 &&
|
||||
std::find_if(volumes.begin(), volumes.end(), [](const ModelVolume *v) { return ! v->mmu_segmentation_facets.empty(); }) != volumes.end()) {
|
||||
//FIXME be more specific! Don't enumerate extruders that are not used for painting!
|
||||
painting_extruders.assign(num_extruders, 0);
|
||||
|
|
|
@ -167,8 +167,9 @@ static std::vector<VolumeSlices> slice_volumes_inner(
|
|||
|
||||
params_base.mode_below = params_base.mode;
|
||||
|
||||
const bool is_mm_painted = std::any_of(model_volumes.cbegin(), model_volumes.cend(), [](const ModelVolume *mv) { return mv->is_mm_painted(); });
|
||||
const auto extra_offset = is_mm_painted ? 0.f : std::max(0.f, float(print_object_config.xy_size_compensation.value));
|
||||
const size_t num_extruders = print_config.nozzle_diameter.size();
|
||||
const bool is_mm_painted = num_extruders > 1 && std::any_of(model_volumes.cbegin(), model_volumes.cend(), [](const ModelVolume *mv) { return mv->is_mm_painted(); });
|
||||
const auto extra_offset = is_mm_painted ? 0.f : std::max(0.f, float(print_object_config.xy_size_compensation.value));
|
||||
|
||||
for (const ModelVolume *model_volume : model_volumes)
|
||||
if (model_volume_needs_slicing(*model_volume)) {
|
||||
|
@ -723,6 +724,7 @@ void PrintObject::slice_volumes()
|
|||
|
||||
// Is any ModelVolume MMU painted?
|
||||
if (const auto& volumes = this->model_object()->volumes;
|
||||
m_print->config().nozzle_diameter.size() > 1 &&
|
||||
std::find_if(volumes.begin(), volumes.end(), [](const ModelVolume* v) { return !v->mmu_segmentation_facets.empty(); }) != volumes.end()) {
|
||||
|
||||
// If XY Size compensation is also enabled, notify the user that XY Size compensation
|
||||
|
@ -743,8 +745,9 @@ void PrintObject::slice_volumes()
|
|||
BOOST_LOG_TRIVIAL(debug) << "Slicing volumes - make_slices in parallel - begin";
|
||||
{
|
||||
// Compensation value, scaled. Only applying the negative scaling here, as the positive scaling has already been applied during slicing.
|
||||
const auto xy_compensation_scaled = this->is_mm_painted() ? scaled<float>(0.f) : scaled<float>(std::min(m_config.xy_size_compensation.value, 0.));
|
||||
const float elephant_foot_compensation_scaled = (m_config.raft_layers == 0) ?
|
||||
const size_t num_extruders = print->config().nozzle_diameter.size();
|
||||
const auto xy_compensation_scaled = (num_extruders > 1 && this->is_mm_painted()) ? scaled<float>(0.f) : scaled<float>(std::min(m_config.xy_size_compensation.value, 0.));
|
||||
const float elephant_foot_compensation_scaled = (m_config.raft_layers == 0) ?
|
||||
// Only enable Elephant foot compensation if printing directly on the print bed.
|
||||
float(scale_(m_config.elefant_foot_compensation.value)) :
|
||||
0.f;
|
||||
|
|
|
@ -283,64 +283,61 @@ std::array<double, N> find_min_score(Fn &&fn, It from, It to, StopCond &&stopfn)
|
|||
|
||||
} // namespace
|
||||
|
||||
// Assemble the mesh with the correct transformation to be used in rotation
|
||||
// optimization.
|
||||
TriangleMesh get_mesh_to_rotate(const ModelObject &mo)
|
||||
{
|
||||
TriangleMesh mesh = mo.raw_mesh();
|
||||
mesh.require_shared_vertices();
|
||||
|
||||
ModelInstance *mi = mo.instances[0];
|
||||
auto rotation = Vec3d::Zero();
|
||||
auto offset = Vec3d::Zero();
|
||||
Transform3d trafo_instance = Geometry::assemble_transform(offset,
|
||||
rotation,
|
||||
mi->get_scaling_factor(),
|
||||
mi->get_mirror());
|
||||
|
||||
mesh.transform(trafo_instance);
|
||||
template<unsigned MAX_ITER>
|
||||
struct RotfinderBoilerplate {
|
||||
static constexpr unsigned MAX_TRIES = MAX_ITER;
|
||||
|
||||
return mesh;
|
||||
}
|
||||
int status = 0;
|
||||
TriangleMesh mesh;
|
||||
unsigned max_tries;
|
||||
const RotOptimizeParams ¶ms;
|
||||
|
||||
// Assemble the mesh with the correct transformation to be used in rotation
|
||||
// optimization.
|
||||
static TriangleMesh get_mesh_to_rotate(const ModelObject &mo)
|
||||
{
|
||||
TriangleMesh mesh = mo.raw_mesh();
|
||||
mesh.require_shared_vertices();
|
||||
|
||||
ModelInstance *mi = mo.instances[0];
|
||||
auto rotation = Vec3d::Zero();
|
||||
auto offset = Vec3d::Zero();
|
||||
Transform3d trafo_instance =
|
||||
Geometry::assemble_transform(offset, rotation,
|
||||
mi->get_scaling_factor(),
|
||||
mi->get_mirror());
|
||||
|
||||
mesh.transform(trafo_instance);
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
RotfinderBoilerplate(const ModelObject &mo, const RotOptimizeParams &p)
|
||||
: mesh{get_mesh_to_rotate(mo)}
|
||||
, params{p}
|
||||
, max_tries(p.accuracy() * MAX_TRIES)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void statusfn() { params.statuscb()(++status * 100.0 / max_tries); }
|
||||
bool stopcond() { return ! params.statuscb()(-1); }
|
||||
};
|
||||
|
||||
Vec2d find_best_misalignment_rotation(const ModelObject & mo,
|
||||
const RotOptimizeParams ¶ms)
|
||||
{
|
||||
static constexpr unsigned MAX_TRIES = 1000;
|
||||
|
||||
// return value
|
||||
XYRotation rot;
|
||||
|
||||
// We will use only one instance of this converted mesh to examine different
|
||||
// rotations
|
||||
TriangleMesh mesh = get_mesh_to_rotate(mo);
|
||||
|
||||
// To keep track of the number of iterations
|
||||
int status = 0;
|
||||
|
||||
// The maximum number of iterations
|
||||
auto max_tries = unsigned(params.accuracy() * MAX_TRIES);
|
||||
|
||||
auto &statuscb = params.statuscb();
|
||||
|
||||
// call status callback with zero, because we are at the start
|
||||
statuscb(status);
|
||||
|
||||
auto statusfn = [&statuscb, &status, &max_tries] {
|
||||
// report status
|
||||
statuscb(++status * 100.0/max_tries);
|
||||
};
|
||||
|
||||
auto stopcond = [&statuscb] {
|
||||
return ! statuscb(-1);
|
||||
};
|
||||
RotfinderBoilerplate<1000> bp{mo, params};
|
||||
|
||||
// Preparing the optimizer.
|
||||
size_t gridsize = std::sqrt(max_tries);
|
||||
opt::Optimizer<opt::AlgBruteForce> solver(opt::StopCriteria{}
|
||||
.max_iterations(max_tries)
|
||||
.stop_condition(stopcond),
|
||||
gridsize);
|
||||
size_t gridsize = std::sqrt(bp.max_tries);
|
||||
opt::Optimizer<opt::AlgBruteForce> solver(
|
||||
opt::StopCriteria{}.max_iterations(bp.max_tries)
|
||||
.stop_condition([&bp] { return bp.stopcond(); }),
|
||||
gridsize
|
||||
);
|
||||
|
||||
// We are searching rotations around only two axes x, y. Thus the
|
||||
// problem becomes a 2 dimensional optimization task.
|
||||
|
@ -348,48 +345,19 @@ Vec2d find_best_misalignment_rotation(const ModelObject & mo,
|
|||
auto bounds = opt::bounds({ {-PI, PI}, {-PI, PI} });
|
||||
|
||||
auto result = solver.to_max().optimize(
|
||||
[&mesh, &statusfn] (const XYRotation &rot)
|
||||
[&bp] (const XYRotation &rot)
|
||||
{
|
||||
statusfn();
|
||||
return get_misalginment_score(mesh, to_transform3f(rot));
|
||||
bp.statusfn();
|
||||
return get_misalginment_score(bp.mesh, to_transform3f(rot));
|
||||
}, opt::initvals({0., 0.}), bounds);
|
||||
|
||||
rot = result.optimum;
|
||||
|
||||
return {rot[0], rot[1]};
|
||||
return {result.optimum[0], result.optimum[1]};
|
||||
}
|
||||
|
||||
Vec2d find_least_supports_rotation(const ModelObject & mo,
|
||||
const RotOptimizeParams ¶ms)
|
||||
{
|
||||
static const unsigned MAX_TRIES = 1000;
|
||||
|
||||
// return value
|
||||
XYRotation rot;
|
||||
|
||||
// We will use only one instance of this converted mesh to examine different
|
||||
// rotations
|
||||
TriangleMesh mesh = get_mesh_to_rotate(mo);
|
||||
|
||||
// To keep track of the number of iterations
|
||||
unsigned status = 0;
|
||||
|
||||
// The maximum number of iterations
|
||||
auto max_tries = unsigned(params.accuracy() * MAX_TRIES);
|
||||
|
||||
auto &statuscb = params.statuscb();
|
||||
|
||||
// call status callback with zero, because we are at the start
|
||||
statuscb(status);
|
||||
|
||||
auto statusfn = [&statuscb, &status, &max_tries] {
|
||||
// report status
|
||||
statuscb(unsigned(++status * 100.0/max_tries) );
|
||||
};
|
||||
|
||||
auto stopcond = [&statuscb] {
|
||||
return ! statuscb(-1);
|
||||
};
|
||||
RotfinderBoilerplate<1000> bp{mo, params};
|
||||
|
||||
SLAPrintObjectConfig pocfg;
|
||||
if (params.print_config())
|
||||
|
@ -397,31 +365,35 @@ Vec2d find_least_supports_rotation(const ModelObject & mo,
|
|||
|
||||
pocfg.apply(mo.config.get());
|
||||
|
||||
XYRotation rot;
|
||||
|
||||
// Different search methods have to be used depending on the model elevation
|
||||
if (is_on_floor(pocfg)) {
|
||||
|
||||
std::vector<XYRotation> inputs = get_chull_rotations(mesh, max_tries);
|
||||
max_tries = inputs.size();
|
||||
std::vector<XYRotation> inputs = get_chull_rotations(bp.mesh, bp.max_tries);
|
||||
bp.max_tries = inputs.size();
|
||||
|
||||
// If the model can be placed on the bed directly, we only need to
|
||||
// check the 3D convex hull face rotations.
|
||||
|
||||
auto objfn = [&mesh, &statusfn](const XYRotation &rot) {
|
||||
statusfn();
|
||||
auto objfn = [&bp](const XYRotation &rot) {
|
||||
bp.statusfn();
|
||||
Transform3f tr = to_transform3f(rot);
|
||||
return get_supportedness_onfloor_score(mesh, tr);
|
||||
return get_supportedness_onfloor_score(bp.mesh, tr);
|
||||
};
|
||||
|
||||
rot = find_min_score<2>(objfn, inputs.begin(), inputs.end(), stopcond);
|
||||
rot = find_min_score<2>(objfn, inputs.begin(), inputs.end(), [&bp] {
|
||||
return bp.stopcond();
|
||||
});
|
||||
|
||||
} else {
|
||||
|
||||
// Preparing the optimizer.
|
||||
size_t gridsize = std::sqrt(max_tries); // 2D grid has gridsize^2 calls
|
||||
opt::Optimizer<opt::AlgBruteForce> solver(opt::StopCriteria{}
|
||||
.max_iterations(max_tries)
|
||||
.stop_condition(stopcond),
|
||||
gridsize);
|
||||
size_t gridsize = std::sqrt(bp.max_tries); // 2D grid has gridsize^2 calls
|
||||
opt::Optimizer<opt::AlgBruteForce> solver(
|
||||
opt::StopCriteria{}.max_iterations(bp.max_tries)
|
||||
.stop_condition([&bp] { return bp.stopcond(); }),
|
||||
gridsize
|
||||
);
|
||||
|
||||
// We are searching rotations around only two axes x, y. Thus the
|
||||
// problem becomes a 2 dimensional optimization task.
|
||||
|
@ -429,10 +401,10 @@ Vec2d find_least_supports_rotation(const ModelObject & mo,
|
|||
auto bounds = opt::bounds({ {-PI, PI}, {-PI, PI} });
|
||||
|
||||
auto result = solver.to_min().optimize(
|
||||
[&mesh, &statusfn] (const XYRotation &rot)
|
||||
[&bp] (const XYRotation &rot)
|
||||
{
|
||||
statusfn();
|
||||
return get_supportedness_score(mesh, to_transform3f(rot));
|
||||
bp.statusfn();
|
||||
return get_supportedness_score(bp.mesh, to_transform3f(rot));
|
||||
}, opt::initvals({0., 0.}), bounds);
|
||||
|
||||
// Save the result
|
||||
|
@ -442,4 +414,66 @@ Vec2d find_least_supports_rotation(const ModelObject & mo,
|
|||
return {rot[0], rot[1]};
|
||||
}
|
||||
|
||||
inline BoundingBoxf3 bounding_box_with_tr(const indexed_triangle_set &its,
|
||||
const Transform3f &tr)
|
||||
{
|
||||
if (its.vertices.empty())
|
||||
return {};
|
||||
|
||||
Vec3f bmin = tr * its.vertices.front(), bmax = tr * its.vertices.front();
|
||||
|
||||
for (const Vec3f &p : its.vertices) {
|
||||
Vec3f pp = tr * p;
|
||||
bmin = pp.cwiseMin(bmin);
|
||||
bmax = pp.cwiseMax(bmax);
|
||||
}
|
||||
|
||||
return {bmin.cast<double>(), bmax.cast<double>()};
|
||||
}
|
||||
|
||||
Vec2d find_min_z_height_rotation(const ModelObject &mo,
|
||||
const RotOptimizeParams ¶ms)
|
||||
{
|
||||
RotfinderBoilerplate<1000> bp{mo, params};
|
||||
|
||||
TriangleMesh chull = bp.mesh.convex_hull_3d();
|
||||
chull.require_shared_vertices();
|
||||
auto inputs = reserve_vector<XYRotation>(chull.its.indices.size());
|
||||
auto rotcmp = [](const XYRotation &r1, const XYRotation &r2) {
|
||||
double xdiff = r1[X] - r2[X], ydiff = r1[Y] - r2[Y];
|
||||
return std::abs(xdiff) < EPSILON ? ydiff < 0. : xdiff < 0.;
|
||||
};
|
||||
auto eqcmp = [](const XYRotation &r1, const XYRotation &r2) {
|
||||
double xdiff = r1[X] - r2[X], ydiff = r1[Y] - r2[Y];
|
||||
return std::abs(xdiff) < EPSILON && std::abs(ydiff) < EPSILON;
|
||||
};
|
||||
|
||||
for (size_t fi = 0; fi < chull.its.indices.size(); ++fi) {
|
||||
Facestats fc{get_triangle_vertices(chull, fi)};
|
||||
|
||||
auto q = Eigen::Quaternionf{}.FromTwoVectors(fc.normal, DOWN);
|
||||
XYRotation rot = from_transform3f(Transform3f::Identity() * q);
|
||||
|
||||
auto it = std::lower_bound(inputs.begin(), inputs.end(), rot, rotcmp);
|
||||
|
||||
if (it == inputs.end() || !eqcmp(*it, rot))
|
||||
inputs.insert(it, rot);
|
||||
}
|
||||
|
||||
inputs.shrink_to_fit();
|
||||
bp.max_tries = inputs.size();
|
||||
|
||||
auto objfn = [&bp, &chull](const XYRotation &rot) {
|
||||
bp.statusfn();
|
||||
Transform3f tr = to_transform3f(rot);
|
||||
return bounding_box_with_tr(chull.its, tr).size().z();
|
||||
};
|
||||
|
||||
XYRotation rot = find_min_score<2>(objfn, inputs.begin(), inputs.end(), [&bp] {
|
||||
return bp.stopcond();
|
||||
});
|
||||
|
||||
return {rot[0], rot[1]};
|
||||
}
|
||||
|
||||
}} // namespace Slic3r::sla
|
||||
|
|
|
@ -63,7 +63,8 @@ Vec2d find_best_misalignment_rotation(const ModelObject &modelobj,
|
|||
Vec2d find_least_supports_rotation(const ModelObject &modelobj,
|
||||
const RotOptimizeParams & = {});
|
||||
|
||||
double find_Z_fit_to_bed_rotation(const ModelObject &mo, const BoundingBox &bed);
|
||||
Vec2d find_min_z_height_rotation(const ModelObject &mo,
|
||||
const RotOptimizeParams ¶ms = {});
|
||||
|
||||
} // namespace sla
|
||||
} // namespace Slic3r
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include <boost/container/small_vector.hpp>
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define EXPENSIVE_DEBUG_CHECKS
|
||||
// #define EXPENSIVE_DEBUG_CHECKS
|
||||
#endif // NDEBUG
|
||||
|
||||
namespace Slic3r {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue