mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-19 13:41:03 +00:00
Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
904b837799 | ||
|
|
4c2ae6d202 | ||
|
|
ac022b56ad | ||
|
|
d3699dd9f6 | ||
|
|
a315a96c5c | ||
|
|
24cf1c6bcc |
No files matched your search
@@ -10657,6 +10657,9 @@ msgstr ""
|
||||
msgid "Send Failed"
|
||||
msgstr ""
|
||||
|
||||
msgid "No available printers"
|
||||
msgstr ""
|
||||
|
||||
msgid "The name length exceeds the limit."
|
||||
msgstr ""
|
||||
|
||||
|
||||
@@ -377,7 +377,8 @@ public:
|
||||
virtual void set(const ConfigOption* rhs, size_t start, size_t len) = 0;
|
||||
virtual void set_with_restore(const ConfigOptionVectorBase* rhs, std::vector<int>& restore_index, int stride) = 0;
|
||||
virtual void set_with_restore_2(const ConfigOptionVectorBase* rhs, std::vector<int>& restore_index, int start, int len, bool skip_error = false) = 0;
|
||||
virtual void set_only_diff(const ConfigOptionVectorBase* rhs, std::vector<int>& diff_index, int stride) = 0;
|
||||
// keep_nil: copy a nil column from rhs as well, for options whose nil means "disabled" rather than "inherit".
|
||||
virtual void set_only_diff(const ConfigOptionVectorBase* rhs, std::vector<int>& diff_index, int stride, bool keep_nil = false) = 0;
|
||||
virtual void set_to_index(const ConfigOptionVectorBase* rhs, std::vector<int>& dest_index, int stride) = 0;
|
||||
virtual void set_with_nil(const ConfigOptionVectorBase* rhs, const ConfigOptionVectorBase* inherits, int stride) = 0;
|
||||
// Resize the vector of values, copy the newly added values from opt_default if provided.
|
||||
@@ -580,7 +581,7 @@ public:
|
||||
//set a item related with extruder variants when loading user config, only set the different value of some extruder
|
||||
//rhs: item from user config
|
||||
//diff_index: which index in this vector need to be set
|
||||
virtual void set_only_diff(const ConfigOptionVectorBase* rhs, std::vector<int>& diff_index, int stride) override
|
||||
virtual void set_only_diff(const ConfigOptionVectorBase* rhs, std::vector<int>& diff_index, int stride, bool keep_nil) override
|
||||
{
|
||||
if (rhs->type() == this->type()) {
|
||||
// Assign the first value of the rhs vector.
|
||||
@@ -592,7 +593,7 @@ public:
|
||||
for (size_t i = 0; i < diff_index.size(); i++) {
|
||||
if (diff_index[i] != -1) {
|
||||
for (size_t j = 0; j < stride; j++) {
|
||||
if (!other->is_nil(diff_index[i] * stride))
|
||||
if (keep_nil || !other->is_nil(diff_index[i] * stride))
|
||||
this->values[i * stride + j] = other->values[diff_index[i] * stride + j];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -988,7 +988,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
|
||||
MetadataList metadata;
|
||||
VolumeMetadataList volumes;
|
||||
bool auto_drop { true };
|
||||
std::optional<bool> auto_drop;
|
||||
};
|
||||
|
||||
struct CutObjectInfo
|
||||
@@ -1208,6 +1208,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
//IdToGeometryMap m_orig_geometries; // backup & restore
|
||||
CurrentConfig m_curr_config;
|
||||
IdToMetadataMap m_objects_metadata;
|
||||
std::set<int> m_items_auto_drop_off; // object ids with a build item auto_drop="0" (2.4.x layout)
|
||||
IdToCutObjectInfoMap m_cut_object_infos;
|
||||
IdToLayerHeightsProfileMap m_layer_heights_profiles;
|
||||
IdToLayerConfigRangesMap m_layer_config_ranges;
|
||||
@@ -1463,6 +1464,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
m_objects.clear();
|
||||
m_instances.clear();
|
||||
m_objects_metadata.clear();
|
||||
m_items_auto_drop_off.clear();
|
||||
m_curr_metadata_name.clear();
|
||||
m_curr_characters.clear();
|
||||
|
||||
@@ -1502,6 +1504,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
m_curr_config.object_id = -1;
|
||||
m_curr_config.volume_id = -1;
|
||||
m_objects_metadata.clear();
|
||||
m_items_auto_drop_off.clear();
|
||||
m_layer_heights_profiles.clear();
|
||||
m_layer_config_ranges.clear();
|
||||
m_brim_ear_points.clear();
|
||||
@@ -2257,11 +2260,14 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
ObjectMetadata::VolumeMetadataList volumes;
|
||||
ObjectMetadata::VolumeMetadataList* volumes_ptr = nullptr;
|
||||
|
||||
// 2.4.x stored auto_drop on build items, shared by all instances of an object; any "off" wins
|
||||
const bool legacy_auto_drop = !m_items_auto_drop_off.count(object.first.second);
|
||||
|
||||
IdToMetadataMap::iterator obj_metadata = m_objects_metadata.find(object.first.second);
|
||||
if (obj_metadata != m_objects_metadata.end()) {
|
||||
// config data has been found, this model was saved using slic3r pe
|
||||
|
||||
model_object->auto_drop = obj_metadata->second.auto_drop;
|
||||
model_object->auto_drop = obj_metadata->second.auto_drop.value_or(legacy_auto_drop);
|
||||
|
||||
// apply object's name and config data
|
||||
for (const Metadata& metadata : obj_metadata->second.metadata) {
|
||||
@@ -2279,6 +2285,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
}
|
||||
else {
|
||||
// config data not found, this model was not saved using slic3r pe
|
||||
model_object->auto_drop = legacy_auto_drop;
|
||||
|
||||
// add the entire geometry as the single volume to generate
|
||||
//volumes.emplace_back(0, (int)obj_geometry->second.triangles.size() - 1);
|
||||
@@ -4121,6 +4128,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
std::string path = bbs_get_attribute_value_string(attributes, num_attributes, PPATH_ATTR);
|
||||
Transform3d transform = bbs_get_transform_from_3mf_specs_string(bbs_get_attribute_value_string(attributes, num_attributes, TRANSFORM_ATTR));
|
||||
int printable = bbs_get_attribute_value_bool(attributes, num_attributes, PRINTABLE_ATTR);
|
||||
if (const char* auto_drop = bbs_get_attribute_value_charptr(attributes, num_attributes, AUTO_DROP_ATTR); auto_drop != nullptr && !::atoi(auto_drop))
|
||||
m_items_auto_drop_off.insert(object_id);
|
||||
return !m_load_model || _create_object_instance(path, object_id, transform, printable, 1);
|
||||
}
|
||||
|
||||
@@ -4489,7 +4498,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
// int instances_count_id = bbs_get_attribute_value_int(attributes, num_attributes, INSTANCESCOUNT_ATTR);
|
||||
|
||||
ObjectMetadata metadata;
|
||||
metadata.auto_drop = bbs_get_attribute_value_bool(attributes, num_attributes, AUTO_DROP_ATTR);
|
||||
if (bbs_get_attribute_value_charptr(attributes, num_attributes, AUTO_DROP_ATTR) != nullptr)
|
||||
metadata.auto_drop = bbs_get_attribute_value_bool(attributes, num_attributes, AUTO_DROP_ATTR);
|
||||
m_objects_metadata.insert({ object_id, std::move(metadata) });
|
||||
m_curr_config.object_id = object_id;
|
||||
return true;
|
||||
|
||||
@@ -797,7 +797,7 @@ bool Preset::save(DynamicPrintConfig* parent_config)
|
||||
{
|
||||
ConfigOption *opt_src = config.option(option);
|
||||
ConfigOption *opt_dst = temp_config.option(option, true);
|
||||
if (opt_dst->is_scalar() || !(opt_dst->nullable()))
|
||||
if (opt_dst->is_scalar() || !(opt_dst->nullable()) || is_filament_extruder_override_key(option))
|
||||
opt_dst->set(opt_src);
|
||||
else {
|
||||
ConfigOptionVectorBase* opt_vec_src = static_cast<ConfigOptionVectorBase*>(opt_src);
|
||||
@@ -2131,7 +2131,7 @@ Preset* PresetCollection::get_preset_differed_for_save(Preset& preset)
|
||||
{
|
||||
ConfigOption *opt_src = preset.config.option(option);
|
||||
ConfigOption *opt_dst = temp_config.option(option, true);
|
||||
if (opt_dst->is_scalar() || !(opt_dst->nullable()))
|
||||
if (opt_dst->is_scalar() || !(opt_dst->nullable()) || is_filament_extruder_override_key(option))
|
||||
opt_dst->set(opt_src);
|
||||
else {
|
||||
ConfigOptionVectorBase* opt_vec_src = static_cast<ConfigOptionVectorBase*>(opt_src);
|
||||
@@ -2198,9 +2198,12 @@ int PresetCollection::get_differed_values_to_update(Preset& preset, std::map<std
|
||||
// option's non-nil default (e.g. filament_retract_before_wipe -> 100%), silently
|
||||
// resurrecting an override the user turned off. See GitHub issue on Retract Before Wipe.
|
||||
if (m_type == Preset::TYPE_FILAMENT) {
|
||||
for (const std::string& opt_key : filament_extruder_override_keys) {
|
||||
if (key_values.count(opt_key))
|
||||
continue; // already carried by the diff
|
||||
for (const std::string& opt_key : preset.config.keys()) {
|
||||
if (!is_filament_extruder_override_key(opt_key) || key_values.count(opt_key))
|
||||
continue; // not an override, or already carried by the diff
|
||||
const ConfigOptionDef *def = print_config_def.get(opt_key);
|
||||
if (def == nullptr || !def->default_value || def->default_value->is_nil())
|
||||
continue; // the option's default is nil too, so an absent key cannot come back as a value
|
||||
const auto* opt_vec = dynamic_cast<const ConfigOptionVectorBase*>(preset.config.option(opt_key));
|
||||
if (opt_vec == nullptr)
|
||||
continue;
|
||||
|
||||
@@ -756,7 +756,14 @@ public:
|
||||
Preset& get_edited_preset() { return m_edited_preset; }
|
||||
const Preset& get_edited_preset() const { return m_edited_preset; }
|
||||
|
||||
const Preset& get_selected_preset_base() const { return *get_preset_base(m_presets[m_idx_selected]); }
|
||||
// The root of the selected preset's inherits chain, or the selected preset itself when
|
||||
// the chain names a preset that is not loaded (e.g. a vendor that is not installed).
|
||||
const Preset& get_selected_preset_base() const
|
||||
{
|
||||
const Preset &selected = m_presets[m_idx_selected];
|
||||
const Preset *base = get_preset_base(selected);
|
||||
return base ? *base : selected;
|
||||
}
|
||||
|
||||
// Return the last saved preset.
|
||||
// const Preset& get_saved_preset() const { return m_saved_preset; }
|
||||
|
||||
@@ -119,12 +119,18 @@ const std::vector<std::string> filament_extruder_override_keys = {
|
||||
};
|
||||
|
||||
// Some filament override parameters are generated from filament_extruder_override_keys,
|
||||
// while filament_retract_length_nc is defined separately. Keep the generator list
|
||||
// unchanged and use this helper for behavior checks that need the full override set.
|
||||
// while filament_retract_length_nc and the ironing overrides are defined separately.
|
||||
// Keep the generator list unchanged and use this helper for behavior checks that need
|
||||
// the full override set. A nil in one of these keys means the override is disabled, so
|
||||
// child presets store and load it verbatim instead of treating nil as "inherit".
|
||||
bool is_filament_extruder_override_key(const std::string &opt_key)
|
||||
{
|
||||
return std::find(filament_extruder_override_keys.begin(), filament_extruder_override_keys.end(), opt_key) != filament_extruder_override_keys.end() ||
|
||||
opt_key == "filament_retract_length_nc";
|
||||
opt_key == "filament_retract_length_nc" ||
|
||||
opt_key == "filament_ironing_flow" ||
|
||||
opt_key == "filament_ironing_spacing" ||
|
||||
opt_key == "filament_ironing_inset" ||
|
||||
opt_key == "filament_ironing_speed";
|
||||
}
|
||||
|
||||
size_t get_extruder_index(const GCodeConfig& config, unsigned int filament_id)
|
||||
@@ -12255,7 +12261,8 @@ void DynamicPrintConfig::update_diff_values_to_child_config(DynamicPrintConfig&
|
||||
opt_src->set(opt_target);
|
||||
}
|
||||
else
|
||||
opt_vec_src->set_only_diff(opt_vec_dest, variant_index, stride);
|
||||
// An override's nil means "disabled" and is the child's value; every other nil column inherits.
|
||||
opt_vec_src->set_only_diff(opt_vec_dest, variant_index, stride, is_filament_extruder_override_key(opt));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3151,7 +3151,7 @@ void CreatePrinterPresetDialog::set_current_visible_printer()
|
||||
m_printer_name_to_preset.clear();
|
||||
for (const Preset &printer_preset : printer_presets) {
|
||||
if (!printer_preset.is_visible) continue;
|
||||
if (preset_bundle->printers.get_preset_base(printer_preset)->name != printer_preset.name) continue;
|
||||
if (preset_bundle->printers.get_preset_base(printer_preset) != &printer_preset) continue;
|
||||
if (auto printer_model = dynamic_cast<ConfigOptionString*>(const_cast<Preset&>(printer_preset).config.option("printer_model", false))) {
|
||||
if (m_printer_name_to_preset.find(printer_model->value) == m_printer_name_to_preset.end()) {
|
||||
printer_choice.push_back(from_u8(printer_model->value));
|
||||
@@ -4962,7 +4962,7 @@ void CreatePresetForPrinterDialog::get_visible_printer_and_compatible_filament_p
|
||||
if (filament_preset.is_default || !filament_preset.is_compatible || filament_preset.is_project_embedded) continue;
|
||||
ConfigOptionStrings *filament_types;
|
||||
const Preset * filament_preset_base = m_preset_bundle->filaments.get_preset_base(filament_preset);
|
||||
if (filament_preset_base == &filament_preset) {
|
||||
if (filament_preset_base == &filament_preset || filament_preset_base == nullptr) {
|
||||
filament_types = dynamic_cast<ConfigOptionStrings *>(const_cast<Preset *>(&filament_preset)->config.option("filament_type"));
|
||||
} else {
|
||||
filament_types = dynamic_cast<ConfigOptionStrings *>(const_cast<Preset *>(filament_preset_base)->config.option("filament_type"));
|
||||
|
||||
@@ -4087,7 +4087,7 @@ void SelectMachineDialog::update_user_printer()
|
||||
{
|
||||
m_printer_last_select = "";
|
||||
update_select_layout(nullptr);
|
||||
m_printer_box->GetPrinterComboBox()->SetTextLabel("");
|
||||
m_printer_box->GetPrinterComboBox()->SetValue(_L("No available printers"));
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "for send task, current printer id = " << m_printer_last_select << std::endl;
|
||||
|
||||
@@ -1229,7 +1229,7 @@ void SendToPrinterDialog::update_user_printer()
|
||||
}
|
||||
else {
|
||||
m_printer_last_select = "";
|
||||
m_comboBox_printer->SetTextLabel("");
|
||||
m_comboBox_printer->SetValue(_L("No available printers"));
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "for send task, current printer id = " << m_printer_last_select << std::endl;
|
||||
|
||||
@@ -287,7 +287,7 @@ void ComboBox::DoDeleteOneItem(unsigned int pos)
|
||||
|
||||
void ComboBox::ForceDropdownOpen()
|
||||
{
|
||||
if (!IsEnabled())
|
||||
if (!IsEnabled() || items.empty())
|
||||
return;
|
||||
|
||||
if (!drop_down) {
|
||||
@@ -398,7 +398,7 @@ void ComboBox::mouseDown(wxMouseEvent &event)
|
||||
SetFocus();
|
||||
if (drop_down) {
|
||||
drop.Hide();
|
||||
} else if (drop.HasDismissLongTime()) {
|
||||
} else if (!items.empty() && drop.HasDismissLongTime()) { // DropDown paints nothing for an empty list
|
||||
drop.autoPosition();
|
||||
drop_down = true;
|
||||
drop.Popup(&drop);
|
||||
@@ -426,7 +426,7 @@ void ComboBox::keyDown(wxKeyEvent& event)
|
||||
case WXK_SPACE:
|
||||
if (drop_down) {
|
||||
drop.DismissAndNotify();
|
||||
} else if (drop.HasDismissLongTime()) {
|
||||
} else if (!items.empty() && drop.HasDismissLongTime()) {
|
||||
drop.autoPosition();
|
||||
drop_down = true;
|
||||
drop.Popup();
|
||||
|
||||
@@ -584,6 +584,19 @@ bool PythonInterpreter::initialize()
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(debug) << "Program name set successfully";
|
||||
|
||||
// Keep bytecode out of the Python home. The shipped .pyc files are stale against their
|
||||
// sources, so a writable install would rewrite them inside the app (breaking a signed
|
||||
// macOS bundle's seal) and a read-only one would recompile them on every launch.
|
||||
const std::string pycache_prefix = data_dir() + "/python/pycache";
|
||||
#ifdef _WIN32
|
||||
status = PyConfig_SetString(&config, &config.pycache_prefix, boost::nowide::widen(pycache_prefix).c_str());
|
||||
#else
|
||||
status = PyConfig_SetBytesString(&config, &config.pycache_prefix, pycache_prefix.c_str());
|
||||
#endif
|
||||
if (PyStatus_Exception(status))
|
||||
BOOST_LOG_TRIVIAL(warning) << "Failed to set the Python bytecode cache to " << pycache_prefix << ": "
|
||||
<< (status.err_msg ? status.err_msg : "unknown error");
|
||||
|
||||
// Use pybind11's scoped_interpreter which properly initializes
|
||||
// pybind11 internals for multi-threaded use — raw Py_InitializeFromConfig
|
||||
// does not set up thread state tracking that pybind11 requires.
|
||||
|
||||
@@ -505,6 +505,28 @@ TEST_CASE("A project whose components reference themselves fails to load", "[3mf
|
||||
REQUIRE_FALSE(loaded);
|
||||
}
|
||||
|
||||
TEST_CASE("A project in the 2.4.x layout keeps auto drop off from its build item", "[3mf][Regression]")
|
||||
{
|
||||
// 2.4.x wrote auto_drop on the build item and left it off the object, so the object is re-laid out here.
|
||||
ScopedTemporaryFile temp(".3mf");
|
||||
{
|
||||
Model model;
|
||||
ModelObject* object = model.add_object();
|
||||
object->add_volume(make_cube(10., 10., 10.));
|
||||
object->add_instance();
|
||||
object->auto_drop = false;
|
||||
store_project(model, temp.string(), SaveStrategy::Zip64 | SaveStrategy::Silence);
|
||||
}
|
||||
REQUIRE(replace_in_3mf_entry(temp.string(), "model_settings.config", " auto_drop=\"0\"", ""));
|
||||
REQUIRE(replace_in_3mf_entry(temp.string(), "3dmodel.model", "printable=\"1\"/>", "printable=\"1\" auto_drop=\"0\"/>"));
|
||||
|
||||
ScopedTemporaryDir backup_dir("orca_auto_drop_dst");
|
||||
Model model;
|
||||
REQUIRE(load_project(temp.string(), model, backup_dir));
|
||||
REQUIRE(model.objects.size() == 1);
|
||||
CHECK_FALSE(model.objects.front()->auto_drop);
|
||||
}
|
||||
|
||||
TEST_CASE("An object loads up to the component reference budget and fails past it", "[3mf][Regression]")
|
||||
{
|
||||
// The importer queues at most 100000 component references per object. Every reference besides the
|
||||
|
||||
@@ -1665,3 +1665,75 @@ TEST_CASE("Default options of enum lists get their definition's keys map", "[Con
|
||||
}
|
||||
CHECK(enum_lists > 0);
|
||||
}
|
||||
|
||||
TEST_CASE("A disabled filament override survives the merge with a parent that sets it", "[Config][Variant]")
|
||||
{
|
||||
std::set<std::string> no_keys;
|
||||
Slic3r::DynamicPrintConfig parent;
|
||||
parent.set_deserialize_strict("filament_retract_before_wipe", "100%");
|
||||
parent.set_deserialize_strict("filament_ironing_flow", "15%");
|
||||
Slic3r::DynamicPrintConfig child;
|
||||
child.set_deserialize_strict("filament_retract_before_wipe", "nil");
|
||||
child.set_deserialize_strict("filament_ironing_flow", "nil");
|
||||
parent.update_diff_values_to_child_config(child, "", "filament_extruder_variant",
|
||||
Slic3r::filament_options_with_variant, no_keys);
|
||||
CHECK(parent.opt_serialize("filament_retract_before_wipe") == "nil");
|
||||
CHECK(parent.opt_serialize("filament_ironing_flow") == "nil");
|
||||
}
|
||||
|
||||
SCENARIO("A two-variant child's disabled override column is matched to the parent's variant by name", "[Config][Variant]")
|
||||
{
|
||||
std::set<std::string> no_keys;
|
||||
GIVEN("A two-variant filament parent") {
|
||||
Slic3r::DynamicPrintConfig parent;
|
||||
parent.set_deserialize_strict("filament_extruder_variant", "Direct Drive Standard;Direct Drive High Flow");
|
||||
parent.set_deserialize_strict("filament_retract_before_wipe", "100%,100%");
|
||||
parent.set_deserialize_strict("filament_flow_ratio", "1,1");
|
||||
WHEN("the child disables the override on one variant and leaves a plain option nil on it") {
|
||||
Slic3r::DynamicPrintConfig child;
|
||||
child.set_deserialize_strict("filament_extruder_variant", "Direct Drive Standard;Direct Drive High Flow");
|
||||
child.set_deserialize_strict("filament_retract_before_wipe", "nil,100%");
|
||||
child.set_deserialize_strict("filament_flow_ratio", "nil,1.1");
|
||||
parent.update_diff_values_to_child_config(child, "", "filament_extruder_variant",
|
||||
Slic3r::filament_options_with_variant, no_keys);
|
||||
THEN("the override column is disabled while the plain option's nil column inherits") {
|
||||
CHECK(parent.opt_serialize("filament_retract_before_wipe") == "nil,100%");
|
||||
CHECK(parent.opt_serialize("filament_flow_ratio") == "1,1.1");
|
||||
}
|
||||
}
|
||||
WHEN("the child lists the same variants in the other order") {
|
||||
Slic3r::DynamicPrintConfig child;
|
||||
child.set_deserialize_strict("filament_extruder_variant", "Direct Drive High Flow;Direct Drive Standard");
|
||||
child.set_deserialize_strict("filament_retract_before_wipe", "70%,nil");
|
||||
child.set_deserialize_strict("filament_flow_ratio", "1.1,nil");
|
||||
parent.update_diff_values_to_child_config(child, "", "filament_extruder_variant",
|
||||
Slic3r::filament_options_with_variant, no_keys);
|
||||
THEN("columns are matched by variant name, the disabled one included") {
|
||||
CHECK(parent.opt_serialize("filament_retract_before_wipe") == "nil,70%");
|
||||
CHECK(parent.opt_serialize("filament_flow_ratio") == "1,1.1");
|
||||
}
|
||||
}
|
||||
WHEN("the child was saved with a single variant") {
|
||||
Slic3r::DynamicPrintConfig child;
|
||||
child.set_deserialize_strict("filament_extruder_variant", "Direct Drive Standard");
|
||||
child.set_deserialize_strict("filament_retract_before_wipe", "nil");
|
||||
parent.update_diff_values_to_child_config(child, "", "filament_extruder_variant",
|
||||
Slic3r::filament_options_with_variant, no_keys);
|
||||
THEN("its one value applies to every variant of that extruder, as slicing does") {
|
||||
CHECK(parent.opt_serialize("filament_retract_before_wipe") == "nil,nil");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Every listed filament override is nullable", "[Config]")
|
||||
{
|
||||
std::set<std::string> overrides(Slic3r::filament_extruder_override_keys.begin(), Slic3r::filament_extruder_override_keys.end());
|
||||
overrides.insert({ "filament_retract_length_nc", "filament_ironing_flow", "filament_ironing_spacing", "filament_ironing_inset", "filament_ironing_speed" });
|
||||
for (const auto &[key, def] : Slic3r::print_config_def.options) {
|
||||
INFO(key);
|
||||
CHECK(Slic3r::is_filament_extruder_override_key(key) == (overrides.count(key) == 1));
|
||||
if (overrides.count(key) == 1)
|
||||
CHECK(def.nullable);
|
||||
}
|
||||
}
|
||||
@@ -1219,6 +1219,24 @@ TEST_CASE("find_preset2 auto-matches removed Generic vendor profiles to the libr
|
||||
CHECK(bundle.filaments.find_preset2("BrandX Generic PETG", /*auto_match=*/true) == nullptr);
|
||||
}
|
||||
|
||||
TEST_CASE("The selected preset is its own base when its parent is not loaded", "[Preset]")
|
||||
{
|
||||
PresetCollection printers(Preset::TYPE_PRINTER, Preset::printer_options(),
|
||||
static_cast<const PrintRegionConfig &>(FullPrintConfig::defaults()));
|
||||
add_inmemory_preset(printers, "Printer");
|
||||
add_inmemory_preset(printers, "User Printer", "Printer");
|
||||
add_inmemory_preset(printers, "Orphan Printer", "Uninstalled Vendor Printer");
|
||||
|
||||
printers.select_preset_by_name("User Printer", true);
|
||||
REQUIRE(printers.get_selected_preset_name() == "User Printer");
|
||||
CHECK(printers.get_selected_preset_base().name == "Printer");
|
||||
|
||||
printers.select_preset_by_name("Orphan Printer", true);
|
||||
REQUIRE(printers.get_selected_preset_name() == "Orphan Printer");
|
||||
REQUIRE(printers.get_preset_base(printers.get_selected_preset()) == nullptr);
|
||||
CHECK(printers.get_selected_preset_base().name == "Orphan Printer");
|
||||
}
|
||||
|
||||
TEST_CASE("Renamed parent is normalized into a loaded preset's inherits", "[Preset][Rename]")
|
||||
{
|
||||
ScopedTemporaryDir temp_dir;
|
||||
@@ -8399,3 +8417,58 @@ TEST_CASE("An empty choice list in a G-code's settings keeps the value it had be
|
||||
CHECK(config.opt_serialize("nozzle_volume_type") == "High Flow");
|
||||
CHECK(config.option<ConfigOptionStrings>("filament_multi_colour")->values.empty());
|
||||
}
|
||||
|
||||
TEST_CASE("A disabled filament override is saved and reloaded column by column", "[Preset][Bundle]")
|
||||
{
|
||||
ScopedPresetDataDir data_dir;
|
||||
PresetBundle bundle;
|
||||
|
||||
Preset &parent = add_inmemory_preset(bundle.filaments, "Parent PLA @System");
|
||||
parent.config.option<ConfigOptionStrings>("filament_extruder_variant", true)->values = { "Direct Drive Standard", "Direct Drive High Flow" };
|
||||
parent.config.option<ConfigOptionPercentsNullable>("filament_retract_before_wipe", true)->values = { 100., 100. };
|
||||
parent.config.option<ConfigOptionFloatsNullable>("filament_flow_ratio", true)->values = { 1., 1. };
|
||||
|
||||
// The child turns the override off on the first variant and changes a plain option on the second.
|
||||
const fs::path file = data_dir.tmp.path() / PRESET_FILAMENT_NAME / "Child PLA.json";
|
||||
Preset child(Preset::TYPE_FILAMENT, "Child PLA");
|
||||
child.file = file.string();
|
||||
child.version = *Semver::parse("1.0.0");
|
||||
child.config = parent.config;
|
||||
child.config.option<ConfigOptionString>(BBL_JSON_KEY_INHERITS, true)->value = parent.name;
|
||||
child.config.option<ConfigOptionPercentsNullable>("filament_retract_before_wipe")->set_at_to_nil(0);
|
||||
child.config.option<ConfigOptionFloatsNullable>("filament_flow_ratio")->values = { 1., 1.1 };
|
||||
REQUIRE(child.save(&parent.config));
|
||||
|
||||
// On disk the override keeps every column, so its nil can only mean "off"; a plain
|
||||
// option still stores nil for the columns that match the parent.
|
||||
DynamicPrintConfig saved;
|
||||
std::map<std::string, std::string> key_values;
|
||||
std::string reason;
|
||||
saved.load_from_json(file.string(), ForwardCompatibilitySubstitutionRule::EnableSilent, key_values, reason);
|
||||
REQUIRE(reason.empty());
|
||||
CHECK(saved.opt_serialize("filament_retract_before_wipe") == "nil,100%");
|
||||
CHECK(saved.opt_serialize("filament_flow_ratio") == "nil,1.1");
|
||||
|
||||
PresetsConfigSubstitutions substitutions;
|
||||
bundle.filaments.load_presets(data_dir.tmp.path().string(), PRESET_FILAMENT_NAME, substitutions,
|
||||
ForwardCompatibilitySubstitutionRule::EnableSilent);
|
||||
const Preset *loaded = bundle.filaments.find_preset("Child PLA");
|
||||
REQUIRE(loaded != nullptr);
|
||||
CHECK(loaded->config.opt_serialize("filament_retract_before_wipe") == "nil,100%");
|
||||
CHECK(loaded->config.opt_serialize("filament_flow_ratio") == "1,1.1");
|
||||
}
|
||||
|
||||
TEST_CASE("The cloud diff force-emits a disabled override only where the cloud would fill a value in", "[Preset][Bundle]")
|
||||
{
|
||||
PresetBundle bundle;
|
||||
add_inmemory_preset(bundle.filaments, "Parent PLA @System");
|
||||
Preset &child = add_inmemory_preset(bundle.filaments, "Child PLA", "Parent PLA @System");
|
||||
// Every override is nil in both parent and child, so none is in the plain diff.
|
||||
std::map<std::string, std::string> key_values;
|
||||
REQUIRE(bundle.filaments.get_differed_values_to_update(child, key_values) >= 0);
|
||||
// Retract Before Wipe defaults to 100%: an absent key would come back as 100%, so nil is sent.
|
||||
CHECK(key_values.count("filament_retract_before_wipe") == 1);
|
||||
CHECK(key_values["filament_retract_before_wipe"] == "nil");
|
||||
// An ironing override defaults to nil: an absent key stays nil, so it is not sent.
|
||||
CHECK(key_values.count("filament_ironing_flow") == 0);
|
||||
}
|
||||
@@ -150,6 +150,13 @@ public:
|
||||
return m_close_frames;
|
||||
}
|
||||
|
||||
// The printer answers a close frame before its own read completes, so the agent's stop can return first.
|
||||
bool wait_for_close_frames(int count)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
return m_changed.wait_for(lock, 10s, [&] { return m_close_frames >= count; });
|
||||
}
|
||||
|
||||
// Hangs up every connection that is waiting.
|
||||
void release()
|
||||
{
|
||||
@@ -277,6 +284,7 @@ private:
|
||||
if (ec == websocket::error::closed) {
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
++m_close_frames;
|
||||
m_changed.notify_all();
|
||||
}
|
||||
if (ec)
|
||||
reading = false;
|
||||
@@ -449,6 +457,7 @@ TEST_CASE("Stopping the status stream sends the printer a close frame", "[Moonra
|
||||
REQUIRE(f.printer.wait_for_ready_connections(1));
|
||||
|
||||
CHECK(f.stops_within(3s));
|
||||
CHECK(f.printer.wait_for_close_frames(1));
|
||||
CHECK(f.printer.close_frames() == 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -615,8 +615,22 @@ TEST_CASE("Python module release removes package submodules and owned sys.path",
|
||||
|
||||
// Ensure the next import executes the new submodule rather than reusing a stale package child.
|
||||
write_package("'new'");
|
||||
boost::system::error_code ec;
|
||||
fs::remove_all(package_root / "__pycache__", ec);
|
||||
{
|
||||
// The rewrite can keep the old mtime and size, so drop the bytecode wherever the
|
||||
// interpreter caches it.
|
||||
PythonGILState gil;
|
||||
REQUIRE(gil);
|
||||
PyObjectPtr util(PyImport_ImportModule("importlib.util"));
|
||||
REQUIRE(util);
|
||||
for (const char* file : {"__init__.py", "sub.py"}) {
|
||||
PyObjectPtr source(PyUnicode_DecodeFSDefault((package_root / file).string().c_str()));
|
||||
REQUIRE(source);
|
||||
PyObjectPtr cached(PyObject_CallMethod(util.get(), "cache_from_source", "O", source.get()));
|
||||
REQUIRE(cached);
|
||||
boost::system::error_code ec;
|
||||
fs::remove(fs::path(PyUnicode_AsUTF8(cached.get())), ec);
|
||||
}
|
||||
}
|
||||
|
||||
paths.clear();
|
||||
modules.clear();
|
||||
|
||||
Reference in new issue
Block a user