From 72f25b671403370520719257d9ae90896bd93fb8 Mon Sep 17 00:00:00 2001 From: Felix Jen Date: Tue, 21 Apr 2026 23:23:00 -0500 Subject: [PATCH] Add unique filament_id for inherited user presets When a user creates a new filament preset by inheriting from an existing one (e.g., "Brand ABS @BBL H2D" inheriting from "Generic ABS @BBL H2D"), the resulting preset previously had no filament_id of its own. This caused two problems: 1. The AMS sync pipeline could not distinguish the user preset from its parent, so syncing filaments from the printer always resolved to the generic base preset instead of the user's custom one. 2. External tools that rely on filament_id for preset identification (e.g., Bambuddy, see maziggy/bambuddy#1053) could not assign or recognize user-created inherited presets in AMS slots. The root cause was twofold: - No filament_id was generated during save_current_preset() for inherited filament presets. Only base (non-inheriting) presets received one via the CreatePresetsDialog flow. - The AMS matching logic in sync_ams_list(), get_ams_cobox_infos(), and get_filament_presets() filtered candidates with `get_preset_base(f) == &f`, which by definition excludes any preset with a non-empty inherits() field. This commit: - Generates a unique filament_id (MD5 hash of preset name, prefixed with "P", truncated to 8 chars) when creating a new filament preset in PresetCollection::save_current_preset(). This matches the existing ID generation scheme used for base filaments in CreatePresetsDialog::get_filament_id(). - Persists filament_id into the JSON when saving inherited presets via Preset::save() and get_differed_values_to_update(). - Broadens the AMS filament matching predicates to also consider user presets that carry their own filament_id, rather than requiring them to be base presets. Files changed: src/libslic3r/Preset.cpp - ID generation, save, lookup src/libslic3r/PresetBundle.cpp - AMS sync matching predicates --- src/libslic3r/Preset.cpp | 753 ++++++++------------------------- src/libslic3r/PresetBundle.cpp | 4 +- 2 files changed, 190 insertions(+), 567 deletions(-) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index cd58e90e974..2f3c37e3c1e 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -41,6 +41,8 @@ #include #include #include +#include +#include #include "libslic3r.h" #include "Utils.hpp" @@ -52,78 +54,6 @@ using boost::property_tree::ptree; namespace Slic3r { -namespace { - -struct ParsedName { - PresetOrigin::Kind kind { PresetOrigin::Kind::User }; - std::string bundle_id; - std::string bare; -}; - -// Canonical names are built in-memory with '/' separators, so a straight prefix+split match is enough. -static ParsedName parse_preset_name(const std::string &raw_name) -{ - ParsedName out; - - auto try_prefix = [&](const char *dir, PresetOrigin::Kind kind) { - const std::string prefix = std::string(dir) + "/"; - if (! boost::starts_with(raw_name, prefix)) - return false; - const size_t id_start = prefix.size(); - const size_t id_end = raw_name.find('/', id_start); - if (id_end == std::string::npos || id_end == id_start) - return false; - out.kind = kind; - out.bundle_id = raw_name.substr(id_start, id_end - id_start); - out.bare = raw_name.substr(id_end + 1); - return true; - }; - - if (! try_prefix(PRESET_LOCAL_DIR, PresetOrigin::Kind::LocalBundle) && - ! try_prefix(PRESET_SUBSCRIBED_DIR, PresetOrigin::Kind::SubscribedBundle)) - out.bare = raw_name; - - return out; -} - -} // namespace - -std::string get_preset_canonical_name(const std::string &preset_bare_name, const PresetOrigin &origin) -{ - switch (origin.kind) { - case PresetOrigin::Kind::LocalBundle: - return origin.bundle_id.empty() ? preset_bare_name : std::string(PRESET_LOCAL_DIR) + "/" + origin.bundle_id + "/" + preset_bare_name; - case PresetOrigin::Kind::SubscribedBundle: - return origin.bundle_id.empty() ? preset_bare_name : std::string(PRESET_SUBSCRIBED_DIR) + "/" + origin.bundle_id + "/" + preset_bare_name; - default: - return preset_bare_name; - } -} - -std::string get_preset_bare_name(const std::string &canonical_name) -{ - const auto pos = canonical_name.find_last_of('/'); - return pos == std::string::npos ? canonical_name : canonical_name.substr(pos + 1); -} - -PresetOrigin detect_origin_from_path(const boost::filesystem::path &path, const PresetOrigin &explicit_origin) -{ - if (explicit_origin.kind != PresetOrigin::Kind::Auto) - return explicit_origin; - - for (auto it = path.begin(); it != path.end(); ++ it) { - const auto next = std::next(it); - if (next == path.end()) - break; - const std::string segment = it->string(); - if (segment == PRESET_LOCAL_DIR) - return PresetOrigin(PresetOrigin::Kind::LocalBundle, next->string()); - if (segment == PRESET_SUBSCRIBED_DIR) - return PresetOrigin(PresetOrigin::Kind::SubscribedBundle, next->string()); - } - return PresetOrigin(PresetOrigin::Kind::User); -} - //BBS: add a function to load the version from xxx.json Semver get_version_from_json(std::string file_path) { @@ -581,12 +511,20 @@ void Preset::load_info(const std::string& file) catch (...) { return; } + + //TODO: workaround for current info file convert, will remove it later + if (this->updated_time == 0) { + this->updated_time = (long long)Slic3r::Utils::get_current_time_utc(); + //this->sync_info = "update"; + BOOST_LOG_TRIVIAL(info) << boost::format("old info file, updated time to %1%") % this->updated_time; + save_info(); + } } void Preset::save_info(std::string file) { //BBS: add project embedded preset logic - if (this->is_project_embedded || this->is_from_bundle()) + if (this->is_project_embedded) return; if (file.empty()) { fs::path idx_file(this->file); @@ -608,26 +546,17 @@ void Preset::save_info(std::string file) c.close(); } -void Preset::remove_files(bool cloud_already_deleted) +void Preset::remove_files() { //BBS: add project embedded preset logic - if (this->is_project_embedded) { + if (this->is_project_embedded) return; - } // Erase the preset file. boost::nowide::remove(this->file.c_str()); fs::path idx_path(this->file); idx_path.replace_extension(".info"); - if (fs::exists(idx_path)) { - if (!this->setting_id.empty() && !cloud_already_deleted) { - // Cloud-synced preset - mark for deletion and keep .info file until sync confirms - this->sync_info = "delete"; - this->save_info(idx_path.string()); - } else { - // Local-only preset or cloud already confirmed deletion - safe to delete .info immediately - boost::nowide::remove(idx_path.string().c_str()); - } - } + if (fs::exists(idx_path)) + boost::nowide::remove(idx_path.string().c_str()); } //BBS: add logic for only difference save @@ -643,15 +572,12 @@ void Preset::save(DynamicPrintConfig* parent_config) from_str = std::string("User"); else if (this->is_project_embedded) from_str = std::string("Project"); - else if (this->is_from_bundle()) - from_str = std::string("Bundle"); else if (this->is_system) from_str = std::string("System"); else from_str = std::string("Default"); boost::filesystem::create_directories(fs::path(this->file).parent_path()); - const std::string bare_name = get_preset_bare_name(this->name); //BBS: only save difference if it has parent if (parent_config) { @@ -691,22 +617,24 @@ void Preset::save(DynamicPrintConfig* parent_config) opt_dst->set(opt_src); } } - temp_config.save_to_json(this->file, bare_name, from_str, this->version.to_string()); + + if (!filament_id.empty()) { + temp_config.set_key_value(BBL_JSON_KEY_FILAMENT_ID, new ConfigOptionString(filament_id)); + } + + temp_config.save_to_json(this->file, this->name, from_str, this->version.to_string()); } else if (!filament_id.empty() && inherits().empty()) { DynamicPrintConfig temp_config = config; temp_config.set_key_value(BBL_JSON_KEY_FILAMENT_ID, new ConfigOptionString(filament_id)); - temp_config.save_to_json(this->file, bare_name, from_str, this->version.to_string()); + temp_config.save_to_json(this->file, this->name, from_str, this->version.to_string()); } else { - this->config.save_to_json(this->file, bare_name, from_str, this->version.to_string()); + this->config.save_to_json(this->file, this->name, from_str, this->version.to_string()); } BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " save config for: " << this->name << " and filament_id: " << filament_id << " and base_id: " << this->base_id; - // Bundle presets are synced via bundle_id and don't need individual .info files. - if (! this->is_from_bundle()) { - fs::path idx_file(this->file); - idx_file.replace_extension(".info"); - this->save_info(idx_file.string()); - } + fs::path idx_file(this->file); + idx_file.replace_extension(".info"); + this->save_info(idx_file.string()); } void Preset::reload(Preset const &parent) @@ -782,8 +710,7 @@ bool is_compatible_with_printer(const PresetWithVendorProfile &preset, const Pre if (preset.vendor != nullptr && preset.preset.type == Preset::TYPE_FILAMENT) { const auto& excluded_printers = preset.preset.m_excluded_from; const auto excluded = preset.vendor->name == PresetBundle::ORCA_FILAMENT_LIBRARY && - (excluded_printers.find(active_printer.preset.name) != excluded_printers.end() || - excluded_printers.find(active_printer.preset.inherits()) != excluded_printers.end()); + excluded_printers.find(active_printer.preset.name) != excluded_printers.end(); if (excluded) return false; } @@ -967,300 +894,74 @@ bool Preset::has_cali_lines(PresetBundle* preset_bundle) return false; } -static std::vector s_Preset_print_options{ - "layer_height", - "initial_layer_print_height", - "wall_loops", - "alternate_extra_wall", - "slice_closing_radius", - "spiral_mode", - "spiral_mode_smooth", - "spiral_mode_max_xy_smoothing", - "spiral_starting_flow_ratio", - "spiral_finishing_flow_ratio", - "slicing_mode", - "top_shell_layers", - "top_shell_thickness", - "top_surface_density", - "bottom_surface_density", - "bottom_shell_layers", - "bottom_shell_thickness", - "extra_perimeters_on_overhangs", - "ensure_vertical_shell_thickness", - "reduce_crossing_wall", - "detect_thin_wall", - "detect_overhang_wall", - "overhang_reverse", - "overhang_reverse_threshold", - "overhang_reverse_internal_only", - "wall_direction", - "seam_position", - "staggered_inner_seams", - "wall_sequence", - "is_infill_first", - "sparse_infill_density", - "fill_multiline", - "gyroid_optimized", - "sparse_infill_pattern", - "lateral_lattice_angle_1", - "lateral_lattice_angle_2", - "infill_overhang_angle", - "top_surface_pattern", - "bottom_surface_pattern", - "infill_direction", - "solid_infill_direction", - "counterbore_hole_bridging", - "infill_shift_step", - "sparse_infill_rotate_template", - "solid_infill_rotate_template", - "symmetric_infill_y_axis", - "skeleton_infill_density", - "infill_lock_depth", - "skin_infill_depth", - "skin_infill_density", - "align_infill_direction_to_model", - "extra_solid_infills", - "minimum_sparse_infill_area", - "reduce_infill_retraction", - "internal_solid_infill_pattern", - "gap_fill_target", - "ironing_type", - "ironing_pattern", - "ironing_flow", - "ironing_speed", - "ironing_spacing", - "ironing_angle", - "ironing_angle_fixed", - "ironing_inset", - "support_ironing", - "support_ironing_pattern", - "support_ironing_flow", - "support_ironing_spacing", +static std::vector s_Preset_print_options { + "layer_height", "initial_layer_print_height", "wall_loops", "alternate_extra_wall", "slice_closing_radius", "spiral_mode", "spiral_mode_smooth", "spiral_mode_max_xy_smoothing", "spiral_starting_flow_ratio", "spiral_finishing_flow_ratio", "slicing_mode", + "top_shell_layers", "top_shell_thickness", "top_surface_density", "bottom_surface_density", "bottom_shell_layers", "bottom_shell_thickness", + "extra_perimeters_on_overhangs", "ensure_vertical_shell_thickness", "reduce_crossing_wall", "detect_thin_wall", "detect_overhang_wall", "overhang_reverse", "overhang_reverse_threshold","overhang_reverse_internal_only", "wall_direction", + "seam_position", "staggered_inner_seams", "wall_sequence", "is_infill_first", "sparse_infill_density","fill_multiline", "sparse_infill_pattern", "lateral_lattice_angle_1", "lateral_lattice_angle_2", "infill_overhang_angle", "top_surface_pattern", "bottom_surface_pattern", + "infill_direction", "solid_infill_direction", "counterbore_hole_bridging","infill_shift_step", "sparse_infill_rotate_template", "solid_infill_rotate_template", "symmetric_infill_y_axis","skeleton_infill_density", "infill_lock_depth", "skin_infill_depth", "skin_infill_density", + "align_infill_direction_to_model", "extra_solid_infills", + "minimum_sparse_infill_area", "reduce_infill_retraction","internal_solid_infill_pattern","gap_fill_target", + "ironing_type", "ironing_pattern", "ironing_flow", "ironing_speed", "ironing_spacing", "ironing_angle", "ironing_angle_fixed", "ironing_inset", + "support_ironing", "support_ironing_pattern", "support_ironing_flow", "support_ironing_spacing", "max_travel_detour_distance", - "fuzzy_skin", "fuzzy_skin_thickness", "fuzzy_skin_point_distance", "fuzzy_skin_first_layer", "fuzzy_skin_noise_type", "fuzzy_skin_mode", "fuzzy_skin_scale", "fuzzy_skin_octaves", "fuzzy_skin_persistence", "fuzzy_skin_ripples_per_layer", "fuzzy_skin_ripple_offset", "fuzzy_skin_layers_between_ripple_offset", + "fuzzy_skin", "fuzzy_skin_thickness", "fuzzy_skin_point_distance", "fuzzy_skin_first_layer", "fuzzy_skin_noise_type", "fuzzy_skin_mode", "fuzzy_skin_scale", "fuzzy_skin_octaves", "fuzzy_skin_persistence", "max_volumetric_extrusion_rate_slope", "max_volumetric_extrusion_rate_slope_segment_length","extrusion_rate_smoothing_external_perimeter_only", "inner_wall_speed", "outer_wall_speed", "sparse_infill_speed", "internal_solid_infill_speed", "top_surface_speed", "support_speed", "support_object_xy_distance", "support_object_first_layer_gap", "support_interface_speed", "bridge_speed", "internal_bridge_speed", "gap_infill_speed", "travel_speed", "travel_speed_z", "initial_layer_speed", "outer_wall_acceleration", "initial_layer_acceleration", "top_surface_acceleration", "default_acceleration", "skirt_type", "skirt_loops", "skirt_speed","min_skirt_length", "skirt_distance", "skirt_start_angle", "skirt_height","single_loop_draft_shield", "draft_shield", - "brim_width", "brim_object_gap", "brim_flow_ratio", "brim_use_efc_outline", "combine_brims", "brim_type", "brim_ears_max_angle", "brim_ears_detection_length", "enable_support", "support_type", "support_threshold_angle", "support_threshold_overlap","enforce_support_layers", + "brim_width", "brim_object_gap", "brim_use_efc_outline", "combine_brims", "brim_type", "brim_ears_max_angle", "brim_ears_detection_length", "enable_support", "support_type", "support_threshold_angle", "support_threshold_overlap","enforce_support_layers", "raft_layers", "raft_first_layer_density", "raft_first_layer_expansion", "raft_contact_distance", "raft_expansion", "support_base_pattern", "support_base_pattern_spacing", "support_expansion", "support_style", // BBS - "print_extruder_id", - "print_extruder_variant", + "print_extruder_id", "print_extruder_variant", "independent_support_layer_height", - "support_angle", - "support_interface_top_layers", - "support_interface_bottom_layers", - "support_interface_pattern", - "support_interface_spacing", - "support_interface_loop_pattern", - "support_top_z_distance", - "support_on_build_plate_only", - "support_critical_regions_only", - "bridge_no_support", - "thick_bridges", - "thick_internal_bridges", - "dont_filter_internal_bridges", - "enable_extra_bridge_layer", - "max_bridge_length", - "print_sequence", - "print_order", - "support_remove_small_overhang", - "filename_format", - "wall_filament", - "support_bottom_z_distance", - "sparse_infill_filament", - "solid_infill_filament", - "support_filament", - "support_interface_filament", - "support_interface_not_for_body", - "ooze_prevention", - "standby_temperature_delta", - "preheat_time", - "preheat_steps", - "interface_shells", - "line_width", - "initial_layer_line_width", - "inner_wall_line_width", - "outer_wall_line_width", - "sparse_infill_line_width", - "internal_solid_infill_line_width", - "skin_infill_line_width", - "skeleton_infill_line_width", - "top_surface_line_width", - "support_line_width", - "infill_wall_overlap", - "top_bottom_infill_wall_overlap", - "bridge_flow", - "internal_bridge_flow", - "elefant_foot_compensation", - "elefant_foot_compensation_layers", - "elefant_foot_layers_density", - "xy_contour_compensation", - "xy_hole_compensation", - "resolution", - "enable_prime_tower", - "prime_tower_enable_framework", - "prime_tower_width", - "prime_tower_brim_width", - "prime_tower_skip_points", - "prime_volume", + "support_angle", "support_interface_top_layers", "support_interface_bottom_layers", + "support_interface_pattern", "support_interface_spacing", "support_interface_loop_pattern", + "support_top_z_distance", "support_on_build_plate_only","support_critical_regions_only", "bridge_no_support", "thick_bridges", "thick_internal_bridges","dont_filter_internal_bridges","enable_extra_bridge_layer", "max_bridge_length", "print_sequence", "print_order", "support_remove_small_overhang", + "filename_format", "wall_filament", "support_bottom_z_distance", + "sparse_infill_filament", "solid_infill_filament", "support_filament", "support_interface_filament","support_interface_not_for_body", + "ooze_prevention", "standby_temperature_delta", "preheat_time","preheat_steps", "interface_shells", "line_width", "initial_layer_line_width", "inner_wall_line_width", + "outer_wall_line_width", "sparse_infill_line_width", "internal_solid_infill_line_width", + "skin_infill_line_width","skeleton_infill_line_width", + "top_surface_line_width", "support_line_width", "infill_wall_overlap","top_bottom_infill_wall_overlap", "bridge_flow", "internal_bridge_flow", + "elefant_foot_compensation", "elefant_foot_compensation_layers", "xy_contour_compensation", "xy_hole_compensation", "resolution", "enable_prime_tower", "prime_tower_enable_framework", + "prime_tower_width", "prime_tower_brim_width", "prime_tower_skip_points", "prime_volume", "prime_tower_infill_gap", "prime_tower_flat_ironing", "enable_tower_interface_features", "enable_tower_interface_cooldown_during_tower", - "wipe_tower_no_sparse_layers", - "compatible_printers", - "compatible_printers_condition", - "inherits", - "flush_into_infill", - "flush_into_objects", - "flush_into_support", - "tree_support_branch_angle", - "tree_support_angle_slow", - "tree_support_wall_count", - "tree_support_top_rate", - "tree_support_branch_distance", - "tree_support_tip_diameter", - "tree_support_branch_diameter", - "tree_support_branch_diameter_angle", - "detect_narrow_internal_solid_infill", - "gcode_add_line_number", - "enable_arc_fitting", - "precise_z_height", - "infill_combination", - "infill_combination_max_layer_height", /*"adaptive_layer_height",*/ - "support_bottom_interface_spacing", - "enable_overhang_speed", - "slowdown_for_curled_perimeters", - "overhang_1_4_speed", - "overhang_2_4_speed", - "overhang_3_4_speed", - "overhang_4_4_speed", - "initial_layer_infill_speed", - "only_one_wall_top", - "timelapse_type", - "wall_generator", - "wall_transition_length", - "wall_transition_filter_deviation", - "wall_transition_angle", - "wall_distribution_count", - "min_feature_size", - "min_bead_width", - "post_process", - "process_change_extrusion_role_gcode", - "min_length_factor", - "wall_maximum_resolution", - "wall_maximum_deviation", - "small_perimeter_speed", - "small_perimeter_threshold", - "bridge_angle", - "internal_bridge_angle", - "filter_out_gap_fill", - "travel_acceleration", - "inner_wall_acceleration", - "min_width_top_surface", - "default_jerk", - "outer_wall_jerk", - "inner_wall_jerk", - "infill_jerk", - "top_surface_jerk", - "initial_layer_jerk", - "travel_jerk", - "default_junction_deviation", - "top_solid_infill_flow_ratio", - "bottom_solid_infill_flow_ratio", - "only_one_wall_first_layer", - "print_flow_ratio", - "seam_gap", - "set_other_flow_ratios", - "first_layer_flow_ratio", - "outer_wall_flow_ratio", - "inner_wall_flow_ratio", - "overhang_flow_ratio", - "sparse_infill_flow_ratio", - "internal_solid_infill_flow_ratio", - "gap_fill_flow_ratio", - "support_flow_ratio", - "support_interface_flow_ratio", - "role_based_wipe_speed", - "wipe_speed", - "accel_to_decel_enable", - "accel_to_decel_factor", - "wipe_on_loops", - "wipe_before_external_loop", - "bridge_density", - "internal_bridge_density", - "precise_outer_wall", - "bridge_acceleration", - "sparse_infill_acceleration", - "internal_solid_infill_acceleration", - "tree_support_auto_brim", - "tree_support_brim_width", - "gcode_comments", - "gcode_label_objects", - "initial_layer_travel_speed", - "initial_layer_travel_acceleration", - "initial_layer_travel_jerk", - "exclude_object", - "slow_down_layers", - "infill_anchor", - "infill_anchor_max", - "initial_layer_min_bead_width", - "make_overhang_printable", - "make_overhang_printable_angle", - "make_overhang_printable_hole_size", - "notes", - "wipe_tower_cone_angle", - "wipe_tower_extra_spacing", - "wipe_tower_max_purge_speed", - "wipe_tower_wall_type", - "wipe_tower_extra_rib_length", - "wipe_tower_rib_width", - "wipe_tower_fillet_wall", - "wipe_tower_filament", - "wiping_volumes_extruders", - "wipe_tower_bridging", - "wipe_tower_extra_flow", - "single_extruder_multi_material_priming", - "wipe_tower_rotation_angle", - "tree_support_branch_distance_organic", - "tree_support_branch_diameter_organic", - "tree_support_branch_angle_organic", - "hole_to_polyhole", - "hole_to_polyhole_threshold", - "hole_to_polyhole_twisted", - "mmu_segmented_region_max_width", - "mmu_segmented_region_interlocking_depth", - "small_area_infill_flow_compensation", - "small_area_infill_flow_compensation_model", - "enable_wrapping_detection", - "seam_slope_type", - "seam_slope_conditional", - "scarf_angle_threshold", - "scarf_joint_speed", - "scarf_joint_flow_ratio", - "seam_slope_start_height", - "seam_slope_entire_loop", - "seam_slope_min_length", - "seam_slope_steps", - "seam_slope_inner_walls", - "scarf_overhang_threshold", - "interlocking_beam", - "interlocking_orientation", - "interlocking_beam_layer_count", - "interlocking_depth", - "interlocking_boundary_avoidance", - "interlocking_beam_width", - "calib_flowrate_topinfill_special_order", - // Z Anti-Aliasing (ZAA) - "zaa_enabled", - "zaa_minimize_perimeter_height", - "zaa_dont_alternate_fill_direction", - "zaa_min_z", - "ironing_expansion", + "wipe_tower_no_sparse_layers", "compatible_printers", "compatible_printers_condition", "inherits", + "flush_into_infill", "flush_into_objects", "flush_into_support", + "tree_support_branch_angle", "tree_support_angle_slow", "tree_support_wall_count", "tree_support_top_rate", "tree_support_branch_distance", "tree_support_tip_diameter", + "tree_support_branch_diameter", "tree_support_branch_diameter_angle", + "detect_narrow_internal_solid_infill", + "gcode_add_line_number", "enable_arc_fitting", "precise_z_height", "infill_combination","infill_combination_max_layer_height", /*"adaptive_layer_height",*/ + "support_bottom_interface_spacing", "enable_overhang_speed", "slowdown_for_curled_perimeters", "overhang_1_4_speed", "overhang_2_4_speed", "overhang_3_4_speed", "overhang_4_4_speed", + "initial_layer_infill_speed", "only_one_wall_top", + "timelapse_type", + "wall_generator", "wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle", + "wall_distribution_count", "min_feature_size", "min_bead_width", "post_process", "min_length_factor", + "small_perimeter_speed", "small_perimeter_threshold","bridge_angle","internal_bridge_angle", "filter_out_gap_fill", "travel_acceleration","inner_wall_acceleration", "min_width_top_surface", + "default_jerk", "outer_wall_jerk", "inner_wall_jerk", "infill_jerk", "top_surface_jerk", "initial_layer_jerk","travel_jerk","default_junction_deviation", + "top_solid_infill_flow_ratio","bottom_solid_infill_flow_ratio","only_one_wall_first_layer", "print_flow_ratio", "seam_gap", + "set_other_flow_ratios", "first_layer_flow_ratio", "outer_wall_flow_ratio", "inner_wall_flow_ratio", "overhang_flow_ratio", "sparse_infill_flow_ratio", "internal_solid_infill_flow_ratio", "gap_fill_flow_ratio", "support_flow_ratio", "support_interface_flow_ratio", + "role_based_wipe_speed", "wipe_speed", "accel_to_decel_enable", "accel_to_decel_factor", "wipe_on_loops", "wipe_before_external_loop", + "bridge_density","internal_bridge_density", "precise_outer_wall", "bridge_acceleration", + "sparse_infill_acceleration", "internal_solid_infill_acceleration", "tree_support_auto_brim", + "tree_support_brim_width", "gcode_comments", "gcode_label_objects", + "initial_layer_travel_speed", "exclude_object", "slow_down_layers", "infill_anchor", "infill_anchor_max","initial_layer_min_bead_width", + "make_overhang_printable", "make_overhang_printable_angle", "make_overhang_printable_hole_size" ,"notes", + "wipe_tower_cone_angle", "wipe_tower_extra_spacing","wipe_tower_max_purge_speed", + "wipe_tower_wall_type", "wipe_tower_extra_rib_length", "wipe_tower_rib_width", "wipe_tower_fillet_wall", + "wipe_tower_filament", "wiping_volumes_extruders","wipe_tower_bridging", "wipe_tower_extra_flow","single_extruder_multi_material_priming", + "wipe_tower_rotation_angle", "tree_support_branch_distance_organic", "tree_support_branch_diameter_organic", "tree_support_branch_angle_organic", + "hole_to_polyhole", "hole_to_polyhole_threshold", "hole_to_polyhole_twisted", "mmu_segmented_region_max_width", "mmu_segmented_region_interlocking_depth", + "small_area_infill_flow_compensation", "small_area_infill_flow_compensation_model", + "enable_wrapping_detection", + "seam_slope_type", "seam_slope_conditional", "scarf_angle_threshold", "scarf_joint_speed", "scarf_joint_flow_ratio", "seam_slope_start_height", "seam_slope_entire_loop", "seam_slope_min_length", "seam_slope_steps", "seam_slope_inner_walls", "scarf_overhang_threshold", + "interlocking_beam", "interlocking_orientation", "interlocking_beam_layer_count", "interlocking_depth", "interlocking_boundary_avoidance", "interlocking_beam_width","calib_flowrate_topinfill_special_order", }; static std::vector s_Preset_filament_options {/*"filament_colour", */ "default_filament_colour", "required_nozzle_HRC", "filament_diameter", "pellet_flow_coefficient", "volumetric_speed_coefficients", "filament_type", @@ -1275,10 +976,10 @@ static std::vector s_Preset_filament_options {/*"filament_colour", // "bed_type", //BBS:temperature_vitrification "temperature_vitrification", "reduce_fan_stop_start_freq","dont_slow_down_outer_wall", "slow_down_for_layer_cooling", "fan_min_speed", - "fan_max_speed", "enable_overhang_bridge_fan", "overhang_fan_speed", "overhang_fan_threshold", "close_fan_the_first_x_layers", "close_additional_fan_first_x_layers", "first_x_layer_fan_speed", "full_fan_speed_layer", "additional_fan_full_speed_layer", "fan_cooling_layer_time", "slow_down_layer_time", "slow_down_min_speed", - "filament_start_gcode", "filament_end_gcode", "filament_change_extrusion_role_gcode", + "fan_max_speed", "enable_overhang_bridge_fan", "overhang_fan_speed", "overhang_fan_threshold", "close_fan_the_first_x_layers", "full_fan_speed_layer", "fan_cooling_layer_time", "slow_down_layer_time", "slow_down_min_speed", + "filament_start_gcode", "filament_end_gcode", //exhaust fan control - "activate_air_filtration","activate_air_filtration_during_print","activate_air_filtration_on_completion","during_print_exhaust_fan_speed","complete_print_exhaust_fan_speed", + "activate_air_filtration","during_print_exhaust_fan_speed","complete_print_exhaust_fan_speed", // Retract overrides "filament_retraction_length", "filament_z_hop", "filament_z_hop_types", "filament_retract_lift_above", "filament_retract_lift_below", "filament_retract_lift_enforce", "filament_retraction_speed", "filament_deretraction_speed", "filament_retract_restart_extra", "filament_retraction_minimum_travel", "filament_retract_when_changing_layer", "filament_wipe", "filament_retract_before_wipe", @@ -1299,7 +1000,7 @@ static std::vector s_Preset_filament_options {/*"filament_colour", "filament_multitool_ramming", "filament_multitool_ramming_volume", "filament_multitool_ramming_flow", "activate_chamber_temp_control", "filament_long_retractions_when_cut","filament_retraction_distances_when_cut", "idle_temperature", //BBS filament change length while the extruder color - "filament_change_length","filament_flush_volumetric_speed","filament_flush_temp", "filament_cooling_before_tower", + "filament_change_length","filament_flush_volumetric_speed","filament_flush_temp", "long_retractions_when_ec", "retraction_distances_when_ec" }; @@ -1312,14 +1013,12 @@ static std::vector s_Preset_machine_limits_options { "machine_max_junction_deviation", //resonance avoidance ported from qidi slicer "resonance_avoidance", "min_resonance_avoidance_speed", "max_resonance_avoidance_speed", - // Orca: input shaping - "input_shaping_emit", "input_shaping_type", "input_shaping_freq_x", "input_shaping_freq_y", "input_shaping_damp_x", "input_shaping_damp_y", }; static std::vector s_Preset_printer_options { "printer_technology", "printable_area", "extruder_printable_area", "bed_exclude_area","bed_custom_texture", "bed_custom_model", "gcode_flavor", - "fan_kickstart", "part_cooling_fan_min_pwm", "fan_speedup_time", "fan_speedup_overhangs", + "fan_kickstart", "fan_speedup_time", "fan_speedup_overhangs", "single_extruder_multi_material", "manual_filament_change", "file_start_gcode", "machine_start_gcode", "machine_end_gcode", "before_layer_change_gcode", "printing_by_object_gcode", "layer_change_gcode", "time_lapse_gcode", "wrapping_detection_gcode", "change_filament_gcode", "change_extrusion_role_gcode", "printer_model", "printer_variant", "printer_extruder_id", "printer_extruder_variant", "extruder_variant_list", "default_nozzle_volume_type", "printable_height", "extruder_printable_height", "extruder_clearance_radius", "extruder_clearance_height_to_lid", "extruder_clearance_height_to_rod", @@ -1336,7 +1035,7 @@ static std::vector s_Preset_printer_options { "use_relative_e_distances", "extruder_type", "use_firmware_retraction", "printer_notes", "grab_length", "support_object_skip_flush", "physical_extruder_map", "cooling_tube_retraction", - "cooling_tube_length", "high_current_on_filament_swap", "parking_pos_retraction", "extra_loading_move", "wipe_tower_type", "purge_in_prime_tower", "enable_filament_ramming", "tool_change_on_wipe_tower", + "cooling_tube_length", "high_current_on_filament_swap", "parking_pos_retraction", "extra_loading_move", "wipe_tower_type", "purge_in_prime_tower", "enable_filament_ramming", "z_offset", "disable_m73", "preferred_orientation", "emit_machine_limits_to_gcode", "pellet_modded_printer", "support_multi_bed_types", "default_bed_type", "bed_mesh_min","bed_mesh_max","bed_mesh_probe_distance", "adaptive_bed_mesh_margin", "enable_long_retraction_when_cut","long_retractions_when_cut","retraction_distances_when_cut", "bed_temperature_formula", "nozzle_flush_dataset" @@ -1512,34 +1211,19 @@ void PresetCollection::add_default_preset(const std::vector &keys, ++ m_num_default_presets; } -std::string PresetCollection::canonical_preset_name(const std::string &name, const PresetOrigin &load_origin) const -{ - const ParsedName parsed = parse_preset_name(name); - PresetOrigin origin = load_origin; - if (origin.kind == PresetOrigin::Kind::Auto) { - origin.kind = parsed.kind; - origin.bundle_id = parsed.bundle_id; - } else if (origin.is_bundle() && origin.bundle_id.empty()) { - origin.bundle_id = parsed.bundle_id; - } - return get_preset_canonical_name(parsed.bare, origin); -} - // Load all presets found in dir_path. // Throws an exception on error. void PresetCollection::load_presets( const std::string &dir_path, const std::string &subdir, - PresetsConfigSubstitutions& substitutions, ForwardCompatibilitySubstitutionRule substitution_rule, - std::function preset_loaded_fn, const PresetOrigin &load_origin) + PresetsConfigSubstitutions& substitutions, ForwardCompatibilitySubstitutionRule substitution_rule) { // Don't use boost::filesystem::canonical() on Windows, it is broken in regard to reparse points, // see https://github.com/prusa3d/PrusaSlicer/issues/732 boost::filesystem::path dir = boost::filesystem::absolute(boost::filesystem::path(dir_path) / subdir).make_preferred(); - const PresetOrigin resolved_origin = detect_origin_from_path(dir, load_origin); // Load custom roots first if (fs::exists(dir / "base")) { - load_presets(dir.string(), "base", substitutions, substitution_rule, nullptr, resolved_origin); + load_presets(dir.string(), "base", substitutions, substitution_rule); } //BBS: add config related logs @@ -1569,16 +1253,14 @@ void PresetCollection::load_presets( if (Slic3r::is_json_file(file_name)) { // Remove the .ini suffix. std::string name = file_name.erase(file_name.size() - 5); - std::string canonical_name = this->canonical_preset_name(name, resolved_origin); - if (this->find_preset(canonical_name, false)) { + if (this->find_preset(name, false)) { // This happens when there's is a preset (most likely legacy one) with the same name as a system preset // that's already been loaded from a bundle. - BOOST_LOG_TRIVIAL(warning) << "Preset already present, not loading: " << canonical_name; + BOOST_LOG_TRIVIAL(warning) << "Preset already present, not loading: " << name; continue; } try { - Preset preset(m_type, canonical_name, false); - preset.bundle_id = resolved_origin.bundle_id; + Preset preset(m_type, name, false); preset.file = dir_entry.path().string(); // Load the preset file, apply preset values on top of defaults. try { @@ -1629,6 +1311,7 @@ void PresetCollection::load_presets( std::string inherits_value = option_str->value; // Orca: try to find if the parent preset has been renamed inherit_preset = this->find_preset2(inherits_value); + } else { ; } @@ -1706,9 +1389,6 @@ void PresetCollection::load_presets( fs::remove(file_path); } - if (preset_loaded_fn != nullptr) - preset_loaded_fn(preset); - presets_loaded.emplace_back(preset); BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << __LINE__ << " load config successful and preset name is:" << preset.name; } catch (const std::runtime_error &err) { @@ -1827,9 +1507,9 @@ int PresetCollection::get_differed_values_to_update(Preset& preset, std::map& proje inherits_value.replace(pos, 1, 1, '~'); option_str->value = inherits_value; }*/ - inherit_preset = this->find_preset2(inherits_value, true); + inherit_preset = this->find_preset(inherits_value, false, true); } const Preset& default_preset = this->default_preset_for(config); if (inherit_preset) { @@ -1981,10 +1661,9 @@ bool PresetCollection::reset_project_embedded_presets() void PresetCollection::set_sync_info_and_save(std::string name, std::string setting_id, std::string syncinfo, long long update_time) { lock(); - const std::string canonical_name = this->canonical_preset_name(name); for (auto it = m_presets.begin(); it != m_presets.end(); it++) { Preset* preset = &m_presets[it - m_presets.begin()]; - if (preset->name == canonical_name) { + if (preset->name == name) { if (syncinfo.empty()) preset->sync_info.clear(); else @@ -2049,7 +1728,7 @@ void PresetCollection::update_user_presets_directory(const std::string& dir_path } //BBS: save user presets to local -void PresetCollection::save_user_presets(const std::string& dir_path, const std::string& type, std::map& need_to_delete_list) +void PresetCollection::save_user_presets(const std::string& dir_path, const std::string& type, std::vector& need_to_delete_list) { boost::filesystem::path dir = boost::filesystem::absolute(boost::filesystem::path(dir_path) / type).make_preferred(); @@ -2103,37 +1782,31 @@ void PresetCollection::save_user_presets(const std::string& dir_path, const std: } //BBS: load one user preset from key-values -bool PresetCollection::load_user_preset(std::string name, std::map preset_values, PresetsConfigSubstitutions& substitutions, ForwardCompatibilitySubstitutionRule rule, const PresetOrigin &load_origin) +bool PresetCollection::load_user_preset(std::string name, std::map preset_values, PresetsConfigSubstitutions& substitutions, ForwardCompatibilitySubstitutionRule rule) { std::string errors_cummulative; // Store the loaded presets into a new vector, otherwise the binary search for already existing presets would be broken. // (see the "Preset already present, not loading" message). //std::deque presets_loaded; int count = 0; - const std::string canonical_name = this->canonical_preset_name(name, load_origin); - auto update_alias = [this](Preset &preset) { - if (! preset.alias.empty()) - return; - set_custom_preset_alias(preset); - }; - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" enter, name %1% , total value counts %2%")%canonical_name %preset_values.size(); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" enter, name %1% , total value counts %2%")%name %preset_values.size(); //if the version is not matching, skip it if (preset_values.find(BBL_JSON_KEY_VERSION) == preset_values.end()) { - BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format("can not find version, not loading for user preset %1%")%canonical_name; + BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format("can not find version, not loading for user preset %1%")%name; return false; } std::string version_str = preset_values[BBL_JSON_KEY_VERSION]; boost::optional cloud_version = Semver::parse(version_str); if (!cloud_version) { - BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format("invalid version %1%, not loading for user preset %2%")%version_str %canonical_name; + BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format("invalid version %1%, not loading for user preset %2%")%version_str %name; return false; } //setting_id if (preset_values.find(BBL_JSON_KEY_SETTING_ID) == preset_values.end()) { - BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format("can not find setting_id, not loading for user preset %1%")%canonical_name; + BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format("can not find setting_id, not loading for user preset %1%")%name; return false; } std::string cloud_setting_id = preset_values[BBL_JSON_KEY_SETTING_ID]; @@ -2146,17 +1819,17 @@ bool PresetCollection::load_user_preset(std::string name, std::mapname; - auto iter = this->find_preset_internal(canonical_name); + auto iter = this->find_preset_internal(name); bool need_update = false; - if ((iter != m_presets.end()) && (iter->name == canonical_name)) { - BOOST_LOG_TRIVIAL(info) << "Found the Preset locally: " << canonical_name; + if ((iter != m_presets.end()) && (iter->name == name)) { + BOOST_LOG_TRIVIAL(info) << "Found the Preset locally: " << name; //BBS: we should compare the time between cloud and local if ((cloud_update_time == 0) || (cloud_update_time <= iter->updated_time)) { if (cloud_update_time < iter->updated_time) @@ -2168,7 +1841,7 @@ bool PresetCollection::load_user_preset(std::string name, std::mapfile); idx_file.replace_extension(".info"); iter->save_info(idx_file.string()); - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format("preset %1%'s update_time is eqaul or newer, cloud update_time %2%, local update_time %3%")%canonical_name %cloud_update_time %iter->updated_time; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format("preset %1%'s update_time is eqaul or newer, cloud update_time %2%, local update_time %3%")%name %cloud_update_time %iter->updated_time; unlock(); return false; } @@ -2178,36 +1851,26 @@ bool PresetCollection::load_user_preset(std::string name, std::mapsecond; - } else { - const auto inherits_iter = preset_values.find(BBL_JSON_KEY_INHERITS); - const bool preset_inherits_from_parent = inherits_iter != preset_values.end() && !inherits_iter->second.empty(); - if (preset_inherits_from_parent) { - // This indicates that there is inherits exists but there is no base_id - BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ - << boost::format("can not find base_id, not loading for user preset %1%") % canonical_name; - unlock(); - return false; - } + // base_id + if (preset_values.find(BBL_JSON_KEY_BASE_ID) == preset_values.end()) { + BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format("can not find base_id, not loading for user preset %1%") % name; + unlock(); + return false; } + std::string cloud_base_id = preset_values[BBL_JSON_KEY_BASE_ID]; //filament_id std::string cloud_filament_id; if ((m_type == Preset::TYPE_FILAMENT) && preset_values.find(BBL_JSON_KEY_FILAMENT_ID) != preset_values.end()) { cloud_filament_id = preset_values[BBL_JSON_KEY_FILAMENT_ID]; - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " " << canonical_name << " filament_id: " << cloud_filament_id << " base_id: " << based_id; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " " << name << " filament_id: " << cloud_filament_id << " base_id: " << cloud_base_id; } DynamicPrintConfig new_config, cloud_config; try { ConfigSubstitutions config_substitutions = cloud_config.load_string_map(preset_values, rule); if (! config_substitutions.empty()) - substitutions.push_back({ canonical_name, m_type, PresetConfigSubstitutions::Source::UserCloud, canonical_name, std::move(config_substitutions) }); + substitutions.push_back({ name, m_type, PresetConfigSubstitutions::Source::UserCloud, name, std::move(config_substitutions) }); //BBS: use inherit config as the base Preset* inherit_preset = nullptr; @@ -2215,7 +1878,12 @@ bool PresetCollection::load_user_preset(std::string name, std::map (inherits_config); std::string inherits_value = option_str->value; - inherit_preset = this->find_preset2(inherits_value, true); + /*size_t pos = inherits_value.find_first_of('*'); + if (pos != std::string::npos) { + inherits_value.replace(pos, 1, 1, '~'); + option_str->value = inherits_value; + }*/ + inherit_preset = this->find_preset(inherits_value, false, true); } const Preset& default_preset = this->default_preset_for(cloud_config); if (inherit_preset) { @@ -2229,7 +1897,7 @@ bool PresetCollection::load_user_preset(std::string name, std::map(inherits_config); if (inherits_config2 && !inherits_config2->value.empty()) { //we should skip this preset here - BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", can not find inherit preset for user preset %1%, just skip")%canonical_name; + BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", can not find inherit preset for user preset %1%, just skip")%name; unlock(); return false; } @@ -2255,17 +1923,13 @@ bool PresetCollection::load_user_preset(std::string name, std::mapname == m_edited_preset.name && iter->is_dirty) { // Keep modifies when update from remote new_config.apply_only(m_edited_preset.config, m_edited_preset.config.diff(iter->config)); - } else if (iter->name == m_edited_preset.name) { - // Preset is not dirty (no local unsaved changes) — also update the edited preset - // to prevent a false "dirty" indication (orange highlight) after a silent cloud sync - m_edited_preset.config = new_config; } iter->config = new_config; iter->updated_time = cloud_update_time; @@ -2273,28 +1937,25 @@ bool PresetCollection::load_user_preset(std::string name, std::mapversion = cloud_version.value(); iter->user_id = cloud_user_id; iter->setting_id = cloud_setting_id; - iter->base_id = based_id; + iter->base_id = cloud_base_id; iter->filament_id = cloud_filament_id; - update_alias(*iter); //presets_loaded.emplace_back(*it->second); BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", update the user preset %1% from cloud, type %2%, setting_id %3%, base_id %4%, sync_info %5% inherits %6%, filament_id %7%") % iter->name %Preset::get_type_string(m_type) %iter->setting_id %iter->base_id %iter->sync_info %iter->inherits() % iter->filament_id; } else { //create a new one - Preset preset(m_type, canonical_name, false); + Preset preset(m_type, name, false); preset.is_system = false; preset.loaded = true; - preset.bundle_id = load_origin.bundle_id; preset.config = new_config; preset.updated_time = cloud_update_time; preset.sync_info = "save"; preset.version = cloud_version.value(); preset.user_id = cloud_user_id; preset.setting_id = cloud_setting_id; - preset.base_id = based_id; + preset.base_id = cloud_base_id; preset.filament_id = cloud_filament_id; - update_alias(preset); size_t cur_index = iter - m_presets.begin(); m_presets.insert(iter, preset); @@ -2316,7 +1977,7 @@ bool PresetCollection::load_user_preset(std::string name, std::mapcanonical_preset_name(preset_name); - std::deque::iterator it = this->find_preset_internal(canonical_name); - bool found = (it != m_presets.end()) && (it->name == canonical_name) && is_trusted(*it); + std::deque::iterator it = this->find_preset_internal(preset_name); + bool found = (it != m_presets.end()) && (it->name == preset_name) && (it->is_system || it->is_default); if (!found) { - it = this->find_preset_renamed(canonical_name); - found = it != m_presets.end() && is_trusted(*it); + it = this->find_preset_renamed(preset_name); + found = it != m_presets.end() && (it->is_system || it->is_default); } if (!found) { if (!inherit_name.empty()) { - const std::string canonical_inherit_name = this->canonical_preset_name(inherit_name); - it = this->find_preset_internal(canonical_inherit_name); - found = it != m_presets.end() && it->name == canonical_inherit_name && is_trusted(*it); + it = this->find_preset_internal(inherit_name); + found = it != m_presets.end() && it->name == inherit_name && (it->is_system || it->is_default); if (found) BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": preset_name %1%, inherit_name %2%, found inherit in list")%preset_name %inherit_name; else @@ -2430,7 +2084,7 @@ std::pair PresetCollection::load_external_preset( cfg.apply_only(combined_config, keys, true); std::string &inherits = Preset::inherits(cfg); - //add different settings check logic, replace the old system preset's default value with new system preset's default values + //BBS: add different settings check logic, replace the old system preset's default value with new system preset's default values std::deque::iterator it = this->find_preset_internal(original_name); bool found = it != m_presets.end() && it->name == original_name; if (! found) { @@ -2770,7 +2424,7 @@ std::map> PresetCollection::get_filamen std::map> filament_presets; for (auto &preset : m_presets) { if (preset.is_user()) { - if (preset.inherits() == "") { filament_presets[preset.filament_id].push_back(&preset); } + if (preset.inherits() == "" || !preset.filament_id.empty()) { filament_presets[preset.filament_id].push_back(&preset); } continue; } if (get_preset_base(preset) == &preset) { filament_presets[preset.filament_id].push_back(&preset); } @@ -2792,7 +2446,7 @@ void PresetCollection::save_current_preset(const std::string &new_name, bool det // Preset with the same name found. Preset &preset = *it; //BBS: add project embedded preset logic - if (!preset.can_overwrite()) { + if (preset.is_default || preset.is_system) { //if (preset.is_default || preset.is_external || preset.is_system) // Cannot overwrite the default preset. //BBS: add lock logic for sync preset in background @@ -2851,9 +2505,6 @@ void PresetCollection::save_current_preset(const std::string &new_name, bool det preset.is_default = false; preset.is_system = false; preset.is_external = false; - - preset.bundle_id.clear(); - preset.file = this->path_for_preset(preset); // The newly saved preset will be activated -> make it visible. preset.is_visible = true; @@ -2867,8 +2518,18 @@ void PresetCollection::save_current_preset(const std::string &new_name, bool det preset.is_project_embedded = false; if (m_type == Preset::TYPE_PRINT) preset.config.option("print_settings_id", true)->value = new_name; - else if (m_type == Preset::TYPE_FILAMENT) + else if (m_type == Preset::TYPE_FILAMENT) { preset.config.option("filament_settings_id", true)->values[0] = new_name; + + boost::uuids::detail::md5 hash; + boost::uuids::detail::md5::digest_type digest; + hash.process_bytes(new_name.data(), new_name.size()); + hash.get_digest(digest); + const auto char_digest = reinterpret_cast(&digest); + std::string result; + boost::algorithm::hex(char_digest, char_digest + sizeof(boost::uuids::detail::md5::digest_type), std::back_inserter(result)); + preset.filament_id = "P" + result.substr(0, 7); + } else if (m_type == Preset::TYPE_PRINTER) preset.config.option("printer_settings_id", true)->value = new_name; //BBS: add lock logic for sync preset in background @@ -2896,7 +2557,7 @@ void PresetCollection::save_current_preset(const std::string &new_name, bool det bool PresetCollection::delete_current_preset() { Preset &selected = this->get_selected_preset(); - if (!selected.can_overwrite()) + if (selected.is_default) return false; if (get_preset_base(selected) == &selected) { @@ -2927,24 +2588,18 @@ bool PresetCollection::delete_current_preset() return true; } -bool PresetCollection::delete_preset(const std::string& name, bool force) +bool PresetCollection::delete_preset(const std::string& name) { - Preset *preset_ptr = this->find_preset(name, false, true); - if (preset_ptr == nullptr) - return false; - - auto it = this->find_preset_internal(preset_ptr->name); - if (it == m_presets.end() || it->name != preset_ptr->name) - return false; + auto it = this->find_preset_internal(name); Preset& preset = *it; - // ORCA: if the preset can't be overridden then don't allow deletion - // force=true bypasses this for bundle preset cleanup from cloud sync - if (!force && !preset.can_overwrite()) + if (preset.is_default) return false; - - preset.remove_files(); - + //BBS: add project embedded preset logic and refine is_external + //if (!preset.is_external && !preset.is_system) { + if (! preset.is_system) { + preset.remove_files(); + } //BBS: add lock logic for sync preset in background lock(); set_printer_hold_alias(it->alias, *it, true); @@ -2954,30 +2609,6 @@ bool PresetCollection::delete_preset(const std::string& name, bool force) return true; } -void PresetCollection::check_and_fix_syncinfo(Preset& preset, const std::string& user_id) -{ - // user id can't be empty - if (user_id.empty()) - return; - // correct the sync info if preset.user_id is empty(the profile json file is copied to the user folder with missing .info file) or preset.user_id - // is not equal to the current user id or preset.setting_id is not in expected format(the .info is copied from the older format) - if (preset.user_id.empty() || preset.user_id != user_id || preset.setting_id.find('-') == std::string::npos) { - preset.user_id = user_id; - preset.setting_id = ""; - - if (preset.base_id.empty()) { - const std::string inherits = Preset::inherits(preset.config); - Preset* parent_preset = find_preset2(inherits, true); - if (parent_preset) - preset.base_id = parent_preset->setting_id; - } - // tell the sync logic to sync it as a new preset - preset.updated_time = 0; - preset.sync_info = "create"; - preset.save_info(); - } -} - const Preset* PresetCollection::get_selected_preset_parent() const { if (this->get_selected_idx() == size_t(-1)) @@ -3040,7 +2671,7 @@ const Preset *PresetCollection::get_preset_base(const Preset &child) const // Handle user preset if (child.inherits().empty()) return &child; // this is user root - auto inherits = find_preset2(child.inherits(), true); + auto inherits = find_preset2(child.inherits(),true); return inherits ? get_preset_base(*inherits) : nullptr; } @@ -3114,21 +2745,20 @@ const std::string& PresetCollection::get_suffix_modified() { // If a preset was not found by its name, null is returned. Preset* PresetCollection::find_preset(const std::string &name, bool first_visible_if_not_found, bool real, bool only_from_library) { - const ParsedName parsed = parse_preset_name(name); - const std::string canonical = get_preset_canonical_name(parsed.bare, PresetOrigin(parsed.kind, parsed.bundle_id)); - auto it = this->find_preset_internal(canonical, only_from_library); - if (it != m_presets.end() && it->name == canonical) - return &this->preset(it - m_presets.begin(), real); - return first_visible_if_not_found ? &this->first_visible() : nullptr; + Preset key(m_type, name, false); + auto it = this->find_preset_internal(name, only_from_library); + // Ensure that a temporary copy is returned if the preset found is currently selected. + return (it != m_presets.end() && it->name == key.name) ? &this->preset(it - m_presets.begin(), real) : + first_visible_if_not_found ? &this->first_visible() : nullptr; } Preset* PresetCollection::find_preset2(const std::string& name, bool auto_match/* = true */) { - auto preset = find_preset(name, false, true); + auto preset = find_preset(name,false,true); if (preset == nullptr) { auto _name = get_preset_name_renamed(name); if (_name != nullptr) - preset = find_preset(*_name, false, true); + preset = find_preset(*_name,false,true); if (auto_match && preset == nullptr) { //Orca: one more try, find the most likely preset in OrcaFilamentLibrary if (name.find("Generic") != std::string::npos) { @@ -3144,6 +2774,7 @@ Preset* PresetCollection::find_preset2(const std::string& name, bool auto_match/ } } } + return preset; } @@ -3495,11 +3126,10 @@ bool PresetCollection::select_preset_by_name(const std::string &name_w_suffix, b //BBS: add config related logs BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": %1%, try to select by name %2%, force %3%")%Preset::get_type_string(m_type) %name_w_suffix %force; std::string name = Preset::remove_suffix_modified(name_w_suffix); - const std::string normalized_name = this->canonical_preset_name(name); // 1) Try to find the preset by its name. - auto it = this->find_preset_internal(normalized_name); + auto it = this->find_preset_internal(name); size_t idx = 0; - if (it != m_presets.end() && it->name == normalized_name && it->is_visible) + if (it != m_presets.end() && it->name == name && it->is_visible) // Preset found by its name and it is visible. idx = it - m_presets.begin(); else { @@ -3532,12 +3162,11 @@ bool PresetCollection::select_preset_by_name_strict(const std::string &name) { //BBS: add config related logs BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": %1%, try to select by name %2%")%Preset::get_type_string(m_type) %name; - const std::string canonical_name = this->canonical_preset_name(name); // 1) Try to find the preset by its name. - auto it = this->find_preset_internal(canonical_name); + auto it = this->find_preset_internal(name); size_t idx = (size_t)-1; - if (it != m_presets.end() && it->name == canonical_name && it->is_visible) + if (it != m_presets.end() && it->name == name && it->is_visible) // Preset found by its name. idx = it - m_presets.begin(); // 2) Select the new preset. @@ -3649,30 +3278,24 @@ void PresetCollection::update_map_system_profile_renamed() void PresetCollection::set_custom_preset_alias(Preset &preset) { - // For filaments, remove the postfix - // For printers, there is nothing to remove - // For prints AKA processes, the postfix should be kept - // Alias should be set here, as the preset name may be augmented further later (i.e., prefixing relative path for bundles) - std::string bare_preset_name = get_preset_bare_name(preset.name); - std::string alias_name = bare_preset_name; - - const bool is_root_filament_preset = - m_type == Preset::Type::TYPE_FILAMENT && - preset.config.has(BBL_JSON_KEY_INHERITS) && - preset.config.option(BBL_JSON_KEY_INHERITS)->value.empty(); - if (is_root_filament_preset) { - const size_t suffix_separator_pos = bare_preset_name.find_first_of("@"); - if (suffix_separator_pos != std::string::npos) { - alias_name = bare_preset_name.substr(0, suffix_separator_pos); - boost::trim_right(alias_name); - if (alias_name.empty()) - alias_name = bare_preset_name; + if (m_type == Preset::Type::TYPE_FILAMENT && preset.config.has(BBL_JSON_KEY_INHERITS) && preset.config.option(BBL_JSON_KEY_INHERITS)->value.empty()) { + std::string alias_name; + std::string preset_name = preset.name; + if (alias_name.empty()) { + size_t end_pos = preset_name.find_first_of("@"); + if (end_pos != std::string::npos) { + alias_name = preset_name.substr(0, end_pos); + boost::trim_right(alias_name); + } + } + if (alias_name.empty() || is_alias_exist(alias_name, &preset)) + preset.alias = ""; + else { + preset.alias = std::move(alias_name); + m_map_alias_to_profile_name[preset.alias].push_back(preset.name); + set_printer_hold_alias(preset.alias, preset); } } - - preset.alias = std::move(alias_name); - m_map_alias_to_profile_name[preset.alias].push_back(preset.name); - set_printer_hold_alias(preset.alias, preset); } void PresetCollection::set_printer_hold_alias(const std::string &alias, Preset &preset, bool remove) @@ -3773,7 +3396,7 @@ std::string PresetCollection::path_from_name(const std::string &new_name, bool d std::string PresetCollection::path_for_preset(const Preset &preset) const { - return path_from_name(get_preset_bare_name(preset.name), is_base_preset(preset)); + return path_from_name(preset.name, is_base_preset(preset)); } const Preset& PrinterPresetCollection::default_preset_for(const DynamicPrintConfig &config) const diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp index 5e0ab158499..f85b0103c7f 100644 --- a/src/libslic3r/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -3101,7 +3101,7 @@ void PresetBundle::get_ams_cobox_infos(AMSComboInfo& combox_info) continue; } auto iter = std::find_if(filaments.begin(), filaments.end(), - [this, &filament_id](auto &f) { return f.is_compatible && filaments.get_preset_base(f) == &f && f.filament_id == filament_id; }); + [this, &filament_id](auto &f) { return f.is_compatible && (filaments.get_preset_base(f) == &f || (f.is_user() && !f.filament_id.empty())) && f.filament_id == filament_id; }); if (iter == filaments.end()) { BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": filament_id %1% not found or system or compatible") % filament_id; auto filament_type = ams.opt_string("filament_type", 0u); @@ -3203,7 +3203,7 @@ unsigned int PresetBundle::sync_ams_list(std::vector