mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-02 13:20:59 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
19b8d2138e |
Vendored
+1
-11
@@ -151,12 +151,6 @@ elseif(APPLE)
|
|||||||
# the post-install -add_rpath below.
|
# the post-install -add_rpath below.
|
||||||
set(_python_ldflags "${_python_arch_flags} -Wl,-headerpad_max_install_names")
|
set(_python_ldflags "${_python_arch_flags} -Wl,-headerpad_max_install_names")
|
||||||
|
|
||||||
# The macOS 27 SDK declares pipe2() and dup3() as available from macOS 27, so
|
|
||||||
# configure finds them and CPython 3.12 calls them without a runtime check.
|
|
||||||
# Below a macOS 27 deployment target they are weak-linked and resolve to NULL
|
|
||||||
# on older systems, where os.pipe() then segfaults -- in `make install`
|
|
||||||
# (compileall, ensurepip) and in the shipped app alike. Every configure below
|
|
||||||
# keeps the pipe()/dup2() fallbacks (python/cpython#153711).
|
|
||||||
if(IS_CROSS_COMPILE)
|
if(IS_CROSS_COMPILE)
|
||||||
set(_python_build_tgt --build=${_python_build_arch}-apple-darwin --host=${_python_host_arch}-apple-darwin)
|
set(_python_build_tgt --build=${_python_build_arch}-apple-darwin --host=${_python_host_arch}-apple-darwin)
|
||||||
set(_python_build_arch_flags "-arch ${_python_build_arch_flag} -mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}")
|
set(_python_build_arch_flags "-arch ${_python_build_arch_flag} -mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}")
|
||||||
@@ -180,8 +174,7 @@ elseif(APPLE)
|
|||||||
--enable-shared \
|
--enable-shared \
|
||||||
--without-static-libpython \
|
--without-static-libpython \
|
||||||
--disable-test-modules \
|
--disable-test-modules \
|
||||||
--build=${_python_build_arch}-apple-darwin \
|
--build=${_python_build_arch}-apple-darwin && \
|
||||||
ac_cv_func_pipe2=no ac_cv_func_dup3=no && \
|
|
||||||
make -j${NPROC} python && \
|
make -j${NPROC} python && \
|
||||||
cd '<SOURCE_DIR>' && \
|
cd '<SOURCE_DIR>' && \
|
||||||
env \
|
env \
|
||||||
@@ -198,7 +191,6 @@ elseif(APPLE)
|
|||||||
--without-static-libpython \
|
--without-static-libpython \
|
||||||
--with-openssl='${DESTDIR}' \
|
--with-openssl='${DESTDIR}' \
|
||||||
--disable-test-modules \
|
--disable-test-modules \
|
||||||
ac_cv_func_pipe2=no ac_cv_func_dup3=no \
|
|
||||||
${_python_build_tgt} \
|
${_python_build_tgt} \
|
||||||
--with-build-python='${_python_build_python}' \
|
--with-build-python='${_python_build_python}' \
|
||||||
py_cv_module__tkinter=n/a"
|
py_cv_module__tkinter=n/a"
|
||||||
@@ -221,8 +213,6 @@ elseif(APPLE)
|
|||||||
--with-openssl=${DESTDIR}
|
--with-openssl=${DESTDIR}
|
||||||
--disable-test-modules
|
--disable-test-modules
|
||||||
${_python_build_tgt}
|
${_python_build_tgt}
|
||||||
ac_cv_func_pipe2=no
|
|
||||||
ac_cv_func_dup3=no
|
|
||||||
# Tcl/Tk 9.0 (e.g. from Homebrew) is incompatible with CPython 3.12's
|
# Tcl/Tk 9.0 (e.g. from Homebrew) is incompatible with CPython 3.12's
|
||||||
# _tkinter; OrcaSlicer's embedded Python does not need tkinter anyway.
|
# _tkinter; OrcaSlicer's embedded Python does not need tkinter anyway.
|
||||||
py_cv_module__tkinter=n/a
|
py_cv_module__tkinter=n/a
|
||||||
|
|||||||
@@ -873,10 +873,10 @@ int ConfigBase::load_from_json(const std::string &file, ConfigSubstitutionContex
|
|||||||
|
|
||||||
CNumericLocalesSetter locales_setter;
|
CNumericLocalesSetter locales_setter;
|
||||||
|
|
||||||
std::function<bool(const json&, const char,const char,const bool,std::string&)> parse_str_arr = [&parse_str_arr](const json& arr, const char single_sep,const char array_sep,const bool escape_string_style,std::string& value_str)->bool {
|
std::function<bool(const json::const_iterator&, const char,const char,const bool,std::string&)> parse_str_arr = [&parse_str_arr](const json::const_iterator& it, const char single_sep,const char array_sep,const bool escape_string_style,std::string& value_str)->bool {
|
||||||
// must have consistent type name
|
// must have consistent type name
|
||||||
std::string consistent_type;
|
std::string consistent_type;
|
||||||
for (auto iter = arr.begin(); iter != arr.end(); ++iter) {
|
for (auto iter = it.value().begin(); iter != it.value().end(); ++iter) {
|
||||||
if (consistent_type.empty())
|
if (consistent_type.empty())
|
||||||
consistent_type = iter.value().type_name();
|
consistent_type = iter.value().type_name();
|
||||||
else {
|
else {
|
||||||
@@ -886,13 +886,13 @@ int ConfigBase::load_from_json(const std::string &file, ConfigSubstitutionContex
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool first = true;
|
bool first = true;
|
||||||
for (auto iter = arr.begin(); iter != arr.end(); iter++) {
|
for (auto iter = it.value().begin(); iter != it.value().end(); iter++) {
|
||||||
if (iter.value().is_array()) {
|
if (iter.value().is_array()) {
|
||||||
if (!first)
|
if (!first)
|
||||||
value_str += array_sep;
|
value_str += array_sep;
|
||||||
else
|
else
|
||||||
first = false;
|
first = false;
|
||||||
bool success = parse_str_arr(iter.value(), single_sep, array_sep,escape_string_style, value_str);
|
bool success = parse_str_arr(iter, single_sep, array_sep,escape_string_style, value_str);
|
||||||
if (!success)
|
if (!success)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1038,39 +1038,8 @@ int ConfigBase::load_from_json(const std::string &file, ConfigSubstitutionContex
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// BambuStudio and its forks save a nozzle variant that matches the parent preset as "nil".
|
|
||||||
// An option that can't hold nil gets its default in that slot, and the slot is reported so
|
|
||||||
// the merge onto the parent keeps the parent's value. All slots nil means the key is not set.
|
|
||||||
const json *values = &it.value();
|
|
||||||
json values_with_defaults;
|
|
||||||
if (substitution_context.accept_nil && optdef && !optdef->nullable && optdef->default_value &&
|
|
||||||
(optdef->type == coFloats || optdef->type == coPercents || optdef->type == coFloatsOrPercents ||
|
|
||||||
optdef->type == coInts || optdef->type == coEnums || optdef->type == coBools)) {
|
|
||||||
auto is_nil = [](const json &v) { return v.is_string() && v.get<std::string>() == "nil"; };
|
|
||||||
std::vector<size_t> nil_slots;
|
|
||||||
for (size_t i = 0; i < values->size(); ++i)
|
|
||||||
if (is_nil((*values)[i]))
|
|
||||||
nil_slots.push_back(i);
|
|
||||||
if (!nil_slots.empty()) {
|
|
||||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ": " << file << ": " << it.key() << " is nil in "
|
|
||||||
<< nil_slots.size() << " of " << values->size()
|
|
||||||
<< " slots, read as not set (the parent preset's value, or the default)";
|
|
||||||
if (nil_slots.size() == values->size())
|
|
||||||
continue;
|
|
||||||
// create_default_option() gives enums their names, which vserialize() needs.
|
|
||||||
std::unique_ptr<ConfigOption> default_option(optdef->create_default_option());
|
|
||||||
const std::vector<std::string> defaults = static_cast<const ConfigOptionVectorBase*>(default_option.get())->vserialize();
|
|
||||||
values_with_defaults = *values;
|
|
||||||
const json first_value = *std::find_if_not(values->begin(), values->end(), is_nil);
|
|
||||||
for (size_t i : nil_slots)
|
|
||||||
values_with_defaults[i] = defaults.empty() ? first_value : json(defaults[i % defaults.size()]);
|
|
||||||
values = &values_with_defaults;
|
|
||||||
substitution_context.nil_slots[opt_key] = std::move(nil_slots);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// BBS: we only support 2 depth array
|
// BBS: we only support 2 depth array
|
||||||
valid = parse_str_arr(*values, single_sep, array_sep,escape_string_type, value_str);
|
valid = parse_str_arr(it, single_sep, array_sep,escape_string_type, value_str);
|
||||||
if (!valid) {
|
if (!valid) {
|
||||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": parse " << file << " error, invalid json array for " << it.key();
|
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": parse " << file << " error, invalid json array for " << it.key();
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -267,11 +267,6 @@ struct ConfigSubstitutionContext
|
|||||||
ForwardCompatibilitySubstitutionRule rule;
|
ForwardCompatibilitySubstitutionRule rule;
|
||||||
ConfigSubstitutions substitutions;
|
ConfigSubstitutions substitutions;
|
||||||
std::vector<std::string> unrecogized_keys;
|
std::vector<std::string> unrecogized_keys;
|
||||||
// Read "nil" in an option that can't hold it as not set instead of failing. Set by callers that hand
|
|
||||||
// nil_slots to the merge onto the parent preset, or for which the option default is the right fallback.
|
|
||||||
bool accept_nil = false;
|
|
||||||
// Slots of options that can't hold nil but were "nil" in the file; they hold the option default.
|
|
||||||
std::map<std::string, std::vector<size_t>> nil_slots;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// A generic value of a configuration option.
|
// A generic value of a configuration option.
|
||||||
|
|||||||
@@ -2696,8 +2696,6 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
|||||||
}
|
}
|
||||||
std::map<std::string, std::string> key_values;
|
std::map<std::string, std::string> key_values;
|
||||||
std::string reason;
|
std::string reason;
|
||||||
// No parent preset here: a nil slot keeps the option default.
|
|
||||||
config_substitutions.accept_nil = true;
|
|
||||||
int ret = config.load_from_json(dest_file, config_substitutions, true, key_values, reason);
|
int ret = config.load_from_json(dest_file, config_substitutions, true, key_values, reason);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
add_error("Error load config from json:"+reason);
|
add_error("Error load config from json:"+reason);
|
||||||
@@ -2738,13 +2736,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
|||||||
//ConfigSubstitutions config_substitutions = config.load_from_ini(dest_file, Enable);
|
//ConfigSubstitutions config_substitutions = config.load_from_ini(dest_file, Enable);
|
||||||
std::map<std::string, std::string> key_values;
|
std::map<std::string, std::string> key_values;
|
||||||
std::string reason;
|
std::string reason;
|
||||||
ConfigSubstitutionContext load_context(Enable);
|
ConfigSubstitutions config_substitutions = use_json? config.load_from_json(dest_file, Enable, key_values, reason) : config.load_from_ini(dest_file, Enable);
|
||||||
load_context.accept_nil = true;
|
|
||||||
if (use_json)
|
|
||||||
config.load_from_json(dest_file, load_context, true, key_values, reason);
|
|
||||||
else
|
|
||||||
load_context.substitutions = config.load_from_ini(dest_file, Enable);
|
|
||||||
ConfigSubstitutions config_substitutions = std::move(load_context.substitutions);
|
|
||||||
if (!reason.empty()) {
|
if (!reason.empty()) {
|
||||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(", load project embedded config from %1% failed\n") % dest_file;
|
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(", load project embedded config from %1% failed\n") % dest_file;
|
||||||
//skip this file
|
//skip this file
|
||||||
@@ -2793,7 +2785,6 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
|||||||
preset->is_project_embedded = true;
|
preset->is_project_embedded = true;
|
||||||
preset->is_external = true;
|
preset->is_external = true;
|
||||||
preset->is_dirty = false;
|
preset->is_dirty = false;
|
||||||
preset->nil_slots = std::move(load_context.nil_slots);
|
|
||||||
|
|
||||||
std::string version_str = key_values[BBL_JSON_KEY_VERSION];
|
std::string version_str = key_values[BBL_JSON_KEY_VERSION];
|
||||||
boost::optional<Semver> version = Semver::parse(version_str);
|
boost::optional<Semver> version = Semver::parse(version_str);
|
||||||
|
|||||||
@@ -1726,11 +1726,9 @@ PresetCollection::UserPresetLoad PresetCollection::resolve_user_preset(
|
|||||||
//ConfigSubstitutions config_substitutions = config.load_from_ini(preset.file, substitution_rule);
|
//ConfigSubstitutions config_substitutions = config.load_from_ini(preset.file, substitution_rule);
|
||||||
std::map<std::string, std::string> key_values;
|
std::map<std::string, std::string> key_values;
|
||||||
std::string reason;
|
std::string reason;
|
||||||
ConfigSubstitutionContext load_context(substitution_rule);
|
ConfigSubstitutions config_substitutions = config.load_from_json(preset.file, substitution_rule, key_values, reason);
|
||||||
load_context.accept_nil = true;
|
if (! config_substitutions.empty())
|
||||||
config.load_from_json(preset.file, load_context, true, key_values, reason);
|
out.substitutions.push_back({ preset.name, m_type, PresetConfigSubstitutions::Source::UserFile, preset.file, std::move(config_substitutions) });
|
||||||
if (! load_context.substitutions.empty())
|
|
||||||
out.substitutions.push_back({ preset.name, m_type, PresetConfigSubstitutions::Source::UserFile, preset.file, std::move(load_context.substitutions) });
|
|
||||||
if (!reason.empty()) {
|
if (!reason.empty()) {
|
||||||
out.discard_file = true;
|
out.discard_file = true;
|
||||||
out.errors.push_back((boost::format("parse config %1% failed") % preset.file).str());
|
out.errors.push_back((boost::format("parse config %1% failed") % preset.file).str());
|
||||||
@@ -1766,7 +1764,7 @@ PresetCollection::UserPresetLoad PresetCollection::resolve_user_preset(
|
|||||||
preset.config = inherit_preset->config;
|
preset.config = inherit_preset->config;
|
||||||
preset.filament_id = inherit_preset->filament_id;
|
preset.filament_id = inherit_preset->filament_id;
|
||||||
extend_default_config_length(config, false, {});
|
extend_default_config_length(config, false, {});
|
||||||
preset.config.update_diff_values_to_child_config(config, extruder_id_name, extruder_variant_name, *key_set1, *key_set2, &load_context.nil_slots);
|
preset.config.update_diff_values_to_child_config(config, extruder_id_name, extruder_variant_name, *key_set1, *key_set2);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
auto inherits_config2 = dynamic_cast<ConfigOptionString *>(inherits_config);
|
auto inherits_config2 = dynamic_cast<ConfigOptionString *>(inherits_config);
|
||||||
@@ -2139,8 +2137,7 @@ void PresetCollection::load_project_embedded_presets(std::vector<Preset*>& proje
|
|||||||
BOOST_LOG_TRIVIAL(error) << boost::format("can not find parent for config %1%!")%preset->file;
|
BOOST_LOG_TRIVIAL(error) << boost::format("can not find parent for config %1%!")%preset->file;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
preset->config.update_diff_values_to_child_config(config, extruder_id_name, extruder_variant_name, *key_set1, *key_set2, &preset->nil_slots);
|
preset->config.update_diff_values_to_child_config(config, extruder_id_name, extruder_variant_name, *key_set1, *key_set2);
|
||||||
preset->nil_slots.clear();
|
|
||||||
//preset->config.apply(std::move(config));
|
//preset->config.apply(std::move(config));
|
||||||
Preset::normalize(preset->config);
|
Preset::normalize(preset->config);
|
||||||
// Report configuration fields, which are misplaced into a wrong group.
|
// Report configuration fields, which are misplaced into a wrong group.
|
||||||
|
|||||||
@@ -269,8 +269,6 @@ public:
|
|||||||
//BBS: add type for project-embedded
|
//BBS: add type for project-embedded
|
||||||
bool is_project_embedded = false;
|
bool is_project_embedded = false;
|
||||||
ConfigSubstitutions *loading_substitutions{nullptr};
|
ConfigSubstitutions *loading_substitutions{nullptr};
|
||||||
// Slots that were nil in the embedded preset's file, see ConfigSubstitutionContext::nil_slots.
|
|
||||||
std::map<std::string, std::vector<size_t>> nil_slots;
|
|
||||||
bool is_user() const { return ! this->is_default && ! this->is_system && ! this->is_project_embedded && ! this->is_from_bundle(); }
|
bool is_user() const { return ! this->is_default && ! this->is_system && ! this->is_project_embedded && ! this->is_from_bundle(); }
|
||||||
bool can_overwrite() const { return ! this->is_default && ! this->is_system && ! this->is_from_bundle(); }
|
bool can_overwrite() const { return ! this->is_default && ! this->is_system && ! this->is_from_bundle(); }
|
||||||
//bool is_user() const { return ! this->is_default && ! this->is_system; }
|
//bool is_user() const { return ! this->is_default && ! this->is_system; }
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ void PrintTryCancel::operator()()
|
|||||||
m_print->throw_if_canceled();
|
m_print->throw_if_canceled();
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t PrintStateBase::g_last_timestamp = 0;
|
std::atomic<size_t> PrintStateBase::g_last_timestamp{0};
|
||||||
|
|
||||||
// Update "scale", "input_filename", "input_filename_base", "first_object_name" placeholders from the current m_objects.
|
// Update "scale", "input_filename", "input_filename_base", "first_object_name" placeholders from the current m_objects.
|
||||||
void PrintBase::update_object_placeholders(DynamicConfig &config, const std::string &default_ext) const
|
void PrintBase::update_object_placeholders(DynamicConfig &config, const std::string &default_ext) const
|
||||||
@@ -107,11 +107,26 @@ std::string PrintBase::output_filepath(const std::string &path, const std::strin
|
|||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PrintBase::set_status_callback(status_callback_type cb)
|
||||||
|
{
|
||||||
|
std::scoped_lock<std::mutex> lock(m_status_callback_mutex);
|
||||||
|
m_status_callback = std::move(cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a copy, so that the callback is invoked with m_status_callback_mutex released: the callback
|
||||||
|
// may block on the UI thread, which in turn may be assigning a new callback.
|
||||||
|
PrintBase::status_callback_type PrintBase::status_callback() const
|
||||||
|
{
|
||||||
|
std::scoped_lock<std::mutex> lock(m_status_callback_mutex);
|
||||||
|
return m_status_callback;
|
||||||
|
}
|
||||||
|
|
||||||
//BBS: move set_status from hpp to cpp
|
//BBS: move set_status from hpp to cpp
|
||||||
void PrintBase::set_status(int percent, const std::string &message, unsigned int flags, int warning_step) const
|
void PrintBase::set_status(int percent, const std::string &message, unsigned int flags, int warning_step) const
|
||||||
{
|
{
|
||||||
if (m_status_callback)
|
status_callback_type status_callback = this->status_callback();
|
||||||
m_status_callback(SlicingStatus(percent, message, flags, warning_step));
|
if (status_callback)
|
||||||
|
status_callback(SlicingStatus(percent, message, flags, warning_step));
|
||||||
else
|
else
|
||||||
BOOST_LOG_TRIVIAL(debug) <<boost::format("Percent %1%: %2%\n")%percent %message.c_str();
|
BOOST_LOG_TRIVIAL(debug) <<boost::format("Percent %1%: %2%\n")%percent %message.c_str();
|
||||||
}
|
}
|
||||||
@@ -119,9 +134,10 @@ void PrintBase::set_status(int percent, const std::string &message, unsigned in
|
|||||||
void PrintBase::status_update_warnings(int step, PrintStateBase::WarningLevel warning_level,
|
void PrintBase::status_update_warnings(int step, PrintStateBase::WarningLevel warning_level,
|
||||||
const std::string &message, const PrintObjectBase* print_object, PrintStateBase::SlicingNotificationType message_id)
|
const std::string &message, const PrintObjectBase* print_object, PrintStateBase::SlicingNotificationType message_id)
|
||||||
{
|
{
|
||||||
if (this->m_status_callback) {
|
status_callback_type status_callback = this->status_callback();
|
||||||
|
if (status_callback) {
|
||||||
auto status = print_object ? SlicingStatus(*print_object, step, message, message_id, warning_level) : SlicingStatus(*this, step, message, message_id, warning_level);
|
auto status = print_object ? SlicingStatus(*print_object, step, message, message_id, warning_level) : SlicingStatus(*this, step, message, message_id, warning_level);
|
||||||
m_status_callback(status);
|
status_callback(status);
|
||||||
}
|
}
|
||||||
else if (! message.empty())
|
else if (! message.empty())
|
||||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", Print warning: %1%\n")% message.c_str();
|
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", Print warning: %1%\n")% message.c_str();
|
||||||
@@ -132,8 +148,9 @@ void PrintBase::status_update_warnings(int step, PrintStateBase::WarningLevel wa
|
|||||||
const std::string& message, PrintObjectBase &object, PrintStateBase::SlicingNotificationType message_id)
|
const std::string& message, PrintObjectBase &object, PrintStateBase::SlicingNotificationType message_id)
|
||||||
{
|
{
|
||||||
//BBS: add object it into slicing status
|
//BBS: add object it into slicing status
|
||||||
if (this->m_status_callback) {
|
status_callback_type status_callback = this->status_callback();
|
||||||
m_status_callback(SlicingStatus(object, step, message, message_id, warning_level));
|
if (status_callback) {
|
||||||
|
status_callback(SlicingStatus(object, step, message, message_id, warning_level));
|
||||||
}
|
}
|
||||||
else if (!message.empty())
|
else if (!message.empty())
|
||||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", PrintObject warning: %1%\n")% message.c_str();
|
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", PrintObject warning: %1%\n")% message.c_str();
|
||||||
|
|||||||
@@ -99,10 +99,9 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
//FIXME last timestamp is shared between Print & SLAPrint,
|
// The last timestamp is shared between all the Print & SLAPrint instances, and Orca keeps one Print
|
||||||
// and if multiple Print or SLAPrint instances are executed in parallel, modification of g_last_timestamp
|
// per PartPlate, so it is incremented under different state mutexes: it has to be atomic.
|
||||||
// is not synchronized!
|
static std::atomic<size_t> g_last_timestamp;
|
||||||
static size_t g_last_timestamp;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// To be instantiated over PrintStep or PrintObjectStep enums.
|
// To be instantiated over PrintStep or PrintObjectStep enums.
|
||||||
@@ -473,11 +472,12 @@ public:
|
|||||||
};
|
};
|
||||||
typedef std::function<void(const SlicingStatus&)> status_callback_type;
|
typedef std::function<void(const SlicingStatus&)> status_callback_type;
|
||||||
// Default status console print out in the form of percent => message.
|
// Default status console print out in the form of percent => message.
|
||||||
void set_status_default() { m_status_callback = nullptr; }
|
void set_status_default() { this->set_status_callback(nullptr); }
|
||||||
// No status output or callback whatsoever, useful mostly for automatic tests.
|
// No status output or callback whatsoever, useful mostly for automatic tests.
|
||||||
void set_status_silent() { m_status_callback = [](const SlicingStatus&){}; }
|
void set_status_silent() { this->set_status_callback([](const SlicingStatus&){}); }
|
||||||
// Register a custom status callback.
|
// Register a custom status callback. Called from the UI thread while the worker thread may be
|
||||||
void set_status_callback(status_callback_type cb) { m_status_callback = cb; }
|
// invoking the previous callback, therefore guarded by m_status_callback_mutex.
|
||||||
|
void set_status_callback(status_callback_type cb);
|
||||||
// Calls a registered callback to update the status, or print out the default message.
|
// Calls a registered callback to update the status, or print out the default message.
|
||||||
void set_status(int percent, const std::string &message, unsigned int flags = SlicingStatus::DEFAULT, int warning_step = -1) const;
|
void set_status(int percent, const std::string &message, unsigned int flags = SlicingStatus::DEFAULT, int warning_step = -1) const;
|
||||||
|
|
||||||
@@ -563,7 +563,10 @@ protected:
|
|||||||
std::string m_plate_name;
|
std::string m_plate_name;
|
||||||
|
|
||||||
// Callback to be evoked regularly to update state of the UI thread.
|
// Callback to be evoked regularly to update state of the UI thread.
|
||||||
|
// Guarded by m_status_callback_mutex, always invoke the copy returned by status_callback().
|
||||||
status_callback_type m_status_callback;
|
status_callback_type m_status_callback;
|
||||||
|
mutable std::mutex m_status_callback_mutex;
|
||||||
|
status_callback_type status_callback() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::atomic<CancelStatus> m_cancel_status;
|
std::atomic<CancelStatus> m_cancel_status;
|
||||||
|
|||||||
@@ -10,8 +10,6 @@
|
|||||||
#include "GCode/Thumbnails.hpp"
|
#include "GCode/Thumbnails.hpp"
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <boost/algorithm/string/case_conv.hpp>
|
#include <boost/algorithm/string/case_conv.hpp>
|
||||||
#include <boost/algorithm/string/classification.hpp>
|
|
||||||
#include <boost/algorithm/string/join.hpp>
|
|
||||||
#include <boost/algorithm/string/replace.hpp>
|
#include <boost/algorithm/string/replace.hpp>
|
||||||
#include <boost/algorithm/string/split.hpp>
|
#include <boost/algorithm/string/split.hpp>
|
||||||
#include <boost/algorithm/string/trim.hpp>
|
#include <boost/algorithm/string/trim.hpp>
|
||||||
@@ -9187,18 +9185,16 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
|
|||||||
} else if (opt_key == "tree_support_wall_count" && value == "-1") {
|
} else if (opt_key == "tree_support_wall_count" && value == "-1") {
|
||||||
value = "0";
|
value = "0";
|
||||||
} else if (opt_key == "different_settings_to_system") {
|
} else if (opt_key == "different_settings_to_system") {
|
||||||
// Rename whole entries: a substring rename would also hit longer keys that contain the old name.
|
std::string copy_value = value;
|
||||||
std::vector<std::string> entries;
|
copy_value.erase(std::remove(copy_value.begin(), copy_value.end(), '\"'), copy_value.end()); // remove '"' in string
|
||||||
boost::split(entries, value, boost::is_any_of(";"));
|
std::set<std::string> split_keys = SplitStringAndRemoveDuplicateElement(copy_value, ";");
|
||||||
for (std::string &entry : entries) {
|
for (std::string split_key : split_keys) {
|
||||||
std::string key = entry, unused_value;
|
std::string copy_key = split_key, copy_value = "";
|
||||||
key.erase(std::remove(key.begin(), key.end(), '\"'), key.end());
|
handle_legacy(copy_key, copy_value);
|
||||||
std::string new_key = key;
|
if (copy_key != split_key) {
|
||||||
handle_legacy(new_key, unused_value);
|
ReplaceString(value, split_key, copy_key);
|
||||||
if (new_key != key)
|
}
|
||||||
ReplaceString(entry, key, new_key);
|
|
||||||
}
|
}
|
||||||
value = boost::algorithm::join(entries, ";");
|
|
||||||
} else if (opt_key == "overhang_fan_threshold" && value == "5%") {
|
} else if (opt_key == "overhang_fan_threshold" && value == "5%") {
|
||||||
value = "10%";
|
value = "10%";
|
||||||
} else if( opt_key == "wall_infill_order" ) {
|
} else if( opt_key == "wall_infill_order" ) {
|
||||||
@@ -9240,13 +9236,6 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
|
|||||||
else if (value == "0"){
|
else if (value == "0"){
|
||||||
value = "ensure_moderate";
|
value = "ensure_moderate";
|
||||||
}
|
}
|
||||||
// BambuStudio's levels
|
|
||||||
else if (value == "enabled")
|
|
||||||
value = "ensure_all";
|
|
||||||
else if (value == "partial")
|
|
||||||
value = "ensure_moderate";
|
|
||||||
else if (value == "disabled")
|
|
||||||
value = "none";
|
|
||||||
} else if (opt_key == "rotate_solid_infill_direction") {
|
} else if (opt_key == "rotate_solid_infill_direction") {
|
||||||
opt_key = "solid_infill_rotate_template";
|
opt_key = "solid_infill_rotate_template";
|
||||||
if (value == "1") {
|
if (value == "1") {
|
||||||
@@ -9262,11 +9251,9 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
|
|||||||
opt_key = "chamber_temperature";
|
opt_key = "chamber_temperature";
|
||||||
} else if (opt_key == "thumbnail_size") {
|
} else if (opt_key == "thumbnail_size") {
|
||||||
opt_key = "thumbnails";
|
opt_key = "thumbnails";
|
||||||
} else if (opt_key == "top_one_wall_type") {
|
} else if (opt_key == "top_one_wall_type" && value != "none") {
|
||||||
// BambuStudio's "topmost" (one wall on the topmost layer only) widens to every top surface.
|
|
||||||
opt_key = "only_one_wall_top";
|
opt_key = "only_one_wall_top";
|
||||||
if (!value.empty())
|
value = "1";
|
||||||
value = (value == "not apply" || value == "none") ? "0" : "1";
|
|
||||||
} else if (opt_key == "initial_layer_flow_ratio") {
|
} else if (opt_key == "initial_layer_flow_ratio") {
|
||||||
opt_key = "bottom_solid_infill_flow_ratio";
|
opt_key = "bottom_solid_infill_flow_ratio";
|
||||||
} else if (opt_key == "ironing_direction") {
|
} else if (opt_key == "ironing_direction") {
|
||||||
@@ -9284,20 +9271,6 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
|
|||||||
opt_key == "ironing_pattern" ||
|
opt_key == "ironing_pattern" ||
|
||||||
opt_key == "support_ironing_pattern") && value == "zig-zag") {
|
opt_key == "support_ironing_pattern") && value == "zig-zag") {
|
||||||
value = "rectilinear";
|
value = "rectilinear";
|
||||||
} else if (opt_key == "sparse_infill_pattern" && value == "2dlattice") {
|
|
||||||
// BambuStudio's name, and OrcaSlicer's before the lateral patterns were renamed
|
|
||||||
value = "lateral-lattice";
|
|
||||||
} else if (opt_key == "sparse_infill_pattern" && value == "2dhoneycomb") {
|
|
||||||
value = "lateral-honeycomb";
|
|
||||||
} else if (opt_key == "sparse_infill_lattice_angle_1" || opt_key == "lattice_angle_1") {
|
|
||||||
opt_key = "lateral_lattice_angle_1";
|
|
||||||
} else if (opt_key == "sparse_infill_lattice_angle_2" || opt_key == "lattice_angle_2") {
|
|
||||||
opt_key = "lateral_lattice_angle_2";
|
|
||||||
} else if (opt_key == "support_style" && value == "tree_organic") {
|
|
||||||
value = "organic";
|
|
||||||
} else if (opt_key == "raft_first_layer_expansion" && !value.empty() && value.front() == '-') {
|
|
||||||
// BambuStudio's auto: 2 mm for normal supports and rafts. Tree branches use tree_support_auto_brim here.
|
|
||||||
value = "2";
|
|
||||||
} else if (opt_key == "filament_map_mode") {
|
} else if (opt_key == "filament_map_mode") {
|
||||||
if (value == "Auto") value = "Auto For Flush";
|
if (value == "Auto") value = "Auto For Flush";
|
||||||
}
|
}
|
||||||
@@ -9326,11 +9299,12 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
|
|||||||
}
|
}
|
||||||
// Orca: Rename wipe tower ribs related options
|
// Orca: Rename wipe tower ribs related options
|
||||||
else if (opt_key == "prime_tower_rib_wall") {
|
else if (opt_key == "prime_tower_rib_wall") {
|
||||||
opt_key = "wipe_tower_wall_type";
|
if (value == "1") {
|
||||||
if (value == "1" || value == "true")
|
opt_key = "wipe_tower_wall_type";
|
||||||
value = "rib";
|
value = "rib";
|
||||||
else if (!value.empty())
|
} else {
|
||||||
value = "rectangle";
|
opt_key = "";
|
||||||
|
}
|
||||||
} else if (opt_key == "prime_tower_extra_rib_length") {
|
} else if (opt_key == "prime_tower_extra_rib_length") {
|
||||||
opt_key = "wipe_tower_extra_rib_length";
|
opt_key = "wipe_tower_extra_rib_length";
|
||||||
} else if (opt_key == "prime_tower_rib_width") {
|
} else if (opt_key == "prime_tower_rib_width") {
|
||||||
@@ -9342,18 +9316,6 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
|
|||||||
} else if (opt_key == "machine_switch_extruder_time") {
|
} else if (opt_key == "machine_switch_extruder_time") {
|
||||||
opt_key = "machine_tool_change_time";
|
opt_key = "machine_tool_change_time";
|
||||||
}
|
}
|
||||||
// BambuStudio names of OrcaSlicer options
|
|
||||||
else if (opt_key == "role_base_wipe_speed") {
|
|
||||||
opt_key = "role_based_wipe_speed";
|
|
||||||
} else if (opt_key == "no_slow_down_for_cooling_on_outwalls") {
|
|
||||||
opt_key = "dont_slow_down_outer_wall";
|
|
||||||
} else if (opt_key == "process_notes") {
|
|
||||||
opt_key = "notes";
|
|
||||||
} else if (opt_key == "prime_tower_max_speed") {
|
|
||||||
opt_key = "wipe_tower_max_purge_speed";
|
|
||||||
} else if (opt_key == "enable_support_ironing") {
|
|
||||||
opt_key = "support_ironing";
|
|
||||||
}
|
|
||||||
else if (opt_key == "wall_direction" && value == "auto") {
|
else if (opt_key == "wall_direction" && value == "auto") {
|
||||||
value = "ccw";
|
value = "ccw";
|
||||||
}
|
}
|
||||||
@@ -11596,8 +11558,7 @@ void DynamicPrintConfig::update_non_diff_values_to_base_config(DynamicPrintConfi
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DynamicPrintConfig::update_diff_values_to_child_config(DynamicPrintConfig& new_config, std::string extruder_id_name, std::string extruder_variant_name, std::set<std::string>& key_set1, std::set<std::string>& key_set2,
|
void DynamicPrintConfig::update_diff_values_to_child_config(DynamicPrintConfig& new_config, std::string extruder_id_name, std::string extruder_variant_name, std::set<std::string>& key_set1, std::set<std::string>& key_set2)
|
||||||
const std::map<std::string, std::vector<size_t>>* nil_slots)
|
|
||||||
{
|
{
|
||||||
std::vector<int> cur_extruder_ids, target_extruder_ids, variant_index;
|
std::vector<int> cur_extruder_ids, target_extruder_ids, variant_index;
|
||||||
std::vector<std::string> cur_extruder_variants, target_extruder_variants;
|
std::vector<std::string> cur_extruder_variants, target_extruder_variants;
|
||||||
@@ -11658,13 +11619,6 @@ void DynamicPrintConfig::update_diff_values_to_child_config(DynamicPrintConfig&
|
|||||||
if (opt_src && opt_target && (*opt_src != *opt_target)) {
|
if (opt_src && opt_target && (*opt_src != *opt_target)) {
|
||||||
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" change key %1% from base_value %2% to child's value %3%")
|
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" change key %1% from base_value %2% to child's value %3%")
|
||||||
%opt %(opt_src->serialize()) %(opt_target->serialize());
|
%opt %(opt_src->serialize()) %(opt_target->serialize());
|
||||||
const std::vector<size_t> *unset_slots = nullptr;
|
|
||||||
if (nil_slots && opt_src->is_vector())
|
|
||||||
if (auto it = nil_slots->find(opt); it != nil_slots->end())
|
|
||||||
unset_slots = &it->second;
|
|
||||||
std::unique_ptr<ConfigOption> base_value(unset_slots ? opt_src->clone() : nullptr);
|
|
||||||
int stride = 1;
|
|
||||||
bool merged_by_variant = false;
|
|
||||||
if (opt_target->is_scalar()
|
if (opt_target->is_scalar()
|
||||||
|| ((key_set1.find(opt) == key_set1.end()) && (key_set2.empty() || (key_set2.find(opt) == key_set2.end())))) {
|
|| ((key_set1.find(opt) == key_set1.end()) && (key_set2.empty() || (key_set2.find(opt) == key_set2.end())))) {
|
||||||
//nothing to do, keep the original one
|
//nothing to do, keep the original one
|
||||||
@@ -11673,6 +11627,7 @@ void DynamicPrintConfig::update_diff_values_to_child_config(DynamicPrintConfig&
|
|||||||
else {
|
else {
|
||||||
ConfigOptionVectorBase* opt_vec_src = static_cast<ConfigOptionVectorBase*>(opt_src);
|
ConfigOptionVectorBase* opt_vec_src = static_cast<ConfigOptionVectorBase*>(opt_src);
|
||||||
const ConfigOptionVectorBase* opt_vec_dest = static_cast<const ConfigOptionVectorBase*>(opt_target);
|
const ConfigOptionVectorBase* opt_vec_dest = static_cast<const ConfigOptionVectorBase*>(opt_target);
|
||||||
|
int stride = 1;
|
||||||
if (key_set2.find(opt) != key_set2.end())
|
if (key_set2.find(opt) != key_set2.end())
|
||||||
stride = 2;
|
stride = 2;
|
||||||
// set_only_diff() requires the base vector length to equal variant_index.size()*stride, where
|
// set_only_diff() requires the base vector length to equal variant_index.size()*stride, where
|
||||||
@@ -11686,26 +11641,8 @@ void DynamicPrintConfig::update_diff_values_to_child_config(DynamicPrintConfig&
|
|||||||
if (opt_vec_src->size() != variant_index.size() * size_t(stride)) {
|
if (opt_vec_src->size() != variant_index.size() * size_t(stride)) {
|
||||||
opt_src->set(opt_target);
|
opt_src->set(opt_target);
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
opt_vec_src->set_only_diff(opt_vec_dest, variant_index, stride);
|
opt_vec_src->set_only_diff(opt_vec_dest, variant_index, stride);
|
||||||
merged_by_variant = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// A slot that was nil in the child's file is not set there, so it keeps this config's value.
|
|
||||||
if (unset_slots) {
|
|
||||||
auto *merged = static_cast<ConfigOptionVectorBase*>(opt_src);
|
|
||||||
const size_t base_size = static_cast<const ConfigOptionVectorBase*>(base_value.get())->size();
|
|
||||||
for (size_t slot = 0; slot < merged->size() && slot < base_size; ++slot) {
|
|
||||||
size_t child_slot = slot;
|
|
||||||
if (merged_by_variant) {
|
|
||||||
const int child_variant = variant_index[slot / stride];
|
|
||||||
if (child_variant == -1)
|
|
||||||
continue;
|
|
||||||
child_slot = size_t(child_variant) * stride + slot % stride;
|
|
||||||
}
|
|
||||||
if (std::find(unset_slots->begin(), unset_slots->end(), child_slot) != unset_slots->end())
|
|
||||||
merged->set_at(base_value.get(), slot, slot);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -859,10 +859,7 @@ public:
|
|||||||
|
|
||||||
void update_non_diff_values_to_base_config(DynamicPrintConfig& new_config, const t_config_option_keys& keys, const std::set<std::string>& different_keys, std::string extruder_id_name, std::string extruder_variant_name,
|
void update_non_diff_values_to_base_config(DynamicPrintConfig& new_config, const t_config_option_keys& keys, const std::set<std::string>& different_keys, std::string extruder_id_name, std::string extruder_variant_name,
|
||||||
std::set<std::string>& key_set1, std::set<std::string>& key_set2);
|
std::set<std::string>& key_set1, std::set<std::string>& key_set2);
|
||||||
// nil_slots: per option, the slots of new_config that were nil in its file (see ConfigSubstitutionContext::nil_slots);
|
void update_diff_values_to_child_config(DynamicPrintConfig& new_config, std::string extruder_id_name, std::string extruder_variant_name, std::set<std::string>& key_set1, std::set<std::string>& key_set2);
|
||||||
// those variants keep this config's value.
|
|
||||||
void update_diff_values_to_child_config(DynamicPrintConfig& new_config, std::string extruder_id_name, std::string extruder_variant_name, std::set<std::string>& key_set1, std::set<std::string>& key_set2,
|
|
||||||
const std::map<std::string, std::vector<size_t>>* nil_slots = nullptr);
|
|
||||||
|
|
||||||
int update_values_from_single_to_multi(DynamicPrintConfig& multi_config, std::set<std::string>& key_set, std::string id_name, std::string variant_name);
|
int update_values_from_single_to_multi(DynamicPrintConfig& multi_config, std::set<std::string>& key_set, std::string id_name, std::string variant_name);
|
||||||
int update_values_from_multi_to_multi(DynamicPrintConfig& new_config, std::set<std::string>& key_set, std::string id_name, std::string variant_name, std::vector<std::string>& extruder_variants);
|
int update_values_from_multi_to_multi(DynamicPrintConfig& new_config, std::set<std::string>& key_set, std::string id_name, std::string variant_name, std::vector<std::string>& extruder_variants);
|
||||||
|
|||||||
@@ -6693,6 +6693,11 @@ void ObjectList::OnEditingStarted(wxDataViewEvent &event)
|
|||||||
|
|
||||||
void ObjectList::OnEditingDone(wxDataViewEvent &event)
|
void ObjectList::OnEditingDone(wxDataViewEvent &event)
|
||||||
{
|
{
|
||||||
|
// ~wxDataViewCtrl ends the in-place editing, so this handler runs while ~Plater is already tearing
|
||||||
|
// the Plater down. Nothing below may touch the Plater or the plates any more.
|
||||||
|
if (wxGetApp().is_closing())
|
||||||
|
return;
|
||||||
|
|
||||||
if (event.GetColumn() != colName)
|
if (event.GetColumn() != colName)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|||||||
@@ -3572,6 +3572,8 @@ void PartPlate::update_slice_result_valid_state(bool valid)
|
|||||||
//update current slice context into backgroud slicing process
|
//update current slice context into backgroud slicing process
|
||||||
void PartPlate::update_slice_context(BackgroundSlicingProcess & process)
|
void PartPlate::update_slice_context(BackgroundSlicingProcess & process)
|
||||||
{
|
{
|
||||||
|
//this callback outlives the call, so it is dropped again in PartPlateList::clear() and
|
||||||
|
//PartPlateList::delete_plate() before the plate is destroyed
|
||||||
auto statuscb = [this](const Slic3r::PrintBase::SlicingStatus& status) {
|
auto statuscb = [this](const Slic3r::PrintBase::SlicingStatus& status) {
|
||||||
Slic3r::SlicingStatusEvent *event = new Slic3r::SlicingStatusEvent(EVT_SLICING_UPDATE, 0, status);
|
Slic3r::SlicingStatusEvent *event = new Slic3r::SlicingStatusEvent(EVT_SLICING_UPDATE, 0, status);
|
||||||
//BBS: GUI refactor: add plate info befor message
|
//BBS: GUI refactor: add plate info befor message
|
||||||
@@ -4543,7 +4545,14 @@ void PartPlateList::clear(bool delete_plates, bool release_print_list, bool exce
|
|||||||
else
|
else
|
||||||
plate->clear();
|
plate->clear();
|
||||||
if (delete_plates)
|
if (delete_plates)
|
||||||
|
{
|
||||||
|
//the slicing status callback installed by update_slice_context() captures the plate, so drop it
|
||||||
|
//while the Print is still alive: the prints are only released below, after this loop, and are
|
||||||
|
//not released at all when release_print_list is false.
|
||||||
|
if (Print* print = plate->fff_print())
|
||||||
|
print->set_status_default();
|
||||||
delete plate;
|
delete plate;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (delete_plates)
|
if (delete_plates)
|
||||||
@@ -4886,6 +4895,10 @@ int PartPlateList::delete_plate(int index)
|
|||||||
//destroy the print object
|
//destroy the print object
|
||||||
int print_index;
|
int print_index;
|
||||||
plate->get_print(nullptr, nullptr, &print_index);
|
plate->get_print(nullptr, nullptr, &print_index);
|
||||||
|
//the slicing status callback installed by update_slice_context() captures the plate, and destroy_print()
|
||||||
|
//frees the Print, so drop the callback here, the last point where both are still alive.
|
||||||
|
if (Print* print = plate->fff_print())
|
||||||
|
print->set_status_default();
|
||||||
destroy_print(print_index);
|
destroy_print(print_index);
|
||||||
|
|
||||||
delete plate;
|
delete plate;
|
||||||
|
|||||||
+47
-192
@@ -103,7 +103,6 @@
|
|||||||
#include "Selection.hpp"
|
#include "Selection.hpp"
|
||||||
#include "GLToolbar.hpp"
|
#include "GLToolbar.hpp"
|
||||||
#include "GUI_Preview.hpp"
|
#include "GUI_Preview.hpp"
|
||||||
#include "UVEditorCanvas.hpp"
|
|
||||||
#include "3DBed.hpp"
|
#include "3DBed.hpp"
|
||||||
#include "PartPlate.hpp"
|
#include "PartPlate.hpp"
|
||||||
#include "Camera.hpp"
|
#include "Camera.hpp"
|
||||||
@@ -1798,14 +1797,11 @@ bool Sidebar::priv::switch_diameter_to(const wxString &diameter)
|
|||||||
Preset& printer_preset = wxGetApp().preset_bundle->printers.get_edited_preset();
|
Preset& printer_preset = wxGetApp().preset_bundle->printers.get_edited_preset();
|
||||||
// The combo lists printer variants, and the variant of a mixed-nozzle machine ("0.4+0.6") is no
|
// The combo lists printer variants, and the variant of a mixed-nozzle machine ("0.4+0.6") is no
|
||||||
// single extruder's diameter, so the preset's own variant answers first.
|
// single extruder's diameter, so the preset's own variant answers first.
|
||||||
const std::string &printer_variant = printer_preset.config.opt_string("printer_variant");
|
if (printer_preset.config.opt_string("printer_variant") == diameter.ToStdString()) {
|
||||||
if (printer_variant == diameter.ToStdString()) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// A named variant ("0.4 High Flow") shares its diameter with the standard profile, which selecting
|
|
||||||
// the plain diameter switches back to, so only a preset naming no variant is kept by its diameter.
|
|
||||||
auto* nozzle_diameter = dynamic_cast<const ConfigOptionFloats*>(printer_preset.config.option("nozzle_diameter"));
|
auto* nozzle_diameter = dynamic_cast<const ConfigOptionFloats*>(printer_preset.config.option("nozzle_diameter"));
|
||||||
if (printer_variant.empty() && nozzle_diameter && nozzle_diameter->size() > 0) {
|
if (nozzle_diameter && nozzle_diameter->size() > 0) {
|
||||||
auto current_nozzle_dia = get_diameter_string(nozzle_diameter->values[0]);
|
auto current_nozzle_dia = get_diameter_string(nozzle_diameter->values[0]);
|
||||||
// If the selected diameter is the same as current nozzle, don't switch profiles
|
// If the selected diameter is the same as current nozzle, don't switch profiles
|
||||||
if (current_nozzle_dia == diameter.ToStdString()) {
|
if (current_nozzle_dia == diameter.ToStdString()) {
|
||||||
@@ -2240,14 +2236,12 @@ bool Sidebar::priv::sync_extruder_list(bool &only_external_material, bool is_man
|
|||||||
std::string machine_print_name = obj->get_show_printer_type();
|
std::string machine_print_name = obj->get_show_printer_type();
|
||||||
PresetBundle *preset_bundle = wxGetApp().preset_bundle;
|
PresetBundle *preset_bundle = wxGetApp().preset_bundle;
|
||||||
std::string target_model_id = preset_bundle->printers.get_selected_preset().get_printer_type(preset_bundle);
|
std::string target_model_id = preset_bundle->printers.get_selected_preset().get_printer_type(preset_bundle);
|
||||||
const bool optional_printer_model = DevPrinterConfigUtil::is_optional_printer_model_id(obj->printer_type);
|
Preset* machine_preset = get_printer_preset(obj);
|
||||||
const bool optional_target_model = DevPrinterConfigUtil::is_optional_printer_model_id(target_model_id);
|
if (!machine_preset) {
|
||||||
Preset* machine_preset = optional_printer_model ? nullptr : get_printer_preset(obj);
|
|
||||||
if (!optional_printer_model && !optional_target_model && !machine_preset) {
|
|
||||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << __LINE__ << "check error: machine_preset empty";
|
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << __LINE__ << "check error: machine_preset empty";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!optional_printer_model && !optional_target_model && machine_print_name != target_model_id) {
|
if (machine_print_name != target_model_id) {
|
||||||
MessageDialog dlg(this->plater, _L("The currently selected machine preset is inconsistent with the connected printer type.\n"
|
MessageDialog dlg(this->plater, _L("The currently selected machine preset is inconsistent with the connected printer type.\n"
|
||||||
"Are you sure to continue syncing?"), _L("Sync printer information"), wxICON_WARNING | wxYES | wxNO);
|
"Are you sure to continue syncing?"), _L("Sync printer information"), wxICON_WARNING | wxYES | wxNO);
|
||||||
if (dlg.ShowModal() == wxID_NO) {
|
if (dlg.ShowModal() == wxID_NO) {
|
||||||
@@ -2439,11 +2433,6 @@ void Sidebar::priv::update_sync_status(const MachineObject *obj)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DevPrinterConfigUtil::is_optional_printer_model_id(obj->printer_type)) {
|
|
||||||
clear_all_sync_status();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool printer_synced = false;
|
bool printer_synced = false;
|
||||||
// 1. update printer status
|
// 1. update printer status
|
||||||
const Preset &cur_preset = wxGetApp().preset_bundle->printers.get_edited_preset();
|
const Preset &cur_preset = wxGetApp().preset_bundle->printers.get_edited_preset();
|
||||||
@@ -3937,18 +3926,13 @@ void Sidebar::update_presets(Preset::Type preset_type)
|
|||||||
combo_flow->Show(combo_flow->GetCount() > 0);
|
combo_flow->Show(combo_flow->GetCount() > 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
auto update_extruder_diameter = [&diameters, &nozzle_diameter, &diameter](int extruder_index,ExtruderGroup & extruder) {
|
auto update_extruder_diameter = [&diameters, &nozzle_diameter](int extruder_index,ExtruderGroup & extruder) {
|
||||||
extruder.combo_diameter->Clear();
|
extruder.combo_diameter->Clear();
|
||||||
if (extruder_index >= int(nozzle_diameter->values.size()))
|
if (extruder_index >= int(nozzle_diameter->values.size()))
|
||||||
return;
|
return;
|
||||||
int select = -1;
|
int select = -1;
|
||||||
// ORCA get the actual nozzle diameter from printer config
|
// ORCA get the actual nozzle diameter from printer config
|
||||||
auto nozzle_dia = get_diameter_string(nozzle_diameter->values[extruder_index]);
|
auto nozzle_dia = get_diameter_string(nozzle_diameter->values[extruder_index]);
|
||||||
// Named variants such as "0.4HS" and "0.4 High Flow" share a physical diameter.
|
|
||||||
// Retain the variant selection unless the diameter was customized.
|
|
||||||
const bool keep_variant = diameter.substr(0, diameter.find_first_not_of("0123456789.")) == nozzle_dia &&
|
|
||||||
std::find(diameters.begin(), diameters.end(), diameter) != diameters.end();
|
|
||||||
const std::string &selected_variant = keep_variant ? diameter : nozzle_dia;
|
|
||||||
// ORCA try to add nozzle diameter from config if list is empty. fixes blank nozzle combo box when preset has no alias
|
// ORCA try to add nozzle diameter from config if list is empty. fixes blank nozzle combo box when preset has no alias
|
||||||
if(!diameters.empty() && diameters[0].empty() && !nozzle_dia.empty()){
|
if(!diameters.empty() && diameters[0].empty() && !nozzle_dia.empty()){
|
||||||
diameters[0] = nozzle_dia;
|
diameters[0] = nozzle_dia;
|
||||||
@@ -3958,7 +3942,7 @@ void Sidebar::update_presets(Preset::Type preset_type)
|
|||||||
diameters.push_back(nozzle_dia);
|
diameters.push_back(nozzle_dia);
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < diameters.size(); ++i) {
|
for (size_t i = 0; i < diameters.size(); ++i) {
|
||||||
if (diameters[i] == selected_variant)
|
if (diameters[i] == nozzle_dia)
|
||||||
select = extruder.combo_diameter->GetCount();
|
select = extruder.combo_diameter->GetCount();
|
||||||
extruder.combo_diameter->Append(diameters[i], {});
|
extruder.combo_diameter->Append(diameters[i], {});
|
||||||
}
|
}
|
||||||
@@ -6082,30 +6066,11 @@ void Sidebar::load_ams_list(MachineObject* obj)
|
|||||||
filament_ams_list = build_filament_ams_list(obj);
|
filament_ams_list = build_filament_ams_list(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool device_change = false;
|
||||||
const std::string& device = obj ? obj->get_dev_id() : "";
|
const std::string& device = obj ? obj->get_dev_id() : "";
|
||||||
const bool same_device = p->ams_list_device == device;
|
if (p->ams_list_device != device) {
|
||||||
|
|
||||||
// Keep sync metadata out of the device payload, but preserve it across a
|
|
||||||
// subscription refresh when the physical filament in a slot is unchanged.
|
|
||||||
// Otherwise the refreshed configs differ only by the missing
|
|
||||||
// filament_changed key, causing combo boxes to rebuild and lose their
|
|
||||||
// transient post-sync badges.
|
|
||||||
auto &previous_filament_ams_list = wxGetApp().preset_bundle->filament_ams_list;
|
|
||||||
for (auto &entry : filament_ams_list) {
|
|
||||||
auto previous = previous_filament_ams_list.find(entry.first);
|
|
||||||
const auto *previous_changed = previous == previous_filament_ams_list.end() ? nullptr :
|
|
||||||
dynamic_cast<const ConfigOptionBool *>(previous->second.option("filament_changed"));
|
|
||||||
if (!same_device || previous_changed == nullptr ||
|
|
||||||
previous->second.opt_string("filament_id", 0u) != entry.second.opt_string("filament_id", 0u)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
entry.second.set_key_value("filament_changed",
|
|
||||||
new ConfigOptionBool{previous_changed->value});
|
|
||||||
}
|
|
||||||
|
|
||||||
bool device_change = !same_device;
|
|
||||||
if (device_change) {
|
|
||||||
p->ams_list_device = device;
|
p->ams_list_device = device;
|
||||||
|
device_change = true;
|
||||||
}
|
}
|
||||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": %1% items") % filament_ams_list.size();
|
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": %1% items") % filament_ams_list.size();
|
||||||
if (wxGetApp().preset_bundle->filament_ams_list == filament_ams_list && !device_change)
|
if (wxGetApp().preset_bundle->filament_ams_list == filament_ams_list && !device_change)
|
||||||
@@ -6115,27 +6080,9 @@ void Sidebar::load_ams_list(MachineObject* obj)
|
|||||||
wxGetApp().preset_bundle->filament_ams_list = filament_ams_list;
|
wxGetApp().preset_bundle->filament_ams_list = filament_ams_list;
|
||||||
|
|
||||||
for (auto c : p->combos_filament){
|
for (auto c : p->combos_filament){
|
||||||
c->set_sync_badge(false);
|
|
||||||
c->update();
|
c->update();
|
||||||
}
|
if (device_change) {
|
||||||
|
c->ShowBadge(false);//change printer,then clear badge
|
||||||
if (!device_change) {
|
|
||||||
size_t combo_index = 0;
|
|
||||||
for (const auto &entry : filament_ams_list) {
|
|
||||||
const auto &tray = entry.second;
|
|
||||||
const bool has_filament = !tray.opt_string("filament_id", 0u).empty();
|
|
||||||
const bool is_placeholder = tray.has("filament_slot_placeholder") &&
|
|
||||||
tray.opt_bool("filament_slot_placeholder", 0u);
|
|
||||||
if (!has_filament && !is_placeholder) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (combo_index >= p->combos_filament.size()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const auto *filament_changed = dynamic_cast<const ConfigOptionBool *>(tray.option("filament_changed"));
|
|
||||||
p->combos_filament[combo_index]->set_sync_badge(
|
|
||||||
has_filament && !is_placeholder && filament_changed != nullptr && filament_changed->value);
|
|
||||||
++combo_index;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6309,32 +6256,18 @@ void Sidebar::sync_ams_list(bool is_from_big_sync_btn)
|
|||||||
auto tip = sync_color_only ? _L("Only filament color information has been synchronized from printer.") :
|
auto tip = sync_color_only ? _L("Only filament color information has been synchronized from printer.") :
|
||||||
_L("Filament type and color information have been synchronized, but slot information is not included.");
|
_L("Filament type and color information have been synchronized, but slot information is not included.");
|
||||||
c->SetToolTip(tip);
|
c->SetToolTip(tip);
|
||||||
c->set_sync_badge(true);
|
c->ShowBadge(true);
|
||||||
};
|
};
|
||||||
{ // badge ams filament
|
{ // badge ams filament
|
||||||
clear_combos_filament_badge();
|
clear_combos_filament_badge();
|
||||||
if (sync_result.direct_sync) {
|
if (sync_result.direct_sync) {
|
||||||
// A placeholder contributes a preserved project filament to the
|
// Orca: PresetBundle::sync_ams_list rebuilds combos_filament
|
||||||
// overwrite result, but it is not AMS-sourced and must not get a
|
// 1:1 from the AMS trays that produce a combo (loaded trays + placeholders; non-placeholder
|
||||||
// sync badge. Non-placeholder empty trays are omitted entirely.
|
// empty trays are skipped), so every resulting combo is AMS-sourced and gets a badge. The
|
||||||
size_t combo_index = 0;
|
// previous per-tray index walked the full filament_ams_list (including the skipped empties),
|
||||||
for (const auto &entry : wxGetApp().preset_bundle->filament_ams_list) {
|
// so an empty slot before a loaded one dropped the badge for the trailing filaments.
|
||||||
const auto &tray = entry.second;
|
for (auto &c : p->combos_filament) {
|
||||||
const bool has_filament = !tray.opt_string("filament_id", 0u).empty();
|
badge_combox_filament(c);
|
||||||
const bool is_placeholder = tray.has("filament_slot_placeholder") &&
|
|
||||||
tray.opt_bool("filament_slot_placeholder", 0u);
|
|
||||||
if (!has_filament && !is_placeholder) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (combo_index >= p->combos_filament.size()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (is_placeholder) {
|
|
||||||
p->combos_filament[combo_index]->set_sync_badge(false);
|
|
||||||
} else {
|
|
||||||
badge_combox_filament(p->combos_filament[combo_index]);
|
|
||||||
}
|
|
||||||
++combo_index;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6602,11 +6535,6 @@ template<typename T> void setup_dialog_position(T& info)
|
|||||||
|
|
||||||
void Sidebar::pop_sync_nozzle_and_ams_dialog() {
|
void Sidebar::pop_sync_nozzle_and_ams_dialog() {
|
||||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " begin pop_sync_nozzle_and_ams_dialog";
|
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " begin pop_sync_nozzle_and_ams_dialog";
|
||||||
auto agent = wxGetApp().getAgent();
|
|
||||||
if (!agent || agent->get_filament_sync_mode() == FilamentSyncMode::none) {
|
|
||||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " filament synchronization is not supported; skipping dialog";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
wxTheApp->CallAfter([this]() {
|
wxTheApp->CallAfter([this]() {
|
||||||
SyncNozzleAndAmsDialog::InputInfo temp_na_info;
|
SyncNozzleAndAmsDialog::InputInfo temp_na_info;
|
||||||
wxPoint big_btn_pt;
|
wxPoint big_btn_pt;
|
||||||
@@ -6738,14 +6666,17 @@ void Sidebar::clear_combos_filament_badge()
|
|||||||
{
|
{
|
||||||
auto &combos_filament = p->combos_filament;
|
auto &combos_filament = p->combos_filament;
|
||||||
for (auto &c : combos_filament) { // clear flag
|
for (auto &c : combos_filament) { // clear flag
|
||||||
c->set_sync_badge(false);
|
c->ShowBadge(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sidebar::udpate_combos_filament_badge() {
|
void Sidebar::udpate_combos_filament_badge() {
|
||||||
auto &combos_filament = p->combos_filament;
|
auto &combos_filament = p->combos_filament;
|
||||||
for (auto &c : combos_filament) {
|
for (auto &c : combos_filament) {
|
||||||
c->update_badge_according_flag();
|
auto selection = c->GetSelection();
|
||||||
|
auto select_flag = c->GetFlag(selection);
|
||||||
|
auto ok = select_flag == (int) PresetComboBox::FilamentAMSType::FROM_AMS;
|
||||||
|
c->ShowBadge(ok);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -7117,13 +7048,6 @@ struct Plater::priv
|
|||||||
GLToolbar collapse_toolbar;
|
GLToolbar collapse_toolbar;
|
||||||
Preview *preview;
|
Preview *preview;
|
||||||
AssembleView* assemble_view { nullptr };
|
AssembleView* assemble_view { nullptr };
|
||||||
// Docked/resizable 2D pane showing GLGizmoTextureDisplacement's LSCM unwrap of a painted
|
|
||||||
// patch; a sibling AUI pane alongside "sidebar"/"main", not part of the view3D/preview/
|
|
||||||
// assemble_view sizer - see its registration below and Plater::get_uv_editor_canvas(). The
|
|
||||||
// pane hosts the panel (toolbar + canvas + status line); uv_editor_canvas is its inner canvas,
|
|
||||||
// cached so the gizmo can reach it directly.
|
|
||||||
UVEditorPanel* uv_editor_panel { nullptr };
|
|
||||||
UVEditorCanvas* uv_editor_canvas { nullptr };
|
|
||||||
bool first_enter_assemble{ true };
|
bool first_enter_assemble{ true };
|
||||||
std::unique_ptr<NotificationManager> notification_manager;
|
std::unique_ptr<NotificationManager> notification_manager;
|
||||||
|
|
||||||
@@ -7373,8 +7297,6 @@ struct Plater::priv
|
|||||||
|
|
||||||
void undo();
|
void undo();
|
||||||
void redo();
|
void redo();
|
||||||
// True, and tells the user, while a background job is working on the model - see the definition.
|
|
||||||
bool undo_redo_blocked_by_job();
|
|
||||||
void undo_redo_to(size_t time_to_load);
|
void undo_redo_to(size_t time_to_load);
|
||||||
|
|
||||||
// BBS: backup
|
// BBS: backup
|
||||||
@@ -7828,26 +7750,6 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
|||||||
.BottomDockable(false)
|
.BottomDockable(false)
|
||||||
.BestSize(wxSize(39 * wxGetApp().em_unit(), 90 * wxGetApp().em_unit())));
|
.BestSize(wxSize(39 * wxGetApp().em_unit(), 90 * wxGetApp().em_unit())));
|
||||||
|
|
||||||
// UV editor pane for GLGizmoTextureDisplacement's LSCM unwrap preview - a resizable/dockable
|
|
||||||
// sibling of "sidebar"/"main" like everything else registered on this same AUI manager, not a
|
|
||||||
// change to the view3D/preview/assemble_view sizer above. Hidden by default: only relevant
|
|
||||||
// while that gizmo is active with a layer using the "Unwrap (LSCM)" projection method (see
|
|
||||||
// Plater::show_uv_editor()), so it stays out of the way of everyone else's window layout.
|
|
||||||
uv_editor_panel = new UVEditorPanel(q);
|
|
||||||
uv_editor_canvas = uv_editor_panel->canvas();
|
|
||||||
m_aui_mgr.AddPane(uv_editor_panel, wxAuiPaneInfo()
|
|
||||||
.Name("uv_editor")
|
|
||||||
.Caption(_L("UV Editor"))
|
|
||||||
.Right()
|
|
||||||
.Hide()
|
|
||||||
.BestSize(wxSize(40 * wxGetApp().em_unit(), 40 * wxGetApp().em_unit())));
|
|
||||||
// Closing the pane with its own X has to reach the gizmo, or its next update would simply show the pane again.
|
|
||||||
q->Bind(wxEVT_AUI_PANE_CLOSE, [this](wxAuiManagerEvent &evt) {
|
|
||||||
evt.Skip();
|
|
||||||
if (evt.GetPane() != nullptr && evt.GetPane()->window == uv_editor_panel && uv_editor_canvas != nullptr)
|
|
||||||
uv_editor_canvas->run_command(UVEditorCanvas::Command::PaneClosed);
|
|
||||||
});
|
|
||||||
|
|
||||||
auto* panel_sizer = new wxBoxSizer(wxHORIZONTAL);
|
auto* panel_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||||
panel_sizer->Add(view3D, 1, wxEXPAND | wxALL, 0);
|
panel_sizer->Add(view3D, 1, wxEXPAND | wxALL, 0);
|
||||||
panel_sizer->Add(preview, 1, wxEXPAND | wxALL, 0);
|
panel_sizer->Add(preview, 1, wxEXPAND | wxALL, 0);
|
||||||
@@ -7889,13 +7791,6 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
|||||||
BOOST_LOG_TRIVIAL(info) << "Removed floating AUI state from saved window layout for Wayland";
|
BOOST_LOG_TRIVIAL(info) << "Removed floating AUI state from saved window layout for Wayland";
|
||||||
}
|
}
|
||||||
|
|
||||||
// The UV editor is a transient, gizmo-driven pane (see show_uv_editor()); a saved layout
|
|
||||||
// from a session that happened to close with it open would otherwise restore it visible on
|
|
||||||
// startup, with nothing painted in it. Force it hidden here so it only ever appears when the
|
|
||||||
// texture-displacement gizmo asks for it.
|
|
||||||
if (wxAuiPaneInfo &uv_pane = m_aui_mgr.GetPane("uv_editor"); uv_pane.IsOk())
|
|
||||||
uv_pane.Hide();
|
|
||||||
|
|
||||||
sidebar_layout.is_collapsed = !sidebar.IsShown();
|
sidebar_layout.is_collapsed = !sidebar.IsShown();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -10744,13 +10639,15 @@ void Plater::priv::reset(bool apply_presets_change, bool reload_presets)
|
|||||||
|
|
||||||
m_worker.cancel_all();
|
m_worker.cancel_all();
|
||||||
|
|
||||||
|
// Stop and reset the Print content. m_worker.cancel_all() only stops the UI jobs, so this has to
|
||||||
|
// happen before reinit() deletes the plates together with the Print the slicing thread may still
|
||||||
|
// be working on.
|
||||||
|
this->background_process.reset();
|
||||||
|
|
||||||
//BBS: clear the partplate list's object before object cleared
|
//BBS: clear the partplate list's object before object cleared
|
||||||
partplate_list.reinit();
|
partplate_list.reinit();
|
||||||
partplate_list.update_slice_context_to_current_plate(background_process);
|
partplate_list.update_slice_context_to_current_plate(background_process);
|
||||||
preview->update_gcode_result(partplate_list.get_current_slice_result());
|
preview->update_gcode_result(partplate_list.get_current_slice_result());
|
||||||
|
|
||||||
// Stop and reset the Print content.
|
|
||||||
this->background_process.reset();
|
|
||||||
model.clear_objects();
|
model.clear_objects();
|
||||||
// clear_objects() only drops the ModelObjects; the CAD recipe is Model-level state and would
|
// clear_objects() only drops the ModelObjects; the CAD recipe is Model-level state and would
|
||||||
// otherwise be written into every project saved for the rest of the session.
|
// otherwise be written into every project saved for the rest of the session.
|
||||||
@@ -12766,7 +12663,7 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt)
|
|||||||
sidebar->auto_calc_flushing_volumes(idx);
|
sidebar->auto_calc_flushing_volumes(idx);
|
||||||
}
|
}
|
||||||
auto select_flag = combo->GetFlag(selection);
|
auto select_flag = combo->GetFlag(selection);
|
||||||
combo->set_sync_badge(select_flag == (int)PresetComboBox::FilamentAMSType::FROM_AMS);
|
combo->ShowBadge(select_flag == (int)PresetComboBox::FilamentAMSType::FROM_AMS);
|
||||||
q->on_filament_change(idx);
|
q->on_filament_change(idx);
|
||||||
}
|
}
|
||||||
bool select_preset = !combo->selection_is_changed_according_to_physical_printers();
|
bool select_preset = !combo->selection_is_changed_according_to_physical_printers();
|
||||||
@@ -15038,25 +14935,8 @@ void Plater::priv::take_snapshot(const std::string& snapshot_name, const UndoRed
|
|||||||
BOOST_LOG_TRIVIAL(info) << "Undo / Redo snapshot taken: " << snapshot_name << ", Undo / Redo stack memory: " << Slic3r::format_memsize_MB(this->undo_redo_stack().memsize()) << log_memory_info();
|
BOOST_LOG_TRIVIAL(info) << "Undo / Redo snapshot taken: " << snapshot_name << ", Undo / Redo stack memory: " << Slic3r::format_memsize_MB(this->undo_redo_stack().memsize()) << log_memory_info();
|
||||||
}
|
}
|
||||||
|
|
||||||
// A background job holds the model it is working on: the texture displacement bake, for one, hands its
|
|
||||||
// result to the volume when it finishes, and it was queued against the geometry as it was at the time.
|
|
||||||
// Undoing while it runs restores an older state under it - a different transform, a different mesh -
|
|
||||||
// and the result then lands on geometry it was never computed for. Undo and redo therefore wait for
|
|
||||||
// the job, and say so rather than doing nothing.
|
|
||||||
bool Plater::priv::undo_redo_blocked_by_job()
|
|
||||||
{
|
|
||||||
if (m_worker.is_idle())
|
|
||||||
return false;
|
|
||||||
notification_manager->push_notification(NotificationType::CustomNotification,
|
|
||||||
NotificationManager::NotificationLevel::RegularNotificationLevel,
|
|
||||||
_u8L("Cannot undo or redo while an operation is running. Stop it first."));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Plater::priv::undo()
|
void Plater::priv::undo()
|
||||||
{
|
{
|
||||||
if (this->undo_redo_blocked_by_job())
|
|
||||||
return;
|
|
||||||
const std::vector<UndoRedo::Snapshot> &snapshots = this->undo_redo_stack().snapshots();
|
const std::vector<UndoRedo::Snapshot> &snapshots = this->undo_redo_stack().snapshots();
|
||||||
auto it_current = std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(this->undo_redo_stack().active_snapshot_time()));
|
auto it_current = std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(this->undo_redo_stack().active_snapshot_time()));
|
||||||
// BBS: undo-redo until modify record
|
// BBS: undo-redo until modify record
|
||||||
@@ -15074,8 +14954,6 @@ void Plater::priv::undo()
|
|||||||
|
|
||||||
void Plater::priv::redo()
|
void Plater::priv::redo()
|
||||||
{
|
{
|
||||||
if (this->undo_redo_blocked_by_job())
|
|
||||||
return;
|
|
||||||
const std::vector<UndoRedo::Snapshot> &snapshots = this->undo_redo_stack().snapshots();
|
const std::vector<UndoRedo::Snapshot> &snapshots = this->undo_redo_stack().snapshots();
|
||||||
auto it_current = std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(this->undo_redo_stack().active_snapshot_time()));
|
auto it_current = std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(this->undo_redo_stack().active_snapshot_time()));
|
||||||
// BBS: undo-redo until modify record
|
// BBS: undo-redo until modify record
|
||||||
@@ -15114,6 +14992,11 @@ void Plater::priv::undo_redo_to(std::vector<UndoRedo::Snapshot>::const_iterator
|
|||||||
// Make sure that no updating function calls take_snapshot until we are done.
|
// Make sure that no updating function calls take_snapshot until we are done.
|
||||||
SuppressSnapshots snapshot_supressor(q);
|
SuppressSnapshots snapshot_supressor(q);
|
||||||
|
|
||||||
|
// Loading a snapshot deletes every PartPlate, which the slicing thread keeps dereferencing (its
|
||||||
|
// current plate and the status callback). Cancel it and wait for it to finish before the jump,
|
||||||
|
// update_after_undo_redo() re-applies the background process to the rebuilt plates afterwards.
|
||||||
|
this->background_process.stop();
|
||||||
|
|
||||||
bool temp_snapshot_was_taken = this->undo_redo_stack().temp_snapshot_active();
|
bool temp_snapshot_was_taken = this->undo_redo_stack().temp_snapshot_active();
|
||||||
PrinterTechnology new_printer_technology = it_snapshot->snapshot_data.printer_technology;
|
PrinterTechnology new_printer_technology = it_snapshot->snapshot_data.printer_technology;
|
||||||
bool printer_technology_changed = this->printer_technology != new_printer_technology;
|
bool printer_technology_changed = this->printer_technology != new_printer_technology;
|
||||||
@@ -16721,7 +16604,7 @@ void adjust_settings_for_flowrate_calib(ModelObjectPtrs& objects, bool linear, i
|
|||||||
auto printer_config = &wxGetApp().preset_bundle->printers.get_edited_preset().config;
|
auto printer_config = &wxGetApp().preset_bundle->printers.get_edited_preset().config;
|
||||||
auto filament_config = &wxGetApp().preset_bundle->filaments.get_edited_preset().config;
|
auto filament_config = &wxGetApp().preset_bundle->filaments.get_edited_preset().config;
|
||||||
|
|
||||||
/// -- scale --
|
/// --- scale ---
|
||||||
// model is created for a 0.4 nozzle, scale z with nozzle size.
|
// model is created for a 0.4 nozzle, scale z with nozzle size.
|
||||||
const ConfigOptionFloats* nozzle_diameter_config = printer_config->option<ConfigOptionFloats>("nozzle_diameter");
|
const ConfigOptionFloats* nozzle_diameter_config = printer_config->option<ConfigOptionFloats>("nozzle_diameter");
|
||||||
std::vector<int> extruder_types = printer_config->option<ConfigOptionEnumsGeneric>("extruder_type")->values;
|
std::vector<int> extruder_types = printer_config->option<ConfigOptionEnumsGeneric>("extruder_type")->values;
|
||||||
@@ -21198,33 +21081,6 @@ GLCanvas3D* Plater::get_assmeble_canvas3D()
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
UVEditorCanvas* Plater::get_uv_editor_canvas()
|
|
||||||
{
|
|
||||||
return p->uv_editor_canvas;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Plater::show_uv_editor(bool show)
|
|
||||||
{
|
|
||||||
if (p->uv_editor_panel == nullptr)
|
|
||||||
return;
|
|
||||||
const wxAuiPaneInfo &pane = p->m_aui_mgr.GetPane(p->uv_editor_panel);
|
|
||||||
if (!pane.IsOk() || pane.IsShown() == show)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Deferred, because GLGizmoTextureDisplacement calls this from its ImGui panel - that is, from
|
|
||||||
// the middle of the 3D canvas's GL frame. Showing an AUI pane re-lays out the window and
|
|
||||||
// delivers the resulting size/paint events synchronously, and the UV canvas painting itself
|
|
||||||
// makes its own surface current in the app's *shared* GL context, which mid-frame is the one
|
|
||||||
// the 3D canvas is drawing into. Doing the layout once the frame is over avoids that entirely.
|
|
||||||
CallAfter([this, show]() {
|
|
||||||
wxAuiPaneInfo &deferred_pane = p->m_aui_mgr.GetPane(p->uv_editor_panel);
|
|
||||||
if (!deferred_pane.IsOk() || deferred_pane.IsShown() == show)
|
|
||||||
return;
|
|
||||||
deferred_pane.Show(show);
|
|
||||||
p->m_aui_mgr.Update();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
GLCanvas3D* Plater::get_current_canvas3D(bool exclude_preview)
|
GLCanvas3D* Plater::get_current_canvas3D(bool exclude_preview)
|
||||||
{
|
{
|
||||||
return p->get_current_canvas3D(exclude_preview);
|
return p->get_current_canvas3D(exclude_preview);
|
||||||
@@ -21486,14 +21342,9 @@ bool Plater::is_same_printer_for_connected_and_selected(bool popup_warning)
|
|||||||
}
|
}
|
||||||
if (!check_printer_initialized(obj, true, popup_warning))
|
if (!check_printer_initialized(obj, true, popup_warning))
|
||||||
return false;
|
return false;
|
||||||
const std::string machine_model = obj->printer_type;
|
Preset * machine_preset = get_printer_preset(obj);
|
||||||
PresetBundle *preset_bundle = wxGetApp().preset_bundle;
|
if (!machine_preset)
|
||||||
const std::string selected_model = preset_bundle ? preset_bundle->printers.get_edited_preset().get_printer_type(preset_bundle) : std::string();
|
|
||||||
if (!DevPrinterConfigUtil::is_optional_printer_model_id(machine_model) &&
|
|
||||||
!DevPrinterConfigUtil::is_optional_printer_model_id(selected_model) &&
|
|
||||||
!get_printer_preset(obj)) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
if (wxGetApp().is_blocking_printing()) {
|
if (wxGetApp().is_blocking_printing()) {
|
||||||
if (popup_warning) {
|
if (popup_warning) {
|
||||||
@@ -22578,6 +22429,11 @@ int Plater::delete_plate(int plate_index)
|
|||||||
if (plate_index == -1)
|
if (plate_index == -1)
|
||||||
index = p->partplate_list.get_curr_plate_index();
|
index = p->partplate_list.get_curr_plate_index();
|
||||||
|
|
||||||
|
// Orca: delete_plate() destroys the plate's Print and GCodeResult, which the slicing thread is
|
||||||
|
// still working on, so it has to be stopped first. can_delete_plate() also refuses while slicing,
|
||||||
|
// but the plate grabber in the 3D scene does not go through it.
|
||||||
|
p->background_process.stop();
|
||||||
|
|
||||||
take_snapshot("delete partplate");
|
take_snapshot("delete partplate");
|
||||||
ret = p->partplate_list.delete_plate(index);
|
ret = p->partplate_list.delete_plate(index);
|
||||||
|
|
||||||
@@ -22907,7 +22763,7 @@ bool Plater::can_delete() const { return p->can_delete(); }
|
|||||||
bool Plater::can_delete_all() const { return p->can_delete_all(); }
|
bool Plater::can_delete_all() const { return p->can_delete_all(); }
|
||||||
bool Plater::can_add_model() const { return !is_background_process_slicing(); }
|
bool Plater::can_add_model() const { return !is_background_process_slicing(); }
|
||||||
bool Plater::can_add_plate() const { return !is_background_process_slicing() && p->can_add_plate(); }
|
bool Plater::can_add_plate() const { return !is_background_process_slicing() && p->can_add_plate(); }
|
||||||
bool Plater::can_delete_plate() const { return p->can_delete_plate(); }
|
bool Plater::can_delete_plate() const { return !is_background_process_slicing() && p->can_delete_plate(); }
|
||||||
bool Plater::can_increase_instances() const { return p->can_increase_instances(); }
|
bool Plater::can_increase_instances() const { return p->can_increase_instances(); }
|
||||||
bool Plater::can_decrease_instances() const { return p->can_decrease_instances(); }
|
bool Plater::can_decrease_instances() const { return p->can_decrease_instances(); }
|
||||||
bool Plater::can_set_instance_to_object() const { return p->can_set_instance_to_object(); }
|
bool Plater::can_set_instance_to_object() const { return p->can_set_instance_to_object(); }
|
||||||
@@ -22964,9 +22820,8 @@ bool Plater::can_copy_to_clipboard() const
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The job check keeps the buttons in step with priv::undo()/redo(), which refuse while one runs.
|
bool Plater::can_undo() const { return IsShown() && p->is_view3D_shown() && p->undo_redo_stack().has_undo_snapshot(); }
|
||||||
bool Plater::can_undo() const { return IsShown() && p->is_view3D_shown() && p->m_worker.is_idle() && p->undo_redo_stack().has_undo_snapshot(); }
|
bool Plater::can_redo() const { return IsShown() && p->is_view3D_shown() && p->undo_redo_stack().has_redo_snapshot(); }
|
||||||
bool Plater::can_redo() const { return IsShown() && p->is_view3D_shown() && p->m_worker.is_idle() && p->undo_redo_stack().has_redo_snapshot(); }
|
|
||||||
bool Plater::can_reload_from_disk() const { return p->can_reload_from_disk(); }
|
bool Plater::can_reload_from_disk() const { return p->can_reload_from_disk(); }
|
||||||
//BBS
|
//BBS
|
||||||
bool Plater::can_fillcolor() const { return p->can_fillcolor(); }
|
bool Plater::can_fillcolor() const { return p->can_fillcolor(); }
|
||||||
|
|||||||
@@ -819,9 +819,7 @@ int OrcaCloudServiceAgent::user_logout(bool request)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// An explicit logout also wipes the backend the token storage option is not using, so a token
|
clear_session();
|
||||||
// stranded by switching that option cannot sign the account back in later.
|
|
||||||
clear_session(/*all_backends=*/request);
|
|
||||||
return BAMBU_NETWORK_SUCCESS;
|
return BAMBU_NETWORK_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1606,9 +1604,7 @@ void OrcaCloudServiceAgent::persist_user_secret(const std::string& secret)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stored) {
|
(void) stored;
|
||||||
secret_stored = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OrcaCloudServiceAgent::load_user_secret(std::string& out_secret)
|
bool OrcaCloudServiceAgent::load_user_secret(std::string& out_secret)
|
||||||
@@ -1648,7 +1644,6 @@ bool OrcaCloudServiceAgent::load_user_secret(std::string& out_secret)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (integrity_ok && aes256gcm_decrypt(encoded_payload, key, plain) && !plain.empty()) {
|
if (integrity_ok && aes256gcm_decrypt(encoded_payload, key, plain) && !plain.empty()) {
|
||||||
secret_stored = true;
|
|
||||||
out_secret = plain;
|
out_secret = plain;
|
||||||
// Upgrade legacy payloads to signed format
|
// Upgrade legacy payloads to signed format
|
||||||
if (payload.rfind("v2:", 0) != 0) {
|
if (payload.rfind("v2:", 0) != 0) {
|
||||||
@@ -1666,7 +1661,6 @@ bool OrcaCloudServiceAgent::load_user_secret(std::string& out_secret)
|
|||||||
if (store.Load(SECRET_STORE_SERVICE, username, secret) && secret.IsOk()) {
|
if (store.Load(SECRET_STORE_SERVICE, username, secret) && secret.IsOk()) {
|
||||||
out_secret.assign(static_cast<const char*>(secret.GetData()), secret.GetSize());
|
out_secret.assign(static_cast<const char*>(secret.GetData()), secret.GetSize());
|
||||||
if (!out_secret.empty()) {
|
if (!out_secret.empty()) {
|
||||||
secret_stored = true;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1676,20 +1670,11 @@ bool OrcaCloudServiceAgent::load_user_secret(std::string& out_secret)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OrcaCloudServiceAgent::clear_user_secret(bool all_backends)
|
void OrcaCloudServiceAgent::clear_user_secret()
|
||||||
{
|
{
|
||||||
// Nothing this process loaded or saved: leave the store alone. Deleting would only cost a
|
wxSecretStore store = wxSecretStore::GetDefault();
|
||||||
// keychain round trip (or a hang while the keychain is unresponsive) and could remove a
|
if (store.IsOk()) {
|
||||||
// login another instance just saved.
|
store.Delete(SECRET_STORE_SERVICE);
|
||||||
if (!secret_stored.exchange(false) && !all_backends) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (all_backends || !m_use_encrypted_token_file) {
|
|
||||||
wxSecretStore store = wxSecretStore::GetDefault();
|
|
||||||
if (store.IsOk()) {
|
|
||||||
store.Delete(SECRET_STORE_SERVICE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
compute_fallback_path();
|
compute_fallback_path();
|
||||||
@@ -2038,13 +2023,13 @@ bool OrcaCloudServiceAgent::set_user_session(const json& session_json, bool noti
|
|||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OrcaCloudServiceAgent::clear_session(bool all_backends)
|
void OrcaCloudServiceAgent::clear_session()
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(session_mutex);
|
std::lock_guard<std::mutex> lock(session_mutex);
|
||||||
session = SessionInfo{};
|
session = SessionInfo{};
|
||||||
}
|
}
|
||||||
clear_user_secret(all_backends);
|
clear_user_secret();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|||||||
@@ -326,7 +326,7 @@ public:
|
|||||||
|
|
||||||
void persist_user_secret(const std::string& secret);
|
void persist_user_secret(const std::string& secret);
|
||||||
bool load_user_secret(std::string& out_secret);
|
bool load_user_secret(std::string& out_secret);
|
||||||
void clear_user_secret(bool all_backends = false);
|
void clear_user_secret();
|
||||||
|
|
||||||
// Token refresh helpers
|
// Token refresh helpers
|
||||||
bool refresh_if_expiring(std::chrono::seconds skew, const std::string& reason);
|
bool refresh_if_expiring(std::chrono::seconds skew, const std::string& reason);
|
||||||
@@ -344,7 +344,7 @@ public:
|
|||||||
bool persist = true);
|
bool persist = true);
|
||||||
// Accepts either nested Orca cloud / GoTrue session JSON or flat WebView token JSON.
|
// Accepts either nested Orca cloud / GoTrue session JSON or flat WebView token JSON.
|
||||||
bool set_user_session(const nlohmann::json& session_json, bool notify_login = true);
|
bool set_user_session(const nlohmann::json& session_json, bool notify_login = true);
|
||||||
void clear_session(bool all_backends = false);
|
void clear_session();
|
||||||
|
|
||||||
static std::string generate_uuid_for_setting_id(const std::string& name, const std::string& user_id = "");
|
static std::string generate_uuid_for_setting_id(const std::string& name, const std::string& user_id = "");
|
||||||
|
|
||||||
@@ -413,11 +413,6 @@ private:
|
|||||||
// Member variables - auth state
|
// Member variables - auth state
|
||||||
PkceBundle pkce_bundle;
|
PkceBundle pkce_bundle;
|
||||||
std::string secret_fallback_path;
|
std::string secret_fallback_path;
|
||||||
// Set once this process has read a secret from the store or written one. Unless the user logs
|
|
||||||
// out explicitly, clear_user_secret() only touches the store while it is set, so a logged-out
|
|
||||||
// instance (the GUI polls the login status every 2 s) makes no keychain calls and cannot wipe
|
|
||||||
// a login another instance saved.
|
|
||||||
std::atomic_bool secret_stored{false};
|
|
||||||
SessionHandler session_handler;
|
SessionHandler session_handler;
|
||||||
OnLoginCompleteHandler on_login_complete_handler;
|
OnLoginCompleteHandler on_login_complete_handler;
|
||||||
SessionInfo session;
|
SessionInfo session;
|
||||||
|
|||||||
@@ -1025,6 +1025,8 @@ void StackImpl::load_snapshot(size_t timestamp, Slic3r::Model& model, Slic3r::GU
|
|||||||
std::vector<std::string> previous_gcode_paths;
|
std::vector<std::string> previous_gcode_paths;
|
||||||
plate_list.get_sliced_result(previous_slice_result, previous_gcode_paths);
|
plate_list.get_sliced_result(previous_slice_result, previous_gcode_paths);
|
||||||
|
|
||||||
|
// The plates are dereferenced by the slicing thread, which the caller
|
||||||
|
// (Plater::priv::undo_redo_to) has stopped before loading the snapshot.
|
||||||
plate_list.reset(false);
|
plate_list.reset(false);
|
||||||
this->load_mutable_object<Slic3r::GUI::PartPlateList>(plate_list.id(), plate_list);
|
this->load_mutable_object<Slic3r::GUI::PartPlateList>(plate_list.id(), plate_list);
|
||||||
plate_list.rebuild_plates_after_deserialize(previous_slice_result, previous_gcode_paths);
|
plate_list.rebuild_plates_after_deserialize(previous_slice_result, previous_gcode_paths);
|
||||||
|
|||||||
@@ -508,158 +508,6 @@ TEST_CASE("load_from_json hands a preset's include list to the caller instead of
|
|||||||
CHECK(config.opt_string("machine_end_gcode") == "M84");
|
CHECK(config.opt_string("machine_end_gcode") == "M84");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("handle_legacy maps BambuStudio keys and values to their OrcaSlicer equivalents", "[Config]") {
|
|
||||||
struct Row { std::string key, value, expected_key, expected_value; };
|
|
||||||
const Row row = GENERATE(values<Row>({
|
|
||||||
{"role_base_wipe_speed", "0", "role_based_wipe_speed", "0"},
|
|
||||||
{"no_slow_down_for_cooling_on_outwalls", "1", "dont_slow_down_outer_wall", "1"},
|
|
||||||
{"process_notes", "note", "notes", "note"},
|
|
||||||
{"prime_tower_max_speed", "70", "wipe_tower_max_purge_speed", "70"},
|
|
||||||
{"enable_support_ironing", "1", "support_ironing", "1"},
|
|
||||||
{"sparse_infill_lattice_angle_1", "-30", "lateral_lattice_angle_1", "-30"},
|
|
||||||
{"sparse_infill_lattice_angle_2", "20", "lateral_lattice_angle_2", "20"},
|
|
||||||
{"lattice_angle_1", "-30", "lateral_lattice_angle_1", "-30"},
|
|
||||||
{"lattice_angle_2", "20", "lateral_lattice_angle_2", "20"},
|
|
||||||
{"sparse_infill_pattern", "2dlattice", "sparse_infill_pattern", "lateral-lattice"},
|
|
||||||
{"sparse_infill_pattern", "2dhoneycomb", "sparse_infill_pattern", "lateral-honeycomb"},
|
|
||||||
{"support_style", "tree_organic", "support_style", "organic"},
|
|
||||||
{"raft_first_layer_expansion", "-1", "raft_first_layer_expansion", "2"},
|
|
||||||
{"raft_first_layer_expansion", "3.5", "raft_first_layer_expansion", "3.5"},
|
|
||||||
{"ensure_vertical_shell_thickness", "enabled", "ensure_vertical_shell_thickness", "ensure_all"},
|
|
||||||
{"ensure_vertical_shell_thickness", "partial", "ensure_vertical_shell_thickness", "ensure_moderate"},
|
|
||||||
{"ensure_vertical_shell_thickness", "disabled", "ensure_vertical_shell_thickness", "none"},
|
|
||||||
{"top_one_wall_type", "not apply", "only_one_wall_top", "0"},
|
|
||||||
{"top_one_wall_type", "all top", "only_one_wall_top", "1"},
|
|
||||||
{"top_one_wall_type", "topmost", "only_one_wall_top", "1"},
|
|
||||||
{"prime_tower_rib_wall", "1", "wipe_tower_wall_type", "rib"},
|
|
||||||
{"prime_tower_rib_wall", "0", "wipe_tower_wall_type", "rectangle"},
|
|
||||||
{"prime_tower_rib_wall", "true", "wipe_tower_wall_type", "rib"},
|
|
||||||
// A rename touches whole entries, not keys that contain the old name.
|
|
||||||
{"different_settings_to_system", "lattice_angle_1;sparse_infill_lattice_angle_1;wall_loops", "different_settings_to_system",
|
|
||||||
"lateral_lattice_angle_1;lateral_lattice_angle_1;wall_loops"},
|
|
||||||
// different_settings_to_system and JSON arrays pass the key alone
|
|
||||||
{"prime_tower_rib_wall", "", "wipe_tower_wall_type", ""},
|
|
||||||
{"top_one_wall_type", "", "only_one_wall_top", ""},
|
|
||||||
{"enable_height_slowdown", "1", "", "1"},
|
|
||||||
{"counter_coef_2", "0.025", "", "0.025"},
|
|
||||||
}));
|
|
||||||
t_config_option_key key = row.key;
|
|
||||||
std::string value = row.value;
|
|
||||||
PrintConfigDef::handle_legacy(key, value);
|
|
||||||
INFO(row.key << " = " << row.value);
|
|
||||||
CHECK(key == row.expected_key);
|
|
||||||
CHECK(value == row.expected_value);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("A BambuStudio project config loads its renamed settings without substitutions", "[Config]") {
|
|
||||||
ScopedTemporaryFile tmp(".json");
|
|
||||||
{
|
|
||||||
boost::nowide::ofstream ofs(tmp.string());
|
|
||||||
ofs << R"({"role_base_wipe_speed":"0","no_slow_down_for_cooling_on_outwalls":["1","0"],)"
|
|
||||||
R"("process_notes":"note","prime_tower_max_speed":"70","enable_support_ironing":"1",)"
|
|
||||||
R"("sparse_infill_pattern":"2dlattice","sparse_infill_lattice_angle_1":"-30",)"
|
|
||||||
R"("support_style":"tree_organic","ensure_vertical_shell_thickness":"partial",)"
|
|
||||||
R"("top_one_wall_type":"not apply","prime_tower_rib_wall":"0","raft_first_layer_expansion":"-1",)"
|
|
||||||
R"("enable_height_slowdown":["1"]})";
|
|
||||||
}
|
|
||||||
DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
|
|
||||||
ConfigSubstitutionContext substitutions(ForwardCompatibilitySubstitutionRule::Disable);
|
|
||||||
std::map<std::string, std::string> key_values;
|
|
||||||
std::string reason;
|
|
||||||
REQUIRE(config.load_from_json(tmp.string(), substitutions, true, key_values, reason) == 0);
|
|
||||||
CHECK(substitutions.substitutions.empty());
|
|
||||||
CHECK(substitutions.unrecogized_keys == std::vector<std::string>{"enable_height_slowdown"});
|
|
||||||
|
|
||||||
CHECK_FALSE(config.opt_bool("role_based_wipe_speed"));
|
|
||||||
CHECK(config.option<ConfigOptionBools>("dont_slow_down_outer_wall")->values == std::vector<unsigned char>{1, 0});
|
|
||||||
CHECK(config.opt_string("notes") == "note");
|
|
||||||
CHECK_THAT(config.opt_float("wipe_tower_max_purge_speed"), Catch::Matchers::WithinAbs(70., 1e-9));
|
|
||||||
CHECK(config.opt_bool("support_ironing"));
|
|
||||||
CHECK(config.opt_enum<InfillPattern>("sparse_infill_pattern") == ipLateralLattice);
|
|
||||||
CHECK_THAT(config.opt_float("lateral_lattice_angle_1"), Catch::Matchers::WithinAbs(-30., 1e-9));
|
|
||||||
CHECK(config.opt_enum<SupportMaterialStyle>("support_style") == smsTreeOrganic);
|
|
||||||
CHECK(config.opt_enum<EnsureVerticalShellThickness>("ensure_vertical_shell_thickness") == evstModerate);
|
|
||||||
CHECK_FALSE(config.opt_bool("only_one_wall_top"));
|
|
||||||
CHECK(config.opt_enum<WipeTowerWallType>("wipe_tower_wall_type") == wtwRectangle);
|
|
||||||
CHECK_THAT(config.opt_float("raft_first_layer_expansion"), Catch::Matchers::WithinAbs(2., 1e-9));
|
|
||||||
CHECK(config.validate().count("raft_first_layer_expansion") == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("load_from_json reads a BambuStudio nil slot as not set", "[Config]") {
|
|
||||||
ScopedTemporaryFile tmp(".json");
|
|
||||||
{
|
|
||||||
boost::nowide::ofstream ofs(tmp.string());
|
|
||||||
// Keys after retraction_length in file order must still load.
|
|
||||||
ofs << R"({"layer_height":"0.2","retraction_length":["0.8","nil"],"wall_loops":"3","z_hop":["nil","nil"],)"
|
|
||||||
R"("z_hop_types":["Spiral Lift","nil"]})";
|
|
||||||
}
|
|
||||||
std::map<std::string, std::string> key_values;
|
|
||||||
std::string reason;
|
|
||||||
|
|
||||||
SECTION("a loader that doesn't merge onto a parent still rejects nil") {
|
|
||||||
DynamicPrintConfig config;
|
|
||||||
ConfigSubstitutionContext context(ForwardCompatibilitySubstitutionRule::Enable);
|
|
||||||
config.load_from_json(tmp.string(), context, true, key_values, reason);
|
|
||||||
CHECK_FALSE(reason.empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("a loader that opts in reads it as not set") {
|
|
||||||
DynamicPrintConfig config;
|
|
||||||
ConfigSubstitutionContext context(ForwardCompatibilitySubstitutionRule::Enable);
|
|
||||||
context.accept_nil = true;
|
|
||||||
REQUIRE(config.load_from_json(tmp.string(), context, true, key_values, reason) == 0);
|
|
||||||
CHECK(reason.empty());
|
|
||||||
|
|
||||||
const auto *default_length = static_cast<const ConfigOptionFloats*>(print_config_def.get("retraction_length")->default_value.get());
|
|
||||||
const auto &retraction_length = config.option<ConfigOptionFloats>("retraction_length")->values;
|
|
||||||
REQUIRE(retraction_length.size() == 2);
|
|
||||||
CHECK_THAT(retraction_length[0], Catch::Matchers::WithinAbs(0.8, 1e-9));
|
|
||||||
CHECK_THAT(retraction_length[1], Catch::Matchers::WithinAbs(default_length->get_at(1), 1e-9));
|
|
||||||
|
|
||||||
std::unique_ptr<ConfigOption> default_types(print_config_def.get("z_hop_types")->create_default_option());
|
|
||||||
const std::vector<std::string> type_defaults = static_cast<const ConfigOptionVectorBase*>(default_types.get())->vserialize();
|
|
||||||
CHECK(static_cast<const ConfigOptionVectorBase*>(config.option("z_hop_types"))->vserialize() ==
|
|
||||||
std::vector<std::string>{"Spiral Lift", type_defaults[1 % type_defaults.size()]});
|
|
||||||
|
|
||||||
CHECK(context.nil_slots == std::map<std::string, std::vector<size_t>>{{"retraction_length", {1}}, {"z_hop_types", {1}}});
|
|
||||||
CHECK_FALSE(config.has("z_hop"));
|
|
||||||
CHECK(config.opt_int("wall_loops") == 3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("A nozzle variant that was nil keeps the parent preset's value", "[Config]") {
|
|
||||||
auto printer = [](std::vector<double> nozzle_diameter, std::vector<double> retraction_length, std::vector<double> max_speed_x) {
|
|
||||||
DynamicPrintConfig config;
|
|
||||||
config.set_key_value("printer_extruder_variant", new ConfigOptionStrings({"Direct Drive Standard", "Direct Drive High Flow"}));
|
|
||||||
config.set_key_value("printer_extruder_id", new ConfigOptionInts({1, 1}));
|
|
||||||
// Not a per-variant key, so the merge copies it whole.
|
|
||||||
config.set_key_value("nozzle_diameter", new ConfigOptionFloats(nozzle_diameter));
|
|
||||||
config.set_key_value("retraction_length", new ConfigOptionFloats(retraction_length));
|
|
||||||
// Two values per variant: normal and silent mode.
|
|
||||||
config.set_key_value("machine_max_speed_x", new ConfigOptionFloats(max_speed_x));
|
|
||||||
return config;
|
|
||||||
};
|
|
||||||
auto check_values = [](const DynamicPrintConfig &config, const char *key, const std::vector<double> &expected) {
|
|
||||||
const std::vector<double> &values = config.option<ConfigOptionFloats>(key)->values;
|
|
||||||
INFO(key);
|
|
||||||
REQUIRE(values.size() == expected.size());
|
|
||||||
for (size_t i = 0; i < expected.size(); ++i)
|
|
||||||
CHECK_THAT(values[i], Catch::Matchers::WithinAbs(expected[i], 1e-9));
|
|
||||||
};
|
|
||||||
DynamicPrintConfig parent = printer({0.4, 0.6}, {0.6, 0.5}, {500, 200, 400, 100});
|
|
||||||
// The nil slots of the child hold the option default after loading.
|
|
||||||
DynamicPrintConfig child = printer({0.2, 0.4}, {0.8, 0.4}, {500, 200, 300, 90});
|
|
||||||
const std::map<std::string, std::vector<size_t>> nil_slots{{"nozzle_diameter", {1}}, {"retraction_length", {1}}, {"machine_max_speed_x", {3}}};
|
|
||||||
|
|
||||||
parent.update_diff_values_to_child_config(child, "printer_extruder_id", "printer_extruder_variant",
|
|
||||||
printer_options_with_variant_1, printer_options_with_variant_2, &nil_slots);
|
|
||||||
|
|
||||||
check_values(parent, "nozzle_diameter", {0.2, 0.6});
|
|
||||||
check_values(parent, "retraction_length", {0.8, 0.5});
|
|
||||||
// Only the silent-mode value of the second variant was nil.
|
|
||||||
check_values(parent, "machine_max_speed_x", {500, 200, 300, 100});
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("save_to_json writes the same document to a stream as to a file", "[Config]") {
|
TEST_CASE("save_to_json writes the same document to a stream as to a file", "[Config]") {
|
||||||
DynamicPrintConfig config;
|
DynamicPrintConfig config;
|
||||||
config.set_key_value("layer_height", new ConfigOptionFloat(0.2));
|
config.set_key_value("layer_height", new ConfigOptionFloat(0.2));
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ add_executable(${_TEST_NAME}_tests
|
|||||||
test_prebuild_queue.cpp
|
test_prebuild_queue.cpp
|
||||||
test_staged_build.cpp
|
test_staged_build.cpp
|
||||||
test_network_versions.cpp
|
test_network_versions.cpp
|
||||||
test_orca_cloud_agent.cpp
|
|
||||||
test_action_source.cpp
|
test_action_source.cpp
|
||||||
test_plugin_host_api.cpp
|
test_plugin_host_api.cpp
|
||||||
# Exercise seam enums and predicates through the embedded Python host API.
|
# Exercise seam enums and predicates through the embedded Python host API.
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
#include <memory.h>
|
#include <memory.h>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
|
||||||
#include <pybind11/embed.h>
|
#include <pybind11/embed.h>
|
||||||
#include <pybind11/pybind11.h>
|
#include <pybind11/pybind11.h>
|
||||||
|
|
||||||
@@ -26,15 +25,8 @@ void ensure_python_initialized()
|
|||||||
config.parse_argv = 0;
|
config.parse_argv = 0;
|
||||||
|
|
||||||
const auto python_home = boost::dll::program_location().parent_path() / "python";
|
const auto python_home = boost::dll::program_location().parent_path() / "python";
|
||||||
#ifdef _WIN32
|
|
||||||
const auto stdlib = python_home / "Lib";
|
|
||||||
#else
|
|
||||||
const auto stdlib = python_home / "lib" /
|
|
||||||
("python" + std::to_string(PY_MAJOR_VERSION) + "." + std::to_string(PY_MINOR_VERSION));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Only a real runtime: a stray python/ folder (packages a test left behind) is not a home.
|
if (boost::filesystem::exists(python_home)) {
|
||||||
if (boost::filesystem::exists(stdlib / "encodings")) {
|
|
||||||
const std::string home = python_home.string();
|
const std::string home = python_home.string();
|
||||||
const PyStatus status = PyConfig_SetBytesString(&config, &config.home, home.c_str());
|
const PyStatus status = PyConfig_SetBytesString(&config, &config.home, home.c_str());
|
||||||
|
|
||||||
|
|||||||
@@ -1,86 +0,0 @@
|
|||||||
#include <catch2/catch_all.hpp>
|
|
||||||
|
|
||||||
#include <boost/filesystem.hpp>
|
|
||||||
#include <boost/filesystem/fstream.hpp>
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "slic3r/Utils/OrcaCloudServiceAgent.hpp"
|
|
||||||
#include "test_utils.hpp"
|
|
||||||
|
|
||||||
using namespace Slic3r;
|
|
||||||
namespace fs = boost::filesystem;
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
// The encrypted token file is the one secret backend a test can observe without a system
|
|
||||||
// keychain. Every agent pointed at the same directory shares it, like separate app instances
|
|
||||||
// share the keychain entry.
|
|
||||||
std::unique_ptr<OrcaCloudServiceAgent> make_file_backed_agent(const fs::path& dir)
|
|
||||||
{
|
|
||||||
auto agent = std::make_unique<OrcaCloudServiceAgent>(dir.string());
|
|
||||||
agent->set_use_encrypted_token_file(true);
|
|
||||||
agent->set_config_dir(dir.string());
|
|
||||||
return agent;
|
|
||||||
}
|
|
||||||
|
|
||||||
fs::path secret_file(const fs::path& dir) { return dir / secret_constants::USER_SECRET_FILENAME; }
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
TEST_CASE("Logging out removes the secret this instance saved", "[OrcaCloudServiceAgent]")
|
|
||||||
{
|
|
||||||
ScopedTemporaryDir dir("orca-secret");
|
|
||||||
auto agent = make_file_backed_agent(dir.path());
|
|
||||||
|
|
||||||
agent->persist_user_secret("refresh-token");
|
|
||||||
REQUIRE(fs::exists(secret_file(dir.path())));
|
|
||||||
|
|
||||||
agent->user_logout(false);
|
|
||||||
CHECK_FALSE(fs::exists(secret_file(dir.path())));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("Logging out removes a secret this instance loaded from the store", "[OrcaCloudServiceAgent]")
|
|
||||||
{
|
|
||||||
ScopedTemporaryDir dir("orca-secret");
|
|
||||||
make_file_backed_agent(dir.path())->persist_user_secret("refresh-token");
|
|
||||||
|
|
||||||
auto agent = make_file_backed_agent(dir.path());
|
|
||||||
std::string secret;
|
|
||||||
REQUIRE(agent->load_user_secret(secret));
|
|
||||||
CHECK(secret == "refresh-token");
|
|
||||||
|
|
||||||
agent->user_logout(false);
|
|
||||||
CHECK_FALSE(fs::exists(secret_file(dir.path())));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("Logging out leaves a secret this instance never loaded or saved alone", "[OrcaCloudServiceAgent]")
|
|
||||||
{
|
|
||||||
ScopedTemporaryDir dir("orca-secret");
|
|
||||||
make_file_backed_agent(dir.path())->persist_user_secret("refresh-token");
|
|
||||||
|
|
||||||
// A logged-out instance is asked to log out on every login-status poll.
|
|
||||||
auto other = make_file_backed_agent(dir.path());
|
|
||||||
other->user_logout(false);
|
|
||||||
other->user_logout(false);
|
|
||||||
CHECK(fs::exists(secret_file(dir.path())));
|
|
||||||
|
|
||||||
std::string secret;
|
|
||||||
REQUIRE(make_file_backed_agent(dir.path())->load_user_secret(secret));
|
|
||||||
CHECK(secret == "refresh-token");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("Logging out leaves a secret this instance could not read alone", "[OrcaCloudServiceAgent]")
|
|
||||||
{
|
|
||||||
ScopedTemporaryDir dir("orca-secret");
|
|
||||||
// Written under another encryption key, e.g. by another OS user sharing the data directory.
|
|
||||||
fs::ofstream(secret_file(dir.path())) << "v2:0000:not-a-payload-this-user-can-decrypt";
|
|
||||||
|
|
||||||
auto agent = make_file_backed_agent(dir.path());
|
|
||||||
std::string secret;
|
|
||||||
REQUIRE_FALSE(agent->load_user_secret(secret));
|
|
||||||
|
|
||||||
agent->user_logout(false);
|
|
||||||
CHECK(fs::exists(secret_file(dir.path())));
|
|
||||||
}
|
|
||||||
@@ -38,9 +38,6 @@ namespace {
|
|||||||
// before this destructor's shutdown() runs.
|
// before this destructor's shutdown() runs.
|
||||||
struct ScopedPluginManager
|
struct ScopedPluginManager
|
||||||
{
|
{
|
||||||
// Before initialize(): the interpreter creates {data_dir}/python/packages and {data_dir}/log,
|
|
||||||
// which would otherwise land in the working directory.
|
|
||||||
ScopedDataDir python_data_dir{"plugin-python"};
|
|
||||||
bool initialized = PluginManager::instance().initialize();
|
bool initialized = PluginManager::instance().initialize();
|
||||||
|
|
||||||
~ScopedPluginManager()
|
~ScopedPluginManager()
|
||||||
|
|||||||
@@ -31,9 +31,6 @@ namespace {
|
|||||||
// same as any other plugin.
|
// same as any other plugin.
|
||||||
struct ScopedManagerShutdown
|
struct ScopedManagerShutdown
|
||||||
{
|
{
|
||||||
// Before initialize(): the interpreter creates {data_dir}/python/packages and {data_dir}/log,
|
|
||||||
// which would otherwise land in the working directory.
|
|
||||||
ScopedDataDir python_data_dir{"plugin-python"};
|
|
||||||
bool initialized = PluginManager::instance().initialize();
|
bool initialized = PluginManager::instance().initialize();
|
||||||
|
|
||||||
~ScopedManagerShutdown()
|
~ScopedManagerShutdown()
|
||||||
|
|||||||
@@ -42,9 +42,6 @@ namespace {
|
|||||||
// Declare this FIRST in a test so it is destroyed last.
|
// Declare this FIRST in a test so it is destroyed last.
|
||||||
struct ScopedPluginManager
|
struct ScopedPluginManager
|
||||||
{
|
{
|
||||||
// Before initialize(): the interpreter creates {data_dir}/python/packages and {data_dir}/log,
|
|
||||||
// which would otherwise land in the working directory.
|
|
||||||
ScopedDataDir python_data_dir{"plugin-python"};
|
|
||||||
bool initialized = false;
|
bool initialized = false;
|
||||||
|
|
||||||
ScopedPluginManager() { initialized = PluginManager::instance().initialize(); }
|
ScopedPluginManager() { initialized = PluginManager::instance().initialize(); }
|
||||||
|
|||||||
@@ -12,8 +12,6 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "plugin_test_utils.hpp"
|
|
||||||
|
|
||||||
namespace py = pybind11;
|
namespace py = pybind11;
|
||||||
using namespace Slic3r;
|
using namespace Slic3r;
|
||||||
|
|
||||||
@@ -23,9 +21,6 @@ namespace {
|
|||||||
// into Python unless PythonInterpreter::instance() reports initialized.
|
// into Python unless PythonInterpreter::instance() reports initialized.
|
||||||
struct ScopedPluginManager
|
struct ScopedPluginManager
|
||||||
{
|
{
|
||||||
// Before initialize(): the interpreter creates {data_dir}/python/packages and {data_dir}/log,
|
|
||||||
// which would otherwise land in the working directory.
|
|
||||||
ScopedDataDir python_data_dir{"plugin-python"};
|
|
||||||
bool initialized = PluginManager::instance().initialize();
|
bool initialized = PluginManager::instance().initialize();
|
||||||
|
|
||||||
~ScopedPluginManager()
|
~ScopedPluginManager()
|
||||||
|
|||||||
@@ -35,9 +35,6 @@ namespace {
|
|||||||
|
|
||||||
struct ScopedPluginManager
|
struct ScopedPluginManager
|
||||||
{
|
{
|
||||||
// Before initialize(): the interpreter creates {data_dir}/python/packages and {data_dir}/log,
|
|
||||||
// which would otherwise land in the working directory.
|
|
||||||
ScopedDataDir python_data_dir{"plugin-python"};
|
|
||||||
bool initialized = false;
|
bool initialized = false;
|
||||||
|
|
||||||
ScopedPluginManager() { initialized = PluginManager::instance().initialize(); }
|
ScopedPluginManager() { initialized = PluginManager::instance().initialize(); }
|
||||||
|
|||||||
Reference in New Issue
Block a user