diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index 63d37315c4..82f7dc4f69 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -363,6 +363,7 @@ public: virtual void set_with_restore(const ConfigOptionVectorBase* rhs, std::vector& restore_index, int stride) = 0; virtual void set_with_restore_2(const ConfigOptionVectorBase* rhs, std::vector& restore_index, int start, int len, bool skip_error = false) = 0; virtual void set_only_diff(const ConfigOptionVectorBase* rhs, std::vector& diff_index, int stride) = 0; + virtual void set_to_index(const ConfigOptionVectorBase* rhs, std::vector& 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. virtual void resize(size_t n, const ConfigOption *opt_default = nullptr) = 0; @@ -586,6 +587,32 @@ public: throw ConfigurationError("ConfigOptionVector::set_only_diff(): Assigning an incompatible type"); } + //set a item related with extruder variants when apply static config with dynamic config + //rhs: item from dynamic config + //dest_index: which index in this vector need to be used + virtual void set_to_index(const ConfigOptionVectorBase* rhs, std::vector& dest_index, int stride) override + { + if (rhs->type() == this->type()) { + // Assign the first value of the rhs vector. + auto other = static_cast*>(rhs); + T v = other->values.front(); + this->values.resize(dest_index.size() * stride, v); + + for (size_t i = 0; i < dest_index.size(); i++) { + if (dest_index[i] < 0) + continue; + for (size_t j = 0; j < size_t(stride); j++) + { + const size_t src_idx = size_t(dest_index[i]) * size_t(stride) + j; + if (src_idx < other->values.size() && !other->is_nil(size_t(dest_index[i]) * size_t(stride))) + this->values[i * size_t(stride) + j] = other->values[src_idx]; + } + } + } + else + throw ConfigurationError("ConfigOptionVector::set_to_index(): Assigning an incompatible type"); + } + //set a item related with extruder variants when saving user config, set the non-diff value of some extruder to nill //this item has different value with inherit config //rhs: item from userconfig diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index bcbfe542eb..a64c2bc7f5 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -420,7 +420,7 @@ public: // (layer height, first layer height, raft settings, print nozzle diameter etc). const SlicingParameters& slicing_parameters() const { return m_slicing_params; } // Orca: XYZ shrinkage compensation has introduced the const Vec3d &object_shrinkage_compensation parameter to the function below - static SlicingParameters slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object, float object_max_z, const Vec3d &object_shrinkage_compensation); + static SlicingParameters slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object, float object_max_z, const Vec3d &object_shrinkage_compensation, std::vector variant_index = std::vector()); size_t num_printing_regions() const throw() { return m_shared_regions->all_regions.size(); } const PrintRegion& printing_region(size_t idx) const throw() { return *m_shared_regions->all_regions[idx].get(); } @@ -491,7 +491,7 @@ public: // If ! m_slicing_params.valid, recalculate. void update_slicing_parameters(); - static PrintObjectConfig object_config_from_model_object(const PrintObjectConfig &default_object_config, const ModelObject &object, size_t num_extruders); + static PrintObjectConfig object_config_from_model_object(const PrintObjectConfig &default_object_config, const ModelObject &object, size_t num_extruders, std::vector& variant_index); private: void make_perimeters(); diff --git a/src/libslic3r/PrintApply.cpp b/src/libslic3r/PrintApply.cpp index e4f421414a..aeed4a7769 100644 --- a/src/libslic3r/PrintApply.cpp +++ b/src/libslic3r/PrintApply.cpp @@ -727,7 +727,7 @@ PrintObjectRegions::BoundingBox find_modifier_volume_extents(const PrintObjectRe return out; } -PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig &default_or_parent_region_config, const DynamicPrintConfig *layer_range_config, const ModelVolume &volume, size_t num_extruders); +PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig &default_or_parent_region_config, const DynamicPrintConfig *layer_range_config, const ModelVolume &volume, size_t num_extruders, std::vector& variant_index); void print_region_ref_inc(PrintRegion &r) { ++ r.m_ref_cnt; } void print_region_ref_reset(PrintRegion &r) { r.m_ref_cnt = 0; } @@ -741,7 +741,8 @@ bool verify_update_print_object_regions( const PrintRegionConfig &default_region_config, size_t num_extruders, PrintObjectRegions &print_object_regions, - const std::function &callback_invalidate) + const std::function &callback_invalidate, + std::vector& variant_index) { // Sort by ModelVolume ID. model_volumes_sort_by_id(model_volumes); @@ -786,7 +787,7 @@ bool verify_update_print_object_regions( } else if (PrintObjectRegions::BoundingBox parent_bbox = find_modifier_volume_extents(layer_range, parent_region_id); parent_bbox.intersects(*bbox)) // Such parent region does not exist. If it is needed, then we need to reslice. // Only create new region for a modifier, which actually modifies config of it's parent. - if (PrintRegionConfig config = region_config_from_model_volume(parent_region.region->config(), nullptr, **it_model_volume, num_extruders); + if (PrintRegionConfig config = region_config_from_model_volume(parent_region.region->config(), nullptr, **it_model_volume, num_extruders, variant_index); config != parent_region.region->config()) // This modifier newly overrides a region, which it did not before. We need to reslice. return false; @@ -794,8 +795,8 @@ bool verify_update_print_object_regions( } } PrintRegionConfig cfg = region.parent == -1 ? - region_config_from_model_volume(default_region_config, layer_range.config, **it_model_volume, num_extruders) : - region_config_from_model_volume(layer_range.volume_regions[region.parent].region->config(), nullptr, **it_model_volume, num_extruders); + region_config_from_model_volume(default_region_config, layer_range.config, **it_model_volume, num_extruders, variant_index) : + region_config_from_model_volume(layer_range.volume_regions[region.parent].region->config(), nullptr, **it_model_volume, num_extruders, variant_index); if (cfg != region.region->config()) { // Region configuration changed. if (print_region_ref_cnt(*region.region) == 0) { @@ -967,6 +968,7 @@ static PrintObjectRegions* generate_print_object_regions( size_t num_extruders, const float xy_contour_compensation, const std::vector &painting_extruders, + std::vector &variant_index, const bool has_painted_fuzzy_skin) { // Reuse the old object or generate a new one. @@ -1025,7 +1027,7 @@ static PrintObjectRegions* generate_print_object_regions( // Add a model volume, assign an existing region or generate a new one. layer_range.volume_regions.push_back({ &volume, -1, - get_create_region(region_config_from_model_volume(default_region_config, layer_range.config, volume, num_extruders)), + get_create_region(region_config_from_model_volume(default_region_config, layer_range.config, volume, num_extruders, variant_index)), bbox }); } else if (volume.is_negative_volume()) { @@ -1042,7 +1044,7 @@ static PrintObjectRegions* generate_print_object_regions( if (parent_volume.is_model_part() || parent_volume.is_modifier()) if (PrintObjectRegions::BoundingBox parent_bbox = find_modifier_volume_extents(layer_range, parent_region_id); parent_bbox.intersects(*bbox)) { // Only create new region for a modifier, which actually modifies config of it's parent. - if (PrintRegionConfig config = region_config_from_model_volume(parent_region.region->config(), nullptr, volume, num_extruders); + if (PrintRegionConfig config = region_config_from_model_volume(parent_region.region->config(), nullptr, volume, num_extruders, variant_index); config != parent_region.region->config()) { added = true; layer_range.volume_regions.push_back({ &volume, parent_region_id, get_create_region(std::move(config)), bbox }); @@ -1165,6 +1167,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ } //apply extruder related values + std::vector print_variant_index; std::vector> nozzle_volume_types; int extruder_count = 1, extruder_volume_type_count = 1; bool different_extruder = false; @@ -1177,8 +1180,10 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ new_full_config.update_values_to_printer_extruders(new_full_config, extruder_count, extruder_volume_type_count, nozzle_volume_types, printer_options_with_variant_2, "printer_extruder_id", "printer_extruder_variant", 2); new_full_config.update_values_to_printer_extruders(new_full_config, extruder_count, extruder_volume_type_count, nozzle_volume_types, printer_options_with_variant_1, "printer_extruder_id", "printer_extruder_variant"); //update print config related with variants - new_full_config.update_values_to_printer_extruders(new_full_config, extruder_count, extruder_volume_type_count, nozzle_volume_types, print_options_with_variant, "print_extruder_id", "print_extruder_variant"); + print_variant_index = new_full_config.update_values_to_printer_extruders(new_full_config, extruder_count, extruder_volume_type_count, nozzle_volume_types, print_options_with_variant, "print_extruder_id", "print_extruder_variant"); } + else + print_variant_index.resize(1, 0); m_ori_full_print_config = new_full_config; @@ -1188,15 +1193,16 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ new_full_config.update_values_to_printer_extruders_for_multiple_filaments(m_ori_full_print_config, extruder_count, extruder_volume_type_count, filament_keys, "filament_self_index", "filament_extruder_variant"); } - // else { - // int extruder_count; - // bool different_extruder = new_full_config.support_different_extruders(extruder_count); - // print_variant_index.resize(extruder_count); - // for (int e_index = 0; e_index < extruder_count; e_index++) - // { - // print_variant_index[e_index] = e_index; - // } - // } + else { + //should not come here, we can not get the result of print_variant, for the values have been updated + //we just use the default values here + auto variant_opt = dynamic_cast(new_full_config.option("printer_extruder_variant")); + print_variant_index.resize(variant_opt->values.size()); + for (int e_index = 0; e_index < variant_opt->values.size(); e_index++) + { + print_variant_index[e_index] = e_index; + } + } auto opt_filament_map = new_full_config.option("filament_map"); std::vector filament_maps = opt_filament_map ? opt_filament_map->values : std::vector(); @@ -1567,7 +1573,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ if (object_config_changed) model_object.config.assign_config(model_object_new.config); if (! object_diff.empty() || object_config_changed || num_extruders_changed ) { - PrintObjectConfig new_config = PrintObject::object_config_from_model_object(m_default_object_config, model_object, num_extruders ); + PrintObjectConfig new_config = PrintObject::object_config_from_model_object(m_default_object_config, model_object, num_extruders, print_variant_index); for (const PrintObjectStatus &print_object_status : print_object_status_db.get_range(model_object)) { t_config_option_keys diff = print_object_status.print_object->config().diff(new_config); if (! diff.empty()) { @@ -1633,10 +1639,10 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ // Generate a list of trafos and XY offsets for instances of a ModelObject // Producing the config for PrintObject on demand, caching it at print_object_last. const PrintObject *print_object_last = nullptr; - auto print_object_apply_config = [this, &print_object_last, model_object, num_extruders ](PrintObject *print_object) { + auto print_object_apply_config = [this, &print_object_last, model_object, num_extruders, &print_variant_index](PrintObject *print_object) { print_object->config_apply(print_object_last ? print_object_last->config() : - PrintObject::object_config_from_model_object(m_default_object_config, *model_object, num_extruders )); + PrintObject::object_config_from_model_object(m_default_object_config, *model_object, num_extruders, print_variant_index)); print_object_last = print_object; }; if (old.empty()) { @@ -1814,7 +1820,8 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ for (auto it = it_print_object; it != it_print_object_end; ++it) if ((*it)->m_shared_regions != nullptr) update_apply_status((*it)->invalidate_state_by_config_options(old_config, new_config, diff_keys)); - })) { + }, + print_variant_index)) { // Regions are valid, just keep them. } else { // Regions were reshuffled. @@ -1836,6 +1843,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ num_extruders , print_object.is_mm_painted() ? 0.f : float(print_object.config().xy_contour_compensation.value), painting_extruders, + print_variant_index, print_object.is_fuzzy_skin_painted()); } for (auto it = it_print_object; it != it_print_object_end; ++it) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index fe48dcc79a..b495738e32 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -10110,7 +10110,7 @@ int DynamicPrintConfig::update_values_from_multi_to_multi_2(const std::vector::max(); for(auto idx : indices){ - if(opt && !opt->is_nil(idx)){ + if(opt && idx < opt->values.size() && !opt->is_nil(idx)){ has_value = true; target_value = std::min(target_value, src_values[idx]); } @@ -11068,6 +11068,28 @@ void compute_filament_override_value(const std::string& opt_key, const ConfigOpt } +void update_static_print_config_from_dynamic(ConfigBase& config, const DynamicPrintConfig& dest_config, std::vector variant_index, std::set& key_set1, int stride) +{ + if (variant_index.size() > 0) { + const t_config_option_keys &keys = dest_config.keys(); + for (auto& opt : keys) { + ConfigOption *opt_src = config.option(opt); + const ConfigOption *opt_dest = dest_config.option(opt); + if (opt_src && opt_dest && (*opt_src != *opt_dest)) { + if (opt_dest->is_scalar() || (key_set1.find(opt) == key_set1.end())) + opt_src->set(opt_dest); + else { + ConfigOptionVectorBase* opt_vec_src = static_cast(opt_src); + const ConfigOptionVectorBase* opt_vec_dest = static_cast(opt_dest); + opt_vec_src->set_to_index(opt_vec_dest, variant_index, stride); + } + } + } + } + else + config.apply(dest_config, true); +} + //BBS: pass map to recording all invalid valies //FIXME localize this function. std::map validate(const FullPrintConfig &cfg, bool under_cli) diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 9c19c23915..ab5e5797a5 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -773,6 +773,7 @@ extern std::set printer_options_with_variant_1; extern std::set printer_options_with_variant_2; extern std::set empty_options; +extern void update_static_print_config_from_dynamic(ConfigBase& config, const DynamicPrintConfig& dest_config, std::vector variant_index, std::set& key_set1, int stride = 1); extern void compute_filament_override_value(const std::string& opt_key, const ConfigOption *opt_old_machine, const ConfigOption *opt_new_machine, const ConfigOption *opt_new_filament, const DynamicPrintConfig& new_full_config, t_config_option_keys& diff_keys, DynamicPrintConfig& filament_overrides, std::vector& f_map_indices); diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index d977a5419b..9d829b2ccf 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -98,7 +98,7 @@ PrintObject::PrintObject(Print* print, ModelObject* model_object, const Transfor // snug height and an approximate bounding box in XY. BoundingBoxf3 bbox = model_object->raw_bounding_box(); Vec3d bbox_center = bbox.center(); - + // We may need to rotate the bbox / bbox_center from the original instance to the current instance. double z_diff = Geometry::rotation_diff_z(model_object->instances.front()->get_rotation(), instances.front().model_instance->get_rotation()); if (std::abs(z_diff) > EPSILON) { @@ -2257,7 +2257,7 @@ void PrintObject::discover_vertical_shells() #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ Flow solid_infill_flow = layerm->flow(frSolidInfill); - coord_t infill_line_spacing = solid_infill_flow.scaled_spacing(); + coord_t infill_line_spacing = solid_infill_flow.scaled_spacing(); // Find a union of perimeters below / above this surface to guarantee a minimum shell thickness. Polygons shell; Polygons holes; @@ -2299,7 +2299,7 @@ void PrintObject::discover_vertical_shells() shell = std::move(shells2); else if (! shells2.empty()) { polygons_append(shell, shells2); - // Running the union_ using the Clipper library piece by piece is cheaper + // Running the union_ using the Clipper library piece by piece is cheaper // than running the union_ all at once. shell = union_(shell); } @@ -2366,12 +2366,12 @@ void PrintObject::discover_vertical_shells() Slic3r::SVG svg(debug_out_path("discover_vertical_shells-perimeters-before-union-%d.svg", debug_idx), get_extents(shell)); svg.draw(shell); svg.draw_outline(shell, "black", scale_(0.05)); - svg.Close(); + svg.Close(); } #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ #if 0 // shell = union_(shell, true); - shell = union_(shell, false); + shell = union_(shell, false); #endif #ifdef SLIC3R_DEBUG_SLICE_PROCESSING shell_ex = union_safety_offset_ex(shell); @@ -2675,7 +2675,7 @@ void PrintObject::bridge_over_infill() } } - // LIGHTNING INFILL SECTION - If lightning infill is used somewhere, we check the areas that are going to be bridges, and those that rely on the + // LIGHTNING INFILL SECTION - If lightning infill is used somewhere, we check the areas that are going to be bridges, and those that rely on the // lightning infill under them get expanded. This somewhat helps to ensure that most of the extrusions are anchored to the lightning infill at the ends. // It requires modifying this instance of print object in a specific way, so that we do not invalidate the pointers in our surfaces_by_layer structure. if (has_lightning_infill) { @@ -3650,13 +3650,13 @@ static void clamp_feature_filament_to_valid(ConfigOptionInt &opt, size_t num_ext opt.value = 1; } -PrintObjectConfig PrintObject::object_config_from_model_object(const PrintObjectConfig &default_object_config, const ModelObject &object, size_t num_extruders) +PrintObjectConfig PrintObject::object_config_from_model_object(const PrintObjectConfig &default_object_config, const ModelObject &object, size_t num_extruders, std::vector& variant_index) { PrintObjectConfig config = default_object_config; { DynamicPrintConfig src_normalized(object.config.get()); src_normalized.normalize_fdm(); - config.apply(src_normalized, true); + update_static_print_config_from_dynamic(config, src_normalized, variant_index, print_options_with_variant, 1); } // Clamp invalid extruders to the default extruder (with index 1). clamp_exturder_to_default(config.support_filament, num_extruders); @@ -3684,7 +3684,7 @@ struct FeatureFilamentOverrideMask bool inner_wall_filament_id = false; }; -static void apply_to_print_region_config(PrintRegionConfig &out, const DynamicPrintConfig &in, FeatureFilamentOverrideMask &feature_overrides) +static void apply_to_print_region_config(PrintRegionConfig &out, const DynamicPrintConfig &in, FeatureFilamentOverrideMask &feature_overrides, std::vector& variant_index) { // 1) Explicit feature filament values take precedence over base extruder fallback. auto *opt_extruder = in.opt(key_extruder); @@ -3725,8 +3725,18 @@ static void apply_to_print_region_config(PrintRegionConfig &out, const DynamicPr else if (it->first == "inner_wall_filament_id") feature_overrides.inner_wall_filament_id = false; } - } else - my_opt->set(it->second.get()); + } else { + if (*my_opt != *(it->second)) { + if (my_opt->is_scalar() || variant_index.empty() || (print_options_with_variant.find(it->first) == print_options_with_variant.end())) + my_opt->set(it->second.get()); + //my_opt->set(it->second.get()); + else { + ConfigOptionVectorBase* opt_vec_src = static_cast(my_opt); + const ConfigOptionVectorBase* opt_vec_dest = static_cast(it->second.get()); + opt_vec_src->set_to_index(opt_vec_dest, variant_index, 1); + } + } + } } // 3) Apply base extruder only to features that were not explicitly overridden. @@ -3746,7 +3756,7 @@ static void apply_to_print_region_config(PrintRegionConfig &out, const DynamicPr } } -PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig &default_or_parent_region_config, const DynamicPrintConfig *layer_range_config, const ModelVolume &volume, size_t num_extruders) +PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig &default_or_parent_region_config, const DynamicPrintConfig *layer_range_config, const ModelVolume &volume, size_t num_extruders, std::vector& variant_index) { PrintRegionConfig config = default_or_parent_region_config; FeatureFilamentOverrideMask feature_overrides; @@ -3764,17 +3774,17 @@ PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig &defau if (volume.is_model_part()) { // default_or_parent_region_config contains the Print's PrintRegionConfig. // Override with ModelObject's PrintRegionConfig values. - apply_to_print_region_config(config, volume.get_object()->config.get(), feature_overrides); + apply_to_print_region_config(config, volume.get_object()->config.get(), feature_overrides, variant_index); } else { // default_or_parent_region_config contains parent PrintRegion config, which already contains ModelVolume's config. } - apply_to_print_region_config(config, volume.config.get(), feature_overrides); + apply_to_print_region_config(config, volume.config.get(), feature_overrides, variant_index); if (! volume.material_id().empty()) - apply_to_print_region_config(config, volume.material()->config.get(), feature_overrides); + apply_to_print_region_config(config, volume.material()->config.get(), feature_overrides, variant_index); if (layer_range_config != nullptr) { // Not applicable to modifiers. assert(volume.is_model_part()); - apply_to_print_region_config(config, *layer_range_config, feature_overrides); + apply_to_print_region_config(config, *layer_range_config, feature_overrides, variant_index); } // Resolve feature defaults and clamp invalid extruders to index 1. clamp_feature_filament_to_valid(config.sparse_infill_filament_id, num_extruders); @@ -3824,7 +3834,7 @@ void PrintObject::update_slicing_parameters() } // Orca: XYZ shrinkage compensation has introduced the const Vec3d &object_shrinkage_compensation parameter to the function below -SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object, float object_max_z, const Vec3d &object_shrinkage_compensation) +SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object, float object_max_z, const Vec3d &object_shrinkage_compensation, std::vector variant_index) { PrintConfig print_config; PrintObjectConfig object_config; @@ -3834,14 +3844,14 @@ SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig &full default_region_config.apply(full_config, true); // BBS size_t filament_extruders = print_config.filament_diameter.size(); - object_config = object_config_from_model_object(object_config, model_object, filament_extruders); + object_config = object_config_from_model_object(object_config, model_object, filament_extruders, variant_index); std::vector object_extruders; for (const ModelVolume* model_volume : model_object.volumes) if (model_volume->is_model_part()) { PrintRegion::collect_object_printing_extruders( print_config, - region_config_from_model_volume(default_region_config, nullptr, *model_volume, filament_extruders), + region_config_from_model_volume(default_region_config, nullptr, *model_volume, filament_extruders, variant_index), object_config.brim_type != btNoBrim && object_config.brim_width > 0., object_extruders); for (const std::pair &range_and_config : model_object.layer_config_ranges) @@ -3853,7 +3863,7 @@ SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig &full range_and_config.second.has("bottom_surface_filament_id")) PrintRegion::collect_object_printing_extruders( print_config, - region_config_from_model_volume(default_region_config, &range_and_config.second.get(), *model_volume, filament_extruders), + region_config_from_model_volume(default_region_config, &range_and_config.second.get(), *model_volume, filament_extruders, variant_index), object_config.brim_type != btNoBrim && object_config.brim_width > 0., object_extruders); } diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index 42fe6b4ce1..1cb0cf6148 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -936,6 +936,7 @@ bool TextCtrl::value_was_changed() case coString: case coStrings: case coFloatOrPercent: + case coFloatsOrPercents: return boost::any_cast(m_value) != boost::any_cast(val); default: return true; diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index db5725bb13..496bcbd30f 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1183,7 +1183,7 @@ void Tab::update_extruder_switch_colors() void Tab::check_extruder_options_status(int index, bool &sys_extruder, bool &modified_extruder, const std::vector& pages_to_check) { int config_index = index; - if (m_type == Preset::TYPE_PRINT || m_type == Preset::TYPE_PRINTER) { + if (m_type == Preset::TYPE_PRINT || m_type == Preset::TYPE_PRINTER || m_type == Preset::TYPE_MODEL) { int extruder_id; NozzleVolumeType nozzle_type; parse_extruder_selection(index, extruder_id, nozzle_type); diff --git a/tests/libslic3r/test_config.cpp b/tests/libslic3r/test_config.cpp index cd807ca998..3a4ff3a44b 100644 --- a/tests/libslic3r/test_config.cpp +++ b/tests/libslic3r/test_config.cpp @@ -436,3 +436,212 @@ TEST_CASE("H2C/A2L-era multi-nozzle and pre-heat config keys exist", "[config]") REQUIRE(config.option("filament_pre_cooling_temperature_nc")->values == std::vector{0}); REQUIRE(config.option("filament_ramming_volumetric_speed")->values == std::vector{-1}); } + +SCENARIO("ConfigOptionVector::set_to_index with stride=1 copies values correctly", "[Config][set_to_index]") { + GIVEN("A destination vector and a source vector with 3 values") { + Slic3r::ConfigOptionFloats dest({0.0}); + Slic3r::ConfigOptionFloats src({10.0, 20.0, 30.0}); + std::vector variant_index = {0, 1, 2}; + int stride = 1; + + WHEN("set_to_index is called with stride=1") { + dest.set_to_index(&src, variant_index, stride); + + THEN("The destination contains the source values") { + REQUIRE(dest.values.size() == 3); + REQUIRE(dest.values[0] == 10.0); + REQUIRE(dest.values[1] == 20.0); + REQUIRE(dest.values[2] == 30.0); + } + } + } + + GIVEN("A destination vector and a source vector with subset mapping") { + Slic3r::ConfigOptionFloats dest({0.0}); + Slic3r::ConfigOptionFloats src({100.0, 200.0, 300.0}); + std::vector variant_index = {1, 2}; + int stride = 1; + + WHEN("set_to_index maps only indices 1 and 2") { + dest.set_to_index(&src, variant_index, stride); + + THEN("Only the mapped values are copied, default fills the others") { + REQUIRE(dest.values.size() == 2); + REQUIRE(dest.values[0] == 200.0); + REQUIRE(dest.values[1] == 300.0); + } + } + } +} + +SCENARIO("ConfigOptionVector::set_to_index with stride=2 copies grouped values correctly", "[Config][set_to_index]") { + GIVEN("A destination vector and a source vector with stride=2 (e.g., nozzle groups)") { + // Source has 4 groups of 2 values each: (10,11), (20,21), (30,31), (40,41) + Slic3r::ConfigOptionFloats dest({0.0}); + Slic3r::ConfigOptionFloats src({10.0, 11.0, 20.0, 21.0, 30.0, 31.0, 40.0, 41.0}); + int stride = 2; + + WHEN("set_to_index maps groups 0, 1, 3") { + std::vector variant_index = {0, 1, 3}; + dest.set_to_index(&src, variant_index, stride); + + THEN("The destination has 3 groups (6 values) mapped correctly") { + REQUIRE(dest.values.size() == 6); + // Group 0: (10, 11) + REQUIRE(dest.values[0] == 10.0); + REQUIRE(dest.values[1] == 11.0); + // Group 1: (20, 21) + REQUIRE(dest.values[2] == 20.0); + REQUIRE(dest.values[3] == 21.0); + // Group 3: (40, 41) + REQUIRE(dest.values[4] == 40.0); + REQUIRE(dest.values[5] == 41.0); + } + } + } + + GIVEN("A destination and a single-group source") { + Slic3r::ConfigOptionFloats dest({0.0}); + // Source has 1 group of 2 values + Slic3r::ConfigOptionFloats src({50.0, 60.0}); + int stride = 2; + + WHEN("set_to_index maps group 0 from a single-group source") { + std::vector variant_index = {0}; + dest.set_to_index(&src, variant_index, stride); + + THEN("The destination contains the single group correctly") { + REQUIRE(dest.values.size() == 2); + REQUIRE(dest.values[0] == 50.0); + REQUIRE(dest.values[1] == 60.0); + } + } + } +} + +SCENARIO("ConfigOptionVector::set_to_index handles empty dest_index", "[Config][set_to_index]") { + GIVEN("A destination and source with stride=2") { + Slic3r::ConfigOptionFloats dest({0.0}); + Slic3r::ConfigOptionFloats src({10.0, 11.0, 20.0, 21.0}); + std::vector variant_index = {}; + int stride = 2; + + WHEN("set_to_index is called with an empty index vector") { + dest.set_to_index(&src, variant_index, stride); + + THEN("The destination is resized to 0") { + REQUIRE(dest.values.size() == 0); + } + } + } +} + +SCENARIO("ConfigOptionVector::set_to_index handles nil values in source", "[Config][set_to_index]") { + GIVEN("A source with a nil group (stride=2)") { + Slic3r::ConfigOptionFloatsNullable dest({0.0}); + Slic3r::ConfigOptionFloatsNullable src({10.0, 11.0, + Slic3r::ConfigOptionFloatsNullable::nil_value(), Slic3r::ConfigOptionFloatsNullable::nil_value(), + 30.0, 31.0}); + int stride = 2; + + WHEN("set_to_index maps all groups including the nil one") { + std::vector variant_index = {0, 1, 2}; + dest.set_to_index(&src, variant_index, stride); + + THEN("Non-nil groups are copied and the nil group keeps the default") { + REQUIRE(dest.values.size() == 6); + // Group 0: (10, 11) — copied + REQUIRE(dest.values[0] == 10.0); + REQUIRE(dest.values[1] == 11.0); + // Group 1: nil — keeps default (the front value = 10.0) + REQUIRE(dest.values[2] == 10.0); + REQUIRE(dest.values[3] == 10.0); + // Group 2: (30, 31) — copied + REQUIRE(dest.values[4] == 30.0); + REQUIRE(dest.values[5] == 31.0); + } + } + } +} + +SCENARIO("ConfigOptionVector::set_to_index handles out-of-bounds dest_index", "[Config][set_to_index]") { + GIVEN("A source with only 2 groups (4 values) but dest_index references group 3") { + Slic3r::ConfigOptionFloats dest({0.0}); + Slic3r::ConfigOptionFloats src({10.0, 11.0, 20.0, 21.0}); // 2 groups of stride 2 + int stride = 2; + + WHEN("set_to_index maps group 3 which is out of bounds") { + std::vector variant_index = {0, 3}; // group 3 is out of range + dest.set_to_index(&src, variant_index, stride); + + THEN("Group 0 is copied, group 3 falls back to default without crashing") { + REQUIRE(dest.values.size() == 4); + // Group 0: (10, 11) — copied + REQUIRE(dest.values[0] == 10.0); + REQUIRE(dest.values[1] == 11.0); + // Group 3: out of bounds — keeps default (10.0 = src.values.front()) + REQUIRE(dest.values[2] == 10.0); + REQUIRE(dest.values[3] == 10.0); + } + } + } +} + +SCENARIO("ConfigOptionVector::set_to_index handles negative dest_index values", "[Config][set_to_index]") { + GIVEN("A destination and source with a negative entry in dest_index") { + // The dest is initially empty, so resize fills all slots with src.values.front(). + Slic3r::ConfigOptionFloats dest; + Slic3r::ConfigOptionFloats src({100.0, 101.0, 200.0, 201.0}); + int stride = 2; + + WHEN("set_to_index maps group 0 and a negative index") { + std::vector variant_index = {-1, 0}; + dest.set_to_index(&src, variant_index, stride); + + THEN("The negative index is skipped, the valid group is copied") { + REQUIRE(dest.values.size() == 4); + // Position 0 (variant_index[0] = -1): skipped, keeps default fill + // from resize (src.values.front() = 100.0, applied to all new elements) + REQUIRE(dest.values[0] == 100.0); + REQUIRE(dest.values[1] == 100.0); + // Position 1 (variant_index[1] = 0): copied from group 0 of src + REQUIRE(dest.values[2] == 100.0); + REQUIRE(dest.values[3] == 101.0); + } + } + } +} + +SCENARIO("ConfigOptionVector::set_to_index handles single-element groups with stride=1", "[Config][set_to_index]") { + GIVEN("A destination re-mapping one variant index with a stride=1 source") { + // Simulates the PrintObject.cpp code path: stride=1, variant_index={1} + Slic3r::ConfigOptionFloats dest({99.0, 99.0, 99.0, 99.0}); // pre-sized for 4 extruders + Slic3r::ConfigOptionFloats src({0.5, 0.6, 0.7, 0.8}); // 4 extruder values + std::vector variant_index = {1}; // only extruder 1 is active + int stride = 1; + + WHEN("set_to_index is called") { + dest.set_to_index(&src, variant_index, stride); + + THEN("Only the mapped value is copied, rest are defaulted") { + REQUIRE(dest.values.size() == 1); + REQUIRE(dest.values[0] == 0.6); + } + } + } +} + +SCENARIO("ConfigOptionVector::set_to_index throws on incompatible type", "[Config][set_to_index]") { + GIVEN("A Floats destination and an Ints source") { + Slic3r::ConfigOptionFloats dest({0.0}); + Slic3r::ConfigOptionInts src({1, 2, 3}); + std::vector variant_index = {0}; + int stride = 1; + + WHEN("set_to_index is called with mismatched types") { + THEN("A ConfigurationError is thrown") { + REQUIRE_THROWS_AS(dest.set_to_index(&src, variant_index, stride), Slic3r::ConfigurationError); + } + } + } +}