From 2d4b431d5f3994350efe1de63ebd44c32646ba51 Mon Sep 17 00:00:00 2001 From: Ian Bassi Date: Wed, 29 Jul 2026 16:27:15 -0300 Subject: [PATCH] Improve dimmed layers (#15001) Co-authored-by: yw4z Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com> --- src/libslic3r/AppConfig.cpp | 17 ++++++++- src/libvgcode/include/Viewer.hpp | 9 +++-- src/libvgcode/src/Settings.hpp | 6 ++-- src/libvgcode/src/Viewer.cpp | 10 ++++++ src/libvgcode/src/ViewerImpl.cpp | 61 ++++++++++++++++++++------------ src/libvgcode/src/ViewerImpl.hpp | 7 +++- src/slic3r/GUI/GCodeViewer.cpp | 3 +- src/slic3r/GUI/GCodeViewer.hpp | 5 ++- src/slic3r/GUI/Preferences.cpp | 32 ++++++++++++++++- src/slic3r/GUI/Preferences.hpp | 2 ++ 10 files changed, 119 insertions(+), 33 deletions(-) diff --git a/src/libslic3r/AppConfig.cpp b/src/libslic3r/AppConfig.cpp index dcbead0ddd..159d9bbeda 100644 --- a/src/libslic3r/AppConfig.cpp +++ b/src/libslic3r/AppConfig.cpp @@ -202,10 +202,25 @@ void AppConfig::set_defaults() if (get("seq_top_layer_only").empty()) set("seq_top_layer_only", "1"); - // ORCA: darken layers below the current one while scrubbing the preview (ported from preFlight) + // ORCA: darken the layers the preview layer slider is not scrubbed to if (get("preview_dim_previous_layers").empty()) set_bool("preview_dim_previous_layers", false); + // ORCA: brightness of those dimmed layers, in percent. 0 = black, capped at 99 because + // 100 would render them unchanged, which is what disabling the option already does + if (get("preview_dim_previous_layers_brightness").empty()) + set("preview_dim_previous_layers_brightness", "40"); + else { + int brightness = 40; + try { + brightness = std::stoi(get("preview_dim_previous_layers_brightness")); + } + catch (...) { + brightness = 40; + } + set("preview_dim_previous_layers_brightness", std::to_string(std::max(0, std::min(brightness, 99)))); + } + if (get("filaments_area_preferred_count").empty()) set("filaments_area_preferred_count", "10"); diff --git a/src/libvgcode/include/Viewer.hpp b/src/libvgcode/include/Viewer.hpp index 7c0a0c295a..5141245d96 100644 --- a/src/libvgcode/include/Viewer.hpp +++ b/src/libvgcode/include/Viewer.hpp @@ -91,12 +91,15 @@ public: // void toggle_top_layer_only_view_range(); // - // Dim previous layers (ORCA, ported from preFlight) - // Whether the layers below the current top layer are rendered darkened while - // scrubbing below the full print, so only the current layer is shown at full brightness. + // Dim previous layers (ORCA) + // Whether the layers the layer slider is not scrubbed to are rendered darkened while + // showing less than the full print, so only the inspected layer(s) are at full brightness. + // How bright those darkened layers are rendered, 1.0 = unchanged, 0.0 = black. // bool is_dim_previous_layers() const; void set_dim_previous_layers(bool value); + float get_dim_previous_layers_brightness() const; + void set_dim_previous_layers_brightness(float value); // // Returns true if the given option is visible. // diff --git a/src/libvgcode/src/Settings.hpp b/src/libvgcode/src/Settings.hpp index 89cb9771b4..b3aa371c4a 100644 --- a/src/libvgcode/src/Settings.hpp +++ b/src/libvgcode/src/Settings.hpp @@ -19,9 +19,11 @@ struct Settings EViewType view_type{ EViewType::FeatureType }; ETimeMode time_mode{ ETimeMode::Normal }; bool top_layer_only_view_range{ false }; - // ORCA: when enabled, all layers below the current top layer are rendered - // darkened (keeping their color) while scrubbing below the full print (ported from preFlight) + // ORCA: when enabled, every layer the layer slider is not scrubbed to is rendered + // darkened (keeping its color) while showing less than the full print bool dim_previous_layers{ false }; + // ORCA: how bright those darkened layers are rendered, 1.0 = unchanged, 0.0 = black + float dim_previous_layers_brightness{ 0.4f }; bool spiral_vase_mode{ false }; // // Required update flags diff --git a/src/libvgcode/src/Viewer.cpp b/src/libvgcode/src/Viewer.cpp index a36cf011ce..eb606598e9 100644 --- a/src/libvgcode/src/Viewer.cpp +++ b/src/libvgcode/src/Viewer.cpp @@ -82,6 +82,16 @@ void Viewer::set_dim_previous_layers(bool value) m_impl->set_dim_previous_layers(value); } +float Viewer::get_dim_previous_layers_brightness() const +{ + return m_impl->get_dim_previous_layers_brightness(); +} + +void Viewer::set_dim_previous_layers_brightness(float value) +{ + m_impl->set_dim_previous_layers_brightness(value); +} + bool Viewer::is_option_visible(EOptionType type) const { return m_impl->is_option_visible(type); diff --git a/src/libvgcode/src/ViewerImpl.cpp b/src/libvgcode/src/ViewerImpl.cpp index 9804e12b9e..da1a601149 100644 --- a/src/libvgcode/src/ViewerImpl.cpp +++ b/src/libvgcode/src/ViewerImpl.cpp @@ -1223,16 +1223,12 @@ static float encode_color(const Color& color) { return static_cast(i_color); } -// ORCA: how much the layers below the current top layer are darkened when -// Settings::dim_previous_layers is enabled (ported from preFlight). 0.0 = no change, 1.0 = black. -static constexpr float PREVIOUS_LAYER_DARKEN_FACTOR = 0.60f; - -// ORCA: returns the encoded color scaled towards black by 'factor', preserving its hue -static float encode_color_darkened(const Color& color, float factor) { - const float keep = 1.0f - factor; - const int r = static_cast(color[0] * keep); - const int g = static_cast(color[1] * keep); - const int b = static_cast(color[2] * keep); +// ORCA: returns the encoded color scaled towards black by 'brightness', preserving its hue. +// 1.0 = no change, 0.0 = black. +static float encode_color_dimmed(const Color& color, float brightness) { + const int r = static_cast(color[0] * brightness); + const int g = static_cast(color[1] * brightness); + const int b = static_cast(color[2] * brightness); const int i_color = r << 16 | g << 8 | b; return static_cast(i_color); } @@ -1248,15 +1244,20 @@ void ViewerImpl::update_colors_texture() const size_t top_layer_id = m_settings.top_layer_only_view_range ? m_layers.get_view_range()[1] : 0; const bool color_top_layer_only = m_view_range.get_full()[1] != m_view_range.get_visible()[1]; - // ORCA: when dim_previous_layers is enabled, darken every layer below the current top layer - // (keeping its color) whenever we are not rendering the whole print, so that only the layer - // being scrubbed to is shown at full brightness (ported from preFlight). This shares - // top_layer_id with the greying path, so it only applies while in top-layer-only mode - that - // way the moves slider still animates normally across all layers when that mode is disabled. - const bool dim_previous_layers = m_settings.dim_previous_layers && !m_layers.empty(); - const bool full_render = (m_layers.get_view_range()[0] == 0) && - (m_layers.get_view_range()[1] >= static_cast(m_layers.count()) - 1) && - (m_view_range.get_visible()[1] == m_view_range.get_full()[1]); + // ORCA: when dim_previous_layers is enabled, darken every layer (keeping its color) except the + // one(s) the layer slider is being scrubbed to, so that only those are shown at full brightness. + // A slider thumb marks a layer as inspected only once it is moved away from + // its end of the print: the upper one while it is below the last layer (or while the moves + // slider is not at the end of the layer), the lower one while it is above the first layer, so + // trimming the print from the bottom lights up the lowest visible layer and using the slider as + // a range lights up both ends. When neither thumb is moved the whole print is rendered normally. + // Gated on top-layer-only mode, which the greying path below also keys off of, so that the moves + // slider still animates normally across all layers when that mode is disabled. + const Interval& layers_range = m_layers.get_view_range(); + const bool inspecting_top_layer = layers_range[1] + 1 < m_layers.count() || color_top_layer_only; + const bool inspecting_bottom_layer = layers_range[0] > 0; + const bool dim_previous_layers = m_settings.dim_previous_layers && m_settings.top_layer_only_view_range && + !m_layers.empty() && (inspecting_top_layer || inspecting_bottom_layer); // Based on current settings and slider position, we might want to render some // vertices as dark grey (or darkened, see above). Use either that or the normal color (from the cache). @@ -1265,9 +1266,13 @@ void ViewerImpl::update_colors_texture() for (size_t i=0; i ViewerImpl::get_time_modes() const { std::vector ret; diff --git a/src/libvgcode/src/ViewerImpl.hpp b/src/libvgcode/src/ViewerImpl.hpp index 8a91d5524f..4da312fc0e 100644 --- a/src/libvgcode/src/ViewerImpl.hpp +++ b/src/libvgcode/src/ViewerImpl.hpp @@ -85,9 +85,14 @@ public: bool is_top_layer_only_view_range() const { return m_settings.top_layer_only_view_range; } void toggle_top_layer_only_view_range(); - // ORCA: darken layers below the current top layer while scrubbing (ported from preFlight) + // ORCA: darken every layer the layer slider is not scrubbed to, so that only the inspected + // one(s) - the top thumb's layer, the bottom thumb's layer, or both - stay at full + // brightness. dim_previous_layers_brightness sets how dark the rest go, 1.0 = unchanged, + // 0.0 = black bool is_dim_previous_layers() const { return m_settings.dim_previous_layers; } void set_dim_previous_layers(bool value); + float get_dim_previous_layers_brightness() const { return m_settings.dim_previous_layers_brightness; } + void set_dim_previous_layers_brightness(float value); bool is_spiral_vase_mode() const { return m_settings.spiral_vase_mode; } diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 20bcb109e1..b882117aae 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -1134,8 +1134,9 @@ void GCodeViewer::load_as_gcode(const GCodeProcessorResult& gcode_result, const if (current_top_layer_only != required_top_layer_only) m_viewer.toggle_top_layer_only_view_range(); - // ORCA: darken layers below the current one while scrubbing the preview (ported from preFlight) + // ORCA: darken the layers the preview layer slider is not scrubbed to m_viewer.set_dim_previous_layers(get_app_config()->get_bool("preview_dim_previous_layers")); + m_viewer.set_dim_previous_layers_brightness(0.01f * std::stoi(get_app_config()->get("preview_dim_previous_layers_brightness"))); // avoid processing if called with the same gcode_result if (m_last_result_id == gcode_result.id && wxGetApp().is_editor()) { diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 570c688987..a19f7bb9ae 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -333,9 +333,12 @@ public: libvgcode::EViewType get_view_type() const { return m_viewer.get_view_type(); } - // ORCA: darken layers below the current top layer while scrubbing the preview (ported from preFlight) + // ORCA: darken the layers not scrubbed to while using the preview layer slider void set_dim_previous_layers(bool value) { m_viewer.set_dim_previous_layers(value); } bool is_dim_previous_layers() const { return m_viewer.is_dim_previous_layers(); } + // ORCA: brightness of those darkened layers, 1.0 = unchanged, 0.0 = black + void set_dim_previous_layers_brightness(float value) { m_viewer.set_dim_previous_layers_brightness(value); } + float get_dim_previous_layers_brightness() const { return m_viewer.get_dim_previous_layers_brightness(); } void set_layers_z_range(const std::array& layers_z_range); diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index bd5b7420ff..6bcc00848b 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -700,6 +700,12 @@ wxBoxSizer *PreferencesDialog::create_item_spinctrl(wxString title, wxString tit auto input = new SpinInput(m_parent, wxEmptyString, side_label, wxDefaultPosition, DESIGN_INPUT_SIZE, wxSP_ARROW_KEYS, min, max, stoi(app_config->get(param))); input->SetToolTip(tip); + // ORCA: this one is only meaningful while the dimming it controls is enabled + if (param == "preview_dim_previous_layers_brightness") { + m_dim_previous_layers_brightness_input = input; + input->Enable(app_config->get_bool("preview_dim_previous_layers")); + } + m_sizer->Add(input, 0, wxALIGN_CENTER_VERTICAL); if(!title2.empty()){ @@ -1050,8 +1056,10 @@ wxBoxSizer *PreferencesDialog::create_item_checkbox(wxString title, wxString too wxGetApp().mainframe->m_webview->SendCloudProvidersInfo(); } } - // ORCA: apply the preview dimming change immediately to the currently loaded preview (ported from preFlight) + // ORCA: apply the preview dimming change immediately to the currently loaded preview else if (param == "preview_dim_previous_layers") { + if (m_dim_previous_layers_brightness_input) + m_dim_previous_layers_brightness_input->Enable(app_config->get_bool(param)); if (Plater* plater = wxGetApp().plater()) { if (GLCanvas3D* canvas = plater->get_preview_canvas3D()) { canvas->get_gcode_viewer().set_dim_previous_layers(app_config->get_bool(param)); @@ -1914,6 +1922,28 @@ void PreferencesDialog::create_items() ); g_sizer->Add(item_dim_previous_layers); + auto item_dim_previous_layers_brightness = create_item_spinctrl( + _L("Dimmed layer brightness"), + "", + _L("%"), + _L("How brightly the dimmed layers are rendered when \"Dim lower layers\" is enabled.\n" + "99% is barely darkened, 0% renders them black. Capped at 99% because 100% would be the same as disabling the option."), + "preview_dim_previous_layers_brightness", + 0, + 99, + // ORCA: apply the new brightness immediately to the currently loaded preview + [](int value) { + if (Plater* plater = wxGetApp().plater()) { + if (GLCanvas3D* canvas = plater->get_preview_canvas3D()) { + canvas->get_gcode_viewer().set_dim_previous_layers_brightness(0.01f * value); + canvas->set_as_dirty(); + canvas->request_extra_frame(); + } + } + } + ); + g_sizer->Add(item_dim_previous_layers_brightness); + g_sizer->AddSpacer(FromDIP(10)); sizer_page->Add(g_sizer, 0, wxEXPAND); diff --git a/src/slic3r/GUI/Preferences.hpp b/src/slic3r/GUI/Preferences.hpp index 95bfcb5367..94340ce619 100644 --- a/src/slic3r/GUI/Preferences.hpp +++ b/src/slic3r/GUI/Preferences.hpp @@ -13,6 +13,7 @@ #include "Widgets/ComboBox.hpp" #include "Widgets/CheckBox.hpp" #include "Widgets/TextInput.hpp" +#include "Widgets/SpinInput.hpp" #include "Widgets/TabCtrl.hpp" #include "slic3r/Utils/bambu_networking.hpp" @@ -71,6 +72,7 @@ public: ::CheckBox * m_sync_user_preset_checkbox = {nullptr}; ::CheckBox * m_bambu_cloud_checkbox = {nullptr}; ::TextInput *m_backup_interval_textinput = {nullptr}; + ::SpinInput *m_dim_previous_layers_brightness_input = {nullptr}; ::ComboBox * m_network_version_combo = {nullptr}; std::vector m_available_versions;