From 24ab0b81a23ef08ade8c159775f89eeb022a6727 Mon Sep 17 00:00:00 2001 From: sentientstardust Date: Sat, 23 May 2026 21:17:18 +0100 Subject: [PATCH] Add options to texture mapping zone menu: "Assign to selected objects" and "Delete all texture mapping zones" - Use perimeter path modulation by default for gradient zones --- src/libslic3r/TextureMapping.cpp | 18 ++++- src/libslic3r/TextureMapping.hpp | 25 ++++++- src/slic3r/GUI/3DScene.cpp | 22 +++++- src/slic3r/GUI/GUI_ObjectList.cpp | 66 ++++++++++++++++ src/slic3r/GUI/GUI_ObjectList.hpp | 1 + src/slic3r/GUI/Plater.cpp | 120 +++++++++++++++++++++++++++--- 6 files changed, 235 insertions(+), 17 deletions(-) diff --git a/src/libslic3r/TextureMapping.cpp b/src/libslic3r/TextureMapping.cpp index 27bd873d2ae..5364cbb9bc4 100644 --- a/src/libslic3r/TextureMapping.cpp +++ b/src/libslic3r/TextureMapping.cpp @@ -1008,6 +1008,7 @@ bool TextureMappingZone::operator==(const TextureMappingZone &rhs) const seam_hiding == rhs.seam_hiding && nonlinear_offset_adjustment == rhs.nonlinear_offset_adjustment && modulation_mode == rhs.modulation_mode && + modulation_mode_manually_changed == rhs.modulation_mode_manually_changed && recolor_small_perimeter_loops == rhs.recolor_small_perimeter_loops && recolor_top_visible_perimeter_sections == rhs.recolor_top_visible_perimeter_sections && top_visible_perimeter_recolor_aggressiveness == rhs.top_visible_perimeter_recolor_aggressiveness && @@ -1081,6 +1082,7 @@ void TextureMappingManager::refresh(const std::vector &filament_col zone.component_b = zone.linear_gradient_stops.back().filament_id; } } + zone.apply_default_modulation_mode(); } } @@ -1168,6 +1170,7 @@ TextureMappingZone *TextureMappingManager::add_zone(size_t num_physical, surface_pattern == int(TextureMappingZone::Gradient2D) || surface_pattern == int(TextureMappingZone::LinearGradient) ? surface_pattern : int(TextureMappingZone::ImageTexture); + zone.apply_default_modulation_mode(); std::vector ids; for (size_t i = 1; i <= std::min(num_physical, 9); ++i) @@ -1267,6 +1270,7 @@ std::string TextureMappingManager::serialize_entries() zone.stable_id = normalize_stable_id(zone.stable_id); if (zone.display_color.empty() || zone.display_color[0] != '#') zone.display_color = random_display_color(zone.stable_id); + zone.apply_default_modulation_mode(); std::vector component_ids = decode_component_ids(zone.component_ids, 9); if (zone.is_linear_gradient()) { @@ -1335,6 +1339,7 @@ std::string TextureMappingManager::serialize_entries() texture["hide_seams"] = zone.seam_hiding; texture["nonlinear_offset_adjustment"] = zone.nonlinear_offset_adjustment; texture["modulation_mode"] = modulation_mode_name(zone.modulation_mode); + texture["modulation_mode_manually_changed"] = zone.modulation_mode_manually_changed; texture["recolor_small_perimeter_loops"] = zone.recolor_small_perimeter_loops || zone.recolor_top_visible_perimeter_sections; texture["recolor_top_visible_perimeter_sections"] = zone.recolor_top_visible_perimeter_sections; texture["top_visible_perimeter_recolor_aggressiveness"] = @@ -1506,9 +1511,16 @@ void TextureMappingManager::load_entries(const std::string &serialized, zone.reduce_outer_surface_texture = false; zone.seam_hiding = texture.value("hide_seams", false); zone.nonlinear_offset_adjustment = texture.value("nonlinear_offset_adjustment", false); - zone.modulation_mode = - modulation_mode_from_name(texture.value("modulation_mode", - modulation_mode_name(TextureMappingZone::DefaultModulationMode))); + const auto modulation_mode_it = texture.find("modulation_mode"); + const bool has_modulation_mode = + modulation_mode_it != texture.end() && modulation_mode_it->is_string(); + zone.modulation_mode = has_modulation_mode ? + modulation_mode_from_name(modulation_mode_it->get()) : + TextureMappingZone::default_modulation_mode_for_surface_pattern(zone.surface_pattern); + zone.modulation_mode_manually_changed = + texture.value("modulation_mode_manually_changed", + has_modulation_mode && zone.modulation_mode != TextureMappingZone::ModulationLineWidth); + zone.apply_default_modulation_mode(); zone.recolor_small_perimeter_loops = texture.value("recolor_small_perimeter_loops", TextureMappingZone::DefaultRecolorSmallPerimeterLoops); zone.recolor_top_visible_perimeter_sections = diff --git a/src/libslic3r/TextureMapping.hpp b/src/libslic3r/TextureMapping.hpp index 842748d0852..9d8593ad001 100644 --- a/src/libslic3r/TextureMapping.hpp +++ b/src/libslic3r/TextureMapping.hpp @@ -125,7 +125,11 @@ struct TextureMappingZone static constexpr bool DefaultReduceOuterSurfaceTexture = false; static constexpr bool DefaultSeamHiding = false; static constexpr bool DefaultNonlinearOffsetAdjustment = false; - static constexpr int DefaultModulationMode = int(ModulationLineWidth); + static constexpr int DefaultImageTextureModulationMode = int(ModulationLineWidth); + static constexpr int Default2DGradientModulationMode = int(ModulationPerimeterPath); + static constexpr int DefaultLinearGradientModulationMode = int(ModulationPerimeterPath); + static constexpr int DefaultModulationMode = DefaultImageTextureModulationMode; + static constexpr bool DefaultModulationModeManuallyChanged = false; static constexpr bool DefaultRecolorSmallPerimeterLoops = false; static constexpr bool DefaultRecolorTopVisiblePerimeterSections = false; static constexpr int DefaultTopVisiblePerimeterRecolorAggressiveness = int(TopVisibleRecolorAggressive); @@ -153,6 +157,15 @@ struct TextureMappingZone static constexpr bool DefaultPreviewLimitResolution = true; static constexpr bool DefaultAutoAdjustFilamentSelection = true; + static constexpr int default_modulation_mode_for_surface_pattern(int surface_pattern) + { + switch (surface_pattern) { + case int(Gradient2D): return Default2DGradientModulationMode; + case int(LinearGradient): return DefaultLinearGradientModulationMode; + default: return DefaultImageTextureModulationMode; + } + } + struct LinearGradientAnchor { bool valid = false; size_t object_id = 0; @@ -201,6 +214,7 @@ struct TextureMappingZone bool seam_hiding = DefaultSeamHiding; bool nonlinear_offset_adjustment = DefaultNonlinearOffsetAdjustment; int modulation_mode = DefaultModulationMode; + bool modulation_mode_manually_changed = DefaultModulationModeManuallyChanged; bool recolor_small_perimeter_loops = DefaultRecolorSmallPerimeterLoops; bool recolor_top_visible_perimeter_sections = DefaultRecolorTopVisiblePerimeterSections; int top_visible_perimeter_recolor_aggressiveness = DefaultTopVisiblePerimeterRecolorAggressiveness; @@ -243,6 +257,12 @@ struct TextureMappingZone bool is_surface_gradient() const { return is_2d_gradient() || is_linear_gradient(); } bool uses_perimeter_path_modulation() const { return modulation_mode == int(ModulationPerimeterPath); } + void apply_default_modulation_mode() + { + if (!modulation_mode_manually_changed) + modulation_mode = default_modulation_mode_for_surface_pattern(surface_pattern); + } + void reset_offset_settings() { offset_distances.clear(); @@ -265,7 +285,8 @@ struct TextureMappingZone reduce_outer_surface_texture = DefaultReduceOuterSurfaceTexture; seam_hiding = DefaultSeamHiding; nonlinear_offset_adjustment = DefaultNonlinearOffsetAdjustment; - modulation_mode = DefaultModulationMode; + modulation_mode = default_modulation_mode_for_surface_pattern(surface_pattern); + modulation_mode_manually_changed = DefaultModulationModeManuallyChanged; recolor_small_perimeter_loops = DefaultRecolorSmallPerimeterLoops; recolor_top_visible_perimeter_sections = DefaultRecolorTopVisiblePerimeterSections; top_visible_perimeter_recolor_aggressiveness = DefaultTopVisiblePerimeterRecolorAggressiveness; diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 3c7b6dbb93f..ce8f0ad1d16 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -194,6 +194,18 @@ const ModelObject *linear_gradient_anchor_model_object(const Model &model, return nullptr; } +bool linear_gradient_anchor_has_object_reference(const TextureMappingZone::LinearGradientAnchor &anchor) +{ + return anchor.object_backup_id >= 0 || anchor.object_id != 0 || anchor.object_index_valid; +} + +bool linear_gradient_anchor_object_resolves(const TextureMappingZone::LinearGradientAnchor &anchor) +{ + if (!anchor.valid || !linear_gradient_anchor_has_object_reference(anchor)) + return true; + return linear_gradient_anchor_model_object(GUI::wxGetApp().model(), anchor) != nullptr; +} + bool linear_gradient_anchor_matches_model_object(const Model &model, const ModelObject *object, const TextureMappingZone::LinearGradientAnchor &anchor) @@ -3208,11 +3220,17 @@ void GLVolumeCollection::render_linear_gradient_direction_arrows(const Transform component_colors.emplace_back(linear_gradient_filament_color(component_id, filament_colors)); const LinearGradientArrowUsage usage = linear_gradient_arrow_usage(volumes, zone.zone_id); - const std::optional start_anchor = linear_gradient_anchor_global_point(volumes, zone.linear_gradient_start); + const bool stale_unused_start_anchor = + !usage.any && zone.linear_gradient_start.valid && !linear_gradient_anchor_object_resolves(zone.linear_gradient_start); + const bool stale_unused_end_anchor = + !usage.any && zone.linear_gradient_end.valid && !linear_gradient_anchor_object_resolves(zone.linear_gradient_end); + const std::optional start_anchor = stale_unused_start_anchor ? + std::nullopt : + linear_gradient_anchor_global_point(volumes, zone.linear_gradient_start); const bool radial_mode = zone.linear_gradient_mode == int(TextureMappingZone::LinearGradientRadial); const std::optional end_anchor = radial_mode ? std::nullopt : - linear_gradient_anchor_global_point(volumes, zone.linear_gradient_end); + (stale_unused_end_anchor ? std::nullopt : linear_gradient_anchor_global_point(volumes, zone.linear_gradient_end)); if (!usage.any && !start_anchor && !end_anchor) continue; diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 81ad1ac26c8..1f18645a0d9 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -6880,6 +6880,72 @@ void ObjectList::set_extruder_for_selected_items(const int extruder) Refresh(); } +bool ObjectList::assign_extruder_to_objects_and_clear_filament_region_painting(const std::vector& object_idxs, const int extruder) +{ + std::vector colors = wxGetApp().plater()->get_extruder_colors_from_plater_config(); + if (extruder <= 0 || extruder > int(colors.size())) + return false; + + std::vector valid_object_idxs; + for (size_t obj_idx : object_idxs) { + if (m_objects == nullptr || obj_idx >= m_objects->size() || (*m_objects)[obj_idx] == nullptr) + continue; + if (std::find(valid_object_idxs.begin(), valid_object_idxs.end(), obj_idx) == valid_object_idxs.end()) + valid_object_idxs.emplace_back(obj_idx); + } + + if (valid_object_idxs.empty()) + return false; + + bool changed = false; + for (size_t obj_idx : valid_object_idxs) { + ModelObject *model_object = (*m_objects)[obj_idx]; + const int current_extruder = model_object->config.has("extruder") ? model_object->config.opt_int("extruder") : 1; + changed |= current_extruder != extruder; + for (ModelVolume *mv : model_object->volumes) { + if (mv->type() == ModelVolumeType::MODEL_PART && mv->config.has("extruder")) + changed = true; + if (!mv->mmu_segmentation_facets.empty()) + changed = true; + } + } + + if (!changed) + return false; + + take_snapshot("Assign Texture Mapping Zone"); + + if (Plater *plater = wxGetApp().plater(); plater != nullptr) + if (GLCanvas3D *canvas = plater->canvas3D(); canvas != nullptr) + canvas->get_gizmos_manager().reset_all_states(); + + for (size_t obj_idx : valid_object_idxs) { + ModelObject *model_object = (*m_objects)[obj_idx]; + if (model_object->config.has("extruder")) + model_object->config.set("extruder", extruder); + else + model_object->config.set_key_value("extruder", new ConfigOptionInt(extruder)); + + for (ModelVolume *mv : model_object->volumes) { + if (mv->type() == ModelVolumeType::MODEL_PART && mv->config.has("extruder")) + mv->config.erase("extruder"); + if (!mv->mmu_segmentation_facets.empty()) + mv->mmu_segmentation_facets.reset(); + } + + wxDataViewItem item = m_objects_model->GetItemById(int(obj_idx)); + if (item.IsOk()) { + m_objects_model->SetExtruder(wxString::Format("%d", extruder), item); + update_info_items(obj_idx); + } + } + + wxGetApp().plater()->update(); + wxGetApp().plater()->object_list_changed(); + Refresh(); + return true; +} + void ObjectList::on_plate_added(PartPlate* part_plate) { wxDataViewItem plate_item = m_objects_model->AddPlate(part_plate); diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index b97eff05ae8..e544993e3ec 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -456,6 +456,7 @@ public: //BBS: remove const qualifier void set_extruder_for_selected_items(const int extruder); + bool assign_extruder_to_objects_and_clear_filament_region_painting(const std::vector& object_idxs, const int extruder); wxDataViewItemArray reorder_volumes_and_get_selection(int obj_idx, std::function add_to_selection = nullptr); void apply_volumes_order(); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 3a1a470d491..341e0fa604c 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1793,6 +1793,7 @@ private: if (!apply_to(preview)) return; preview.surface_pattern = int(TextureMappingZone::Gradient2D); + preview.apply_default_modulation_mode(); m_live_preview(preview); } @@ -1848,6 +1849,7 @@ public: bool seam_hiding, bool nonlinear_offset_adjustment, int modulation_mode, + bool modulation_mode_manually_changed, bool recolor_small_perimeter_loops, bool recolor_top_visible_perimeter_sections, int top_visible_perimeter_recolor_aggressiveness, @@ -1875,6 +1877,7 @@ public: { (void) generic_solver_mix_model; (void) reduce_outer_surface_texture; + m_modulation_mode_manually_changed = modulation_mode_manually_changed; m_strength_offsets_expanded = initial_strength_offsets_expanded; const int gap = FromDIP(8); auto *root = new wxBoxSizer(wxVERTICAL); @@ -2128,7 +2131,10 @@ public: int(TextureMappingZone::ModulationPerimeterPath))); modulation_mode_row->Add(m_modulation_mode_choice, 1, wxALIGN_CENTER_VERTICAL); print_settings_root->Add(modulation_mode_row, 0, wxEXPAND | wxALL, gap); - m_modulation_mode_choice->Bind(wxEVT_CHOICE, [this](wxCommandEvent &) { update_modulation_mode_options_visibility(false); }); + m_modulation_mode_choice->Bind(wxEVT_CHOICE, [this](wxCommandEvent &) { + m_modulation_mode_manually_changed = true; + update_modulation_mode_options_visibility(false); + }); auto *print_settings_box = new wxStaticBoxSizer(wxVERTICAL, print_settings_page, @@ -2458,6 +2464,7 @@ public: int(TextureMappingZone::ModulationPerimeterPath)) : TextureMappingZone::DefaultModulationMode; } + bool modulation_mode_manually_changed() const { return m_modulation_mode_manually_changed; } bool compact_offset_mode() const { return dithering_enabled() || (m_compact_offset_mode_checkbox && m_compact_offset_mode_checkbox->GetValue()); } bool recolor_small_perimeter_loops() const { @@ -2880,6 +2887,7 @@ private: wxCheckBox *m_seam_hiding_checkbox {nullptr}; wxCheckBox *m_nonlinear_offset_adjustment_checkbox {nullptr}; wxChoice *m_modulation_mode_choice {nullptr}; + bool m_modulation_mode_manually_changed {false}; wxCheckBox *m_recolor_small_perimeter_loops_checkbox {nullptr}; wxCheckBox *m_recolor_top_visible_perimeter_sections_checkbox {nullptr}; wxStaticText *m_top_visible_perimeter_recolor_aggressiveness_label {nullptr}; @@ -6806,6 +6814,27 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager) canvas->reload_scene(true, true); }; + auto selected_texture_mapping_object_idxs = [this]() { + std::vector object_idxs; + auto append_selection = [this, &object_idxs](const Selection &selection) { + if (selection.is_empty() || selection.is_wipe_tower()) + return; + for (const auto &item : selection.get_content()) { + if (item.first < 0 || p->plater == nullptr || size_t(item.first) >= p->plater->model().objects.size()) + continue; + const size_t obj_idx = size_t(item.first); + if (std::find(object_idxs.begin(), object_idxs.end(), obj_idx) == object_idxs.end()) + object_idxs.emplace_back(obj_idx); + } + }; + if (p->plater != nullptr) { + if (GLCanvas3D *canvas = p->plater->get_current_canvas3D()) + append_selection(canvas->get_selection()); + append_selection(p->plater->get_selection()); + } + return object_idxs; + }; + auto texture_mapping_zone_affects_scene = [print_cfg](unsigned int zone_id) { const Plater *plater = wxGetApp().plater(); return plater != nullptr && model_uses_texture_mapping_zone_id(plater->model(), print_cfg, zone_id); @@ -6907,17 +6936,21 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager) rows[zone_index] = std::move(updated); mgr_ptr->normalize_zone_ids(num_physical); const unsigned int after_zone_id = rows[zone_index].zone_id; + const bool linear_gradient_preview_change = before.is_linear_gradient() || rows[zone_index].is_linear_gradient(); const bool affects_scene = texture_mapping_zone_affects_scene(before_zone_id) || texture_mapping_zone_affects_scene(after_zone_id); refresh_summary_preview(rows[zone_index]); set_config_string("texture_mapping_definitions", serialize_texture_mapping_manager(mgr_ptr)); if (preview_only_change) { notify_change(false); - if (affects_scene) + if (affects_scene || linear_gradient_preview_change) refresh_texture_mapping_preview(); } - else + else { notify_change(affects_scene); + if (linear_gradient_preview_change) + refresh_texture_mapping_preview(); + } return affects_scene; }; @@ -7055,12 +7088,14 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager) set_start_btn_ref, set_end_btn_ref, clear_points_btn_ref, + surface_choice, linear_gradient_mode_choice, editor_ref, row_ref, update_texture_mapping_area_height](GLGizmoTextureGradientPointPicker::Target active_target) { const TextureMappingZone *zone = mgr_ptr != nullptr && zone_index < mgr_ptr->zones().size() ? &mgr_ptr->zones()[zone_index] : nullptr; - const bool radial_gradient = linear_gradient_mode_choice != nullptr && linear_gradient_mode_choice->GetSelection() == 1; + const bool linear_gradient = surface_choice != nullptr && surface_choice->GetSelection() == 1; + const bool radial_gradient = linear_gradient && linear_gradient_mode_choice != nullptr && linear_gradient_mode_choice->GetSelection() == 1; const bool has_start = zone != nullptr && zone->linear_gradient_start.valid; const bool has_end = zone != nullptr && zone->linear_gradient_end.valid; if (wxButton *button = set_start_btn_ref.get()) { @@ -7068,18 +7103,22 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager) (has_start ? _L("Set Center") : _L("Add Center")) : (has_start ? _L("Set Start") : _L("Add Start")); button->SetLabel(active_target == GLGizmoTextureGradientPointPicker::Target::Start ? _L("Cancel selection") : inactive_label); + button->Show(linear_gradient); + button->Enable(linear_gradient); button->InvalidateBestSize(); button->Fit(); } if (wxButton *button = set_end_btn_ref.get()) { button->SetLabel(active_target == GLGizmoTextureGradientPointPicker::Target::End ? _L("Cancel selection") : (has_end ? _L("Set End") : _L("Add End"))); - button->Show(!radial_gradient); - button->Enable(!radial_gradient); + button->Show(linear_gradient && !radial_gradient); + button->Enable(linear_gradient && !radial_gradient); button->InvalidateBestSize(); button->Fit(); } - if (wxButton *button = clear_points_btn_ref.get()) - button->Enable(has_start || has_end); + if (wxButton *button = clear_points_btn_ref.get()) { + button->Show(linear_gradient); + button->Enable(linear_gradient && (has_start || has_end)); + } if (wxWindow *window = editor_ref.get()) window->Layout(); if (wxWindow *window = row_ref.get()) @@ -7189,6 +7228,7 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager) } updated.enabled = true; updated.surface_pattern = surface_pattern; + updated.apply_default_modulation_mode(); updated.component_ids = encode_texture_mapping_component_ids(ids); if (updated.is_linear_gradient() && !updated.linear_gradient_stops.empty()) { updated.component_a = updated.linear_gradient_stops.front().filament_id; @@ -7365,6 +7405,7 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager) updated.enabled = true; updated.surface_pattern = surface_pattern; + updated.apply_default_modulation_mode(); updated.component_ids = encode_texture_mapping_component_ids(ids); if (updated.is_linear_gradient() && !updated.linear_gradient_stops.empty()) { updated.component_a = updated.linear_gradient_stops.front().filament_id; @@ -7451,6 +7492,7 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager) }); if (linear_gradient_radius_spin != nullptr) { linear_gradient_radius_spin->Bind(wxEVT_SPINCTRLDOUBLE, [apply_controls](wxSpinDoubleEvent &) { apply_controls(); }); + linear_gradient_radius_spin->Bind(wxEVT_TEXT, [apply_controls](wxCommandEvent &) { apply_controls(); }); linear_gradient_radius_spin->Bind(wxEVT_TEXT_ENTER, [apply_controls](wxCommandEvent &) { apply_controls(); }); linear_gradient_radius_spin->Bind(wxEVT_KILL_FOCUS, [apply_controls](wxFocusEvent &evt) { apply_controls(); @@ -7515,6 +7557,7 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager) else updated.linear_gradient_end = anchor; updated.surface_pattern = int(TextureMappingZone::LinearGradient); + updated.apply_default_modulation_mode(); if (updated == mgr_ptr->zones()[zone_index]) return; sync_current_model_texture_mapping_definitions(serialize_texture_mapping_manager(mgr_ptr)); @@ -7567,6 +7610,7 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager) const std::string original_serialized = serialize_texture_mapping_manager(mgr_ptr); TextureMappingZone active_preview = original; active_preview.surface_pattern = int(TextureMappingZone::LinearGradient); + active_preview.apply_default_modulation_mode(); active_preview.show_linear_gradient_direction_arrow = true; auto live_preview_applied = std::make_shared(false); auto redraw_live_preview = [this]() { @@ -7634,6 +7678,7 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager) else preview.linear_gradient_end = anchor; preview.surface_pattern = int(TextureMappingZone::LinearGradient); + preview.apply_default_modulation_mode(); apply_live_preview(std::move(preview)); }, cancel_live_selection); if (gizmos.get_current_type() != GLGizmosManager::TextureGradientPointPicker) @@ -7661,6 +7706,7 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager) TextureMappingZone updated = mgr_ptr->zones()[zone_index]; updated.clear_linear_gradient_points(); updated.surface_pattern = int(TextureMappingZone::LinearGradient); + updated.apply_default_modulation_mode(); if (updated == mgr_ptr->zones()[zone_index]) return; sync_current_model_texture_mapping_definitions(serialize_texture_mapping_manager(mgr_ptr)); @@ -7725,6 +7771,7 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager) return; } updated.surface_pattern = int(TextureMappingZone::Gradient2D); + updated.apply_default_modulation_mode(); apply_zone(std::move(updated)); CallAfter([this]() { update_texture_mapping_panel(false); }); }); @@ -7777,6 +7824,7 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager) updated.seam_hiding, updated.nonlinear_offset_adjustment, updated.modulation_mode, + updated.modulation_mode_manually_changed, updated.recolor_small_perimeter_loops, updated.recolor_top_visible_perimeter_sections, updated.top_visible_perimeter_recolor_aggressiveness, @@ -7818,6 +7866,8 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager) updated.seam_hiding = dlg.seam_hiding(); updated.nonlinear_offset_adjustment = dlg.nonlinear_offset_adjustment(); updated.modulation_mode = dlg.modulation_mode(); + updated.modulation_mode_manually_changed = dlg.modulation_mode_manually_changed(); + updated.apply_default_modulation_mode(); updated.recolor_small_perimeter_loops = dlg.recolor_small_perimeter_loops(); updated.recolor_top_visible_perimeter_sections = dlg.recolor_top_visible_perimeter_sections(); updated.top_visible_perimeter_recolor_aggressiveness = dlg.top_visible_perimeter_recolor_aggressiveness(); @@ -7916,20 +7966,43 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager) evt.StopPropagation(); evt.Skip(); }); - menu_btn->Bind(wxEVT_BUTTON, [this, zone_index, num_physical, physical_colors, mgr_ptr, persist_rows, menu_btn, bundle, set_config_string, texture_mapping_zone_affects_scene](wxCommandEvent &) { + menu_btn->Bind(wxEVT_BUTTON, [this, zone_index, num_physical, physical_colors, mgr_ptr, persist_rows, menu_btn, bundle, set_config_string, + texture_mapping_zone_affects_scene, selected_texture_mapping_object_idxs, refresh_texture_mapping_preview](wxCommandEvent &) { if (menu_btn == nullptr) return; wxMenu menu; + const int assign_selected_objects_id = wxWindow::NewControlId(); const int duplicate_id = wxWindow::NewControlId(); const int delete_id = wxWindow::NewControlId(); + const int delete_all_id = wxWindow::NewControlId(); + wxMenuItem *assign_selected_objects_item = menu.Append(assign_selected_objects_id, _L("Assign to selected objects")); + if (assign_selected_objects_item != nullptr) + assign_selected_objects_item->Enable(!selected_texture_mapping_object_idxs().empty()); menu.Append(duplicate_id, _L("Duplicate")); menu.Append(delete_id, _L("Delete")); - menu.Bind(wxEVT_COMMAND_MENU_SELECTED, [this, zone_index, num_physical, physical_colors, mgr_ptr, persist_rows, duplicate_id, delete_id, bundle, set_config_string, texture_mapping_zone_affects_scene](wxCommandEvent &evt) { + menu.Append(delete_all_id, _L("Delete All Texture Mapping Zones")); + menu.Bind(wxEVT_COMMAND_MENU_SELECTED, [this, zone_index, num_physical, physical_colors, mgr_ptr, persist_rows, assign_selected_objects_id, + duplicate_id, delete_id, delete_all_id, bundle, set_config_string, + texture_mapping_zone_affects_scene, selected_texture_mapping_object_idxs, + refresh_texture_mapping_preview, menu_btn](wxCommandEvent &evt) { if (mgr_ptr == nullptr) return; auto &rows = mgr_ptr->zones(); if (zone_index >= rows.size()) return; + if (evt.GetId() == assign_selected_objects_id) { + std::vector object_idxs = selected_texture_mapping_object_idxs(); + if (object_idxs.empty()) { + MessageDialog(menu_btn, _L("No objects are currently selected."), _L("Assign to selected objects"), wxOK | wxICON_INFORMATION).ShowModal(); + return; + } + const unsigned int zone_id = rows[zone_index].zone_id; + if (zone_id == 0 || obj_list() == nullptr) + return; + if (obj_list()->assign_extruder_to_objects_and_clear_filament_region_painting(object_idxs, int(zone_id))) + refresh_texture_mapping_preview(); + return; + } if (evt.GetId() == duplicate_id) { mgr_ptr->duplicate_zone(zone_index, num_physical, physical_colors); persist_rows(false); @@ -7950,6 +8023,33 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager) p->m_expanded_texture_mapping_rows.clear(); persist_rows(affects_scene); CallAfter([this]() { update_texture_mapping_panel(false); }); + return; + } + if (evt.GetId() == delete_all_id) { + if (MessageDialog(menu_btn, + _L("Delete all texture mapping zones?"), + wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Delete All Texture Mapping Zones"), + wxYES_NO | wxNO_DEFAULT | wxICON_WARNING).ShowModal() != wxID_YES) + return; + bool affects_scene = false; + const uint64_t settings_zone_uid = bundle->texture_mapping_global_settings.prime_tower_settings_zone_uid; + bool clear_settings_zone_uid = false; + for (const TextureMappingZone &zone : rows) { + if (!zone.enabled || zone.deleted) + continue; + affects_scene |= texture_mapping_zone_affects_scene(zone.zone_id); + clear_settings_zone_uid |= settings_zone_uid != 0 && zone.stable_id == settings_zone_uid; + } + rows.clear(); + if (clear_settings_zone_uid) { + TextureMappingGlobalSettings settings = bundle->texture_mapping_global_settings; + settings.prime_tower_settings_zone_uid = 0; + bundle->texture_mapping_global_settings = settings; + set_config_string("texture_mapping_global_settings", bundle->texture_mapping_global_settings.serialize()); + } + p->m_expanded_texture_mapping_rows.clear(); + persist_rows(affects_scene); + CallAfter([this]() { update_texture_mapping_panel(false); }); } }); menu_btn->PopupMenu(&menu, wxPoint(0, menu_btn->GetSize().GetHeight()));