From 4fea9f5d2cfe2125619a901be5597827df567c49 Mon Sep 17 00:00:00 2001 From: Rad Date: Fri, 13 Feb 2026 21:45:31 +0100 Subject: [PATCH] Add changelog for v0.7-alpha pre-release: Introduce experimental features including Local Z reliability improvements, manual pattern mixed filaments, selective expansion/contraction controls, and a refreshed UI for mixed filaments. Enhance configuration options with surface indentation for mixed filament behavior tuning. Update PrintConfig and GUI components to support new features and improve user experience. --- CHANGELOG_v0.7.md | 75 ++++++++ src/libslic3r/PresetBundle.cpp | 1 + src/libslic3r/Print.cpp | 1 + src/libslic3r/PrintApply.cpp | 8 + src/libslic3r/PrintConfig.cpp | 18 +- src/libslic3r/PrintConfig.hpp | 1 + src/libslic3r/PrintObject.cpp | 1 + src/libslic3r/PrintObjectSlice.cpp | 284 ++++++++++++++++++++++------- src/slic3r/GUI/Plater.cpp | 204 +++++++-------------- src/slic3r/GUI/Tab.cpp | 2 + 10 files changed, 392 insertions(+), 203 deletions(-) create mode 100644 CHANGELOG_v0.7.md diff --git a/CHANGELOG_v0.7.md b/CHANGELOG_v0.7.md new file mode 100644 index 0000000000..4a1e79bf04 --- /dev/null +++ b/CHANGELOG_v0.7.md @@ -0,0 +1,75 @@ +## v0.7-alpha Pre-release + +EXPERIMENTAL BUILD - NOT TESTED +Based on Snapmaker Orca v2.2.4 + +v0.7 full spectrum alpha focuses on Local Z reliability, better mixed-filament control, and a cleaner custom Gradient/Pattern workflow. + +This has had limited testing on the Snapmaker U1 via the community. + +### What's New in v0.7 + +#### Local Z Dithering Reliability (v0.7) +- Improved handling for overlapping mixed-painted zones on the same XY plane. +- Fixed cases where layer-height modulation could flatten or become equal when a second zone starts. +- Fixed stale first-zone behavior where editing one gradient did not correctly update resulting layer heights. +- Reduced repeated same-color layer runs in complex multi-zone transitions. +- Mixed passes and base passes are separated more cleanly in perimeter clipping paths. + +#### Manual Pattern Mixed Filaments (v0.6, refined in v0.7) +- Pattern add button in Mixed Filaments UI (alongside Gradient add). +- Custom repeating layer patterns (for example `11112222` for a 4:4 ratio). +- Pattern parser accepts `1/2` or `A/B` with separators (`/`, `-`, `_`, `|`, etc.). +- Manual pattern takes precedence; blend percentage is auto-derived from the pattern. +- Backward-compatible parsing for older custom mixed-row formats. + +#### Local Z Dithering Mode (v0.4, refined in v0.7) +- Local Z dithering mode in Process -> Others (`dithering_local_z_mode`). +- Mixed-painted zones are split into local sub-passes with pass-specific flow height. +- Runtime guard remains active when wipe tower or wiping overrides are enabled. + +#### Selective Expansion/Contraction (v0.7) +- Added selective expansion/contraction controls for mixed-color behavior tuning in challenging transition regions. +- Intended to help with edge consistency where mixed passes interact with neighboring toolpaths. + +#### Mixed Filaments UI Refresh (v0.7) +- More modern and compact layout for custom Gradient and Pattern mixed filaments. +- Reduced spacing and tighter row cards for better readability with many mixed rows. +- Cleaner preview area with summary inline for faster scanning. + +#### Config/Save/Load Reliability (v0.5 + v0.7 hardening) +- Config comparison checks type and value. +- Improved dirty-state detection, diffing, and unsaved-changes behavior. +- Mixed filament project settings load more consistently. +- Stronger logging for mixed-row parsing and config application. + +#### Same-layer Pointillisme (new in v0.7, experimental) +- What it is: a same-layer color interleaving mode that tries to distribute multiple filaments inside a single layer instead of alternating by layer. +- Goal: test whether very small in-layer color "dots" can produce smoother visual blends than normal layer cycling. +- Status: extremely experimental and may produce unusable results. +- Reality check: this is likely a dead end for production use on current hardware and may be removed or replaced in a future release. + +### Installation +1. Download `Snapmaker_Orca.zip`. +2. Extract to a folder. +3. Run the executable. + +### Warning +- Use at your own risk. +- May produce incorrect G-code. +- Local Z behavior is still experimental. +- Seeking testers with U1 printers. + +### Features Not Yet Tested +1. Local Z height dithering +2. Patterns +3. Selective expansion/contraction +4. Advanced dithering +5. Height-weight cadence + +### Known Issues +- Untested on real hardware. +- Local Z mode still needs validation with real prints. +- Advanced dithering output may require calibration. +- Pointillisme is research-grade only right now and is likely not viable for stable, repeatable prints. +- Startup crash may still occur in some setups and needs further debugging. diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp index 4e9ecb9299..62b8f5281f 100644 --- a/src/libslic3r/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -47,6 +47,7 @@ static std::vector s_project_options { "mixed_filament_height_upper_bound", "mixed_filament_cycle_layers", "mixed_filament_advanced_dithering", + "mixed_filament_surface_indentation", "mixed_filament_definitions", "mixed_color_layer_height_a", "mixed_color_layer_height_b", diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 33f21b0586..59b13e59fe 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -253,6 +253,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n || opt_key == "mixed_filament_height_upper_bound" || opt_key == "mixed_filament_cycle_layers" || opt_key == "mixed_filament_advanced_dithering" + || opt_key == "mixed_filament_surface_indentation" || opt_key == "mixed_filament_definitions" // Spiral Vase forces different kind of slicing than the normal model: // In Spiral Vase mode, holes are closed and only the largest area contour is kept at each layer. diff --git a/src/libslic3r/PrintApply.cpp b/src/libslic3r/PrintApply.cpp index a5da429e3b..d2caa3fd95 100644 --- a/src/libslic3r/PrintApply.cpp +++ b/src/libslic3r/PrintApply.cpp @@ -1177,6 +1177,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ new_full_config.option("mixed_filament_advanced_dithering", true); new_full_config.option("mixed_filament_pointillism_pixel_size", true); new_full_config.option("mixed_filament_pointillism_line_gap", true); + new_full_config.option("mixed_filament_surface_indentation", true); new_full_config.option("mixed_filament_definitions", true); m_config.option("dithering_z_step_size", true); m_config.option("dithering_local_z_mode", true); @@ -1188,6 +1189,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ m_config.option("mixed_filament_advanced_dithering", true); m_config.option("mixed_filament_pointillism_pixel_size", true); m_config.option("mixed_filament_pointillism_line_gap", true); + m_config.option("mixed_filament_surface_indentation", true); m_config.option("mixed_filament_definitions", true); m_default_object_config.option("dithering_z_step_size", true); m_default_object_config.option("dithering_local_z_mode", true); @@ -1199,6 +1201,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ m_default_object_config.option("mixed_filament_advanced_dithering", true); m_default_object_config.option("mixed_filament_pointillism_pixel_size", true); m_default_object_config.option("mixed_filament_pointillism_line_gap", true); + m_default_object_config.option("mixed_filament_surface_indentation", true); m_default_object_config.option("mixed_filament_definitions", true); // BBS int used_filaments = this->extruders(true).size(); @@ -1304,6 +1307,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ bool mixed_advanced_dither = false; float mixed_pointillism_pixel_size = 0.f; float mixed_pointillism_line_gap = 0.f; + float mixed_surface_indentation = 0.f; std::string mixed_custom_definitions; if (new_full_config.has("mixed_filament_gradient_mode")) { if (const ConfigOptionBool *opt = new_full_config.option("mixed_filament_gradient_mode")) @@ -1327,6 +1331,8 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ mixed_pointillism_pixel_size = float(new_full_config.opt_float("mixed_filament_pointillism_pixel_size")); if (new_full_config.has("mixed_filament_pointillism_line_gap")) mixed_pointillism_line_gap = float(new_full_config.opt_float("mixed_filament_pointillism_line_gap")); + if (new_full_config.has("mixed_filament_surface_indentation")) + mixed_surface_indentation = float(new_full_config.opt_float("mixed_filament_surface_indentation")); if (new_full_config.has("mixed_filament_definitions")) mixed_custom_definitions = new_full_config.opt_string("mixed_filament_definitions"); @@ -1336,6 +1342,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ mixed_cycle_layers = std::max(2, mixed_cycle_layers); mixed_pointillism_pixel_size = std::max(0.f, mixed_pointillism_pixel_size); mixed_pointillism_line_gap = std::max(0.f, mixed_pointillism_line_gap); + mixed_surface_indentation = std::clamp(mixed_surface_indentation, -2.f, 2.f); BOOST_LOG_TRIVIAL(info) << "Print::apply mixed settings" << ", gradient_mode=" << mixed_gradient_mode @@ -1345,6 +1352,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ << ", advanced_dither=" << (mixed_advanced_dither ? 1 : 0) << ", pointillism_pixel_size=" << mixed_pointillism_pixel_size << ", pointillism_line_gap=" << mixed_pointillism_line_gap + << ", surface_indentation=" << mixed_surface_indentation << ", custom_definitions_len=" << mixed_custom_definitions.size() << ", physical_extruders=" << num_extruders; diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index face5fe77a..f46eb9f4a3 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -4198,7 +4198,8 @@ void PrintConfigDef::init_fff_params() def->label = L("Pointillisme pixel size"); def->category = L("Others"); def->tooltip = L("Length of one pointillisme segment along an extrusion path for same-layer pointillisme mode. " - "Set to 0 to use automatic nozzle-based sizing."); + "Set to 0 to use automatic nozzle-based sizing.\n\n" + "Warning: Same-layer pointillisme is extremely experimental and may produce unusable results."); def->sidetext = "mm"; def->min = 0.; def->mode = comAdvanced; @@ -4208,12 +4209,25 @@ void PrintConfigDef::init_fff_params() def->label = L("Pointillisme line gap"); def->category = L("Others"); def->tooltip = L("Optional non-extruded spacing between adjacent pointillisme segments. " - "Increase carefully to improve separation and print quality."); + "Increase carefully to improve separation and print quality.\n\n" + "Warning: Same-layer pointillisme is extremely experimental and may produce unusable results."); def->sidetext = "mm"; def->min = 0.; def->mode = comAdvanced; def->set_default_value(new ConfigOptionFloat(0.0)); + def = this->add("mixed_filament_surface_indentation", coFloat); + def->label = L("Selective Expansion contraction"); + def->category = L("Others"); + def->tooltip = L("XY offset applied to mixed-filament painted regions before region assignment.\n\n" + "Positive values contract the mixed zone inward. Negative values expand it outward.\n\n" + "This applies to mixed filament usage in layer cadence, height cadence, same-layer pointillisme, and local Z dithering."); + def->sidetext = "mm"; + def->min = -2.0; + def->max = 2.0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0.0)); + def = this->add("mixed_filament_definitions", coString); def->label = L("Mixed filament custom definitions"); def->tooltip = L("Serialized custom mixed filament rows.\n\n" diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 703879ab83..ae7518923e 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -1360,6 +1360,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE( ((ConfigOptionBool, mixed_filament_advanced_dithering)) ((ConfigOptionFloat, mixed_filament_pointillism_pixel_size)) ((ConfigOptionFloat, mixed_filament_pointillism_line_gap)) + ((ConfigOptionFloat, mixed_filament_surface_indentation)) ((ConfigOptionString, mixed_filament_definitions)) ((ConfigOptionFloat, dithering_z_step_size)) ((ConfigOptionBool, dithering_local_z_mode)) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 1e8fbbb6c4..1ba786eafb 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -973,6 +973,7 @@ bool PrintObject::invalidate_state_by_config_options( || opt_key == "mixed_filament_height_upper_bound" || opt_key == "mixed_filament_cycle_layers" || opt_key == "mixed_filament_advanced_dithering" + || opt_key == "mixed_filament_surface_indentation" || opt_key == "mixed_filament_definitions") { // Mixed filament gradient controls affect layer cadence and virtual // tool distribution, so force a re-slice prompt like other diff --git a/src/libslic3r/PrintObjectSlice.cpp b/src/libslic3r/PrintObjectSlice.cpp index 074c397948..136ebf91cf 100644 --- a/src/libslic3r/PrintObjectSlice.cpp +++ b/src/libslic3r/PrintObjectSlice.cpp @@ -871,6 +871,109 @@ static coordf_t float_from_full_config(const DynamicPrintConfig &full_cfg, const return coordf_t(full_cfg.opt_float(key)); } +static bool apply_mixed_surface_indentation(PrintObject &print_object, std::vector> &segmentation) +{ + const Print *print = print_object.print(); + if (print == nullptr || segmentation.empty()) + return false; + + const PrintConfig &print_cfg = print->config(); + const DynamicPrintConfig &full_cfg = print->full_print_config(); + coordf_t indentation_mm = float_from_full_config(full_cfg, "mixed_filament_surface_indentation", + coordf_t(print_cfg.mixed_filament_surface_indentation.value)); + indentation_mm = std::clamp(indentation_mm, coordf_t(-2.f), coordf_t(2.f)); + if (std::abs(indentation_mm) <= EPSILON) + return false; + + const size_t num_physical = print_cfg.filament_colour.size(); + const size_t num_channels = segmentation.front().size(); + if (num_channels <= num_physical) + return false; + + const MixedFilamentManager &mixed_mgr = print->mixed_filament_manager(); + const bool expand_outward = indentation_mm < 0.f; + const float delta_scaled = float(scale_(std::abs(double(indentation_mm)))); + if (delta_scaled <= float(EPSILON)) + return false; + + size_t changed_layers = 0; + size_t changed_states = 0; + size_t emptied_states = 0; + size_t overlap_clipped_states = 0; + + for (size_t layer_id = 0; layer_id < segmentation.size(); ++layer_id) { + if (segmentation[layer_id].size() != num_channels) + continue; + + bool layer_changed = false; + ExPolygons occupied; + if (expand_outward) { + for (size_t channel_idx = 0; channel_idx < num_channels; ++channel_idx) { + const ExPolygons &state_masks = segmentation[layer_id][channel_idx]; + if (state_masks.empty()) + continue; + + const unsigned int state_id = unsigned(channel_idx + 1); + if (!mixed_mgr.is_mixed(state_id, num_physical)) + append(occupied, state_masks); + } + if (occupied.size() > 1) + occupied = union_ex(occupied); + } + + for (size_t channel_idx = num_physical; channel_idx < num_channels; ++channel_idx) { + ExPolygons &state_masks = segmentation[layer_id][channel_idx]; + if (state_masks.empty()) + continue; + + const unsigned int state_id = unsigned(channel_idx + 1); + if (!mixed_mgr.is_mixed(state_id, num_physical)) + continue; + + ExPolygons adjusted = offset_ex(state_masks, expand_outward ? delta_scaled : -delta_scaled); + if (!adjusted.empty() && adjusted.size() > 1) + adjusted = union_ex(adjusted); + + if (expand_outward && !adjusted.empty() && !occupied.empty()) { + ExPolygons clipped = diff_ex(adjusted, occupied, ApplySafetyOffset::Yes); + if (std::abs(area(clipped)) + EPSILON < std::abs(area(adjusted))) + ++overlap_clipped_states; + adjusted = std::move(clipped); + if (!adjusted.empty() && adjusted.size() > 1) + adjusted = union_ex(adjusted); + } + + state_masks = std::move(adjusted); + if (state_masks.empty()) + ++emptied_states; + ++changed_states; + layer_changed = true; + + if (expand_outward && !state_masks.empty()) { + append(occupied, state_masks); + if (occupied.size() > 1) + occupied = union_ex(occupied); + } + } + + if (layer_changed) + ++changed_layers; + } + + if (changed_states == 0) + return false; + + BOOST_LOG_TRIVIAL(warning) << "Mixed surface indentation applied" + << " object=" << (print_object.model_object() ? print_object.model_object()->name : std::string("")) + << " indentation_mm=" << indentation_mm + << " direction=" << (expand_outward ? "outward" : "inward") + << " changed_layers=" << changed_layers + << " changed_states=" << changed_states + << " emptied_states=" << emptied_states + << " overlap_clipped_states=" << overlap_clipped_states; + return true; +} + static bool fit_pass_heights_to_interval(std::vector &passes, double base_height, double lo, double hi) { if (passes.empty() || base_height <= EPSILON) @@ -1255,6 +1358,16 @@ static std::vector pointillism_sequence_for_row(const MixedFilamen return sequence; } +static bool local_z_eligible_mixed_row(const MixedFilament &mf) +{ + // Local-Z flow-height modulation is intended for custom gradient rows. + // Keep auto-generated premix rows and manual pattern rows on nominal layers. + return mf.enabled && + mf.custom && + mf.manual_pattern.empty() && + mf.distribution_mode != int(MixedFilament::SameLayerPointillisme); +} + static size_t unique_extruder_count(const std::vector &sequence, size_t num_physical) { if (sequence.empty() || num_physical == 0) @@ -1773,20 +1886,16 @@ static void build_local_z_plan(PrintObject &print_object, const std::vector row_next_component_a(mixed_rows.size(), uint8_t(1)); + std::vector row_phase_initialized(mixed_rows.size(), uint8_t(0)); int cadence_index = 0; for (size_t layer_id = 0; layer_id < print_object.layer_count(); ++layer_id) { @@ -1813,16 +1922,24 @@ static void build_local_z_plan(PrintObject &print_object, const std::vector dominant_mixed_area) { - dominant_mixed_area = mixed_area; - const int resolved_mixed_idx = mixed_mgr.mixed_index_from_filament_id(state_id, num_physical); - dominant_mixed_idx = resolved_mixed_idx >= 0 ? size_t(resolved_mixed_idx) : size_t(-1); - } + if (!mixed_mgr.is_mixed(state_id, num_physical)) + continue; + + const int mixed_idx = mixed_mgr.mixed_index_from_filament_id(state_id, num_physical); + if (mixed_idx < 0 || size_t(mixed_idx) >= mixed_rows.size()) + continue; + const MixedFilament &mf = mixed_rows[size_t(mixed_idx)]; + if (!local_z_eligible_mixed_row(mf)) + continue; + + interval.has_mixed_paint = true; + ++mixed_state_count; + append(mixed_masks, state_masks); + + const double mixed_area = std::abs(area(state_masks)); + if (mixed_area > dominant_mixed_area) { + dominant_mixed_area = mixed_area; + dominant_mixed_idx = size_t(mixed_idx); } } if (dominant_mixed_idx < mixed_rows.size()) { @@ -1830,25 +1947,6 @@ static void build_local_z_plan(PrintObject &print_object, const std::vector> mm_segmentation = multi_material_segmentation_by_painting(*this, [print]() { print->throw_if_canceled(); }); + apply_mixed_surface_indentation(*this, mm_segmentation); // Same-layer pointillisme is applied in G-code path domain (segment-level assignment), // not by XY state mask splitting, to avoid boolean-induced voids. BOOST_LOG_TRIVIAL(info) << "Same-layer pointillisme uses path-domain G-code segmentation"; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index c976d9e032..69789d572b 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2215,7 +2215,7 @@ public: , m_value(std::clamp(value_percent, 0, 100)) { SetBackgroundStyle(wxBG_STYLE_PAINT); - SetMinSize(wxSize(FromDIP(96), FromDIP(14))); + SetMinSize(wxSize(FromDIP(96), FromDIP(12))); Bind(wxEVT_PAINT, &MixedGradientSelector::on_paint, this); Bind(wxEVT_LEFT_DOWN, &MixedGradientSelector::on_left_down, this); Bind(wxEVT_LEFT_UP, &MixedGradientSelector::on_left_up, this); @@ -2785,7 +2785,7 @@ public: : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE) { SetBackgroundStyle(wxBG_STYLE_PAINT); - SetMinSize(wxSize(FromDIP(132), FromDIP(24))); + SetMinSize(wxSize(FromDIP(120), FromDIP(20))); Bind(wxEVT_PAINT, &MixedMixPreview::on_paint, this); } @@ -3254,6 +3254,7 @@ void Sidebar::update_mixed_filament_panel() int cycle_layers = std::max(2, get_mixed_int("mixed_filament_cycle_layers", 4)); float pointillism_pixel_size = std::max(0.f, get_mixed_float("mixed_filament_pointillism_pixel_size", 0.f)); float pointillism_line_gap = std::max(0.f, get_mixed_float("mixed_filament_pointillism_line_gap", 0.f)); + float mixed_surface_indentation = std::clamp(get_mixed_float("mixed_filament_surface_indentation", 0.f), -2.f, 2.f); bool advanced_dithering = get_mixed_bool("mixed_filament_advanced_dithering", false); const std::string mixed_definitions = get_mixed_string("mixed_filament_definitions"); @@ -3273,20 +3274,27 @@ void Sidebar::update_mixed_filament_panel() set_mixed_int("mixed_filament_cycle_layers", cycle_layers); set_mixed_float("mixed_filament_pointillism_pixel_size", pointillism_pixel_size); set_mixed_float("mixed_filament_pointillism_line_gap", pointillism_line_gap); + set_mixed_float("mixed_filament_surface_indentation", mixed_surface_indentation); set_mixed_string("mixed_filament_definitions", mixed_mgr.serialize_custom_entries()); } auto &mixed = mixed_mgr.mixed_filaments(); + const int compact_gap_x = FromDIP(6); + const int compact_gap_y = FromDIP(4); + const int compact_row_pad = FromDIP(6); + const wxColour mixed_rows_bg(246, 248, 251); + const wxColour mixed_row_bg(255, 255, 255); + p->m_panel_mixed_filaments->SetBackgroundColour(mixed_rows_bg); p->m_sizer_mixed_filaments->Clear(true); // Header with title and add buttons (gradient / pattern). auto *header_row = new wxPanel(p->m_panel_mixed_filaments, wxID_ANY); - header_row->SetBackgroundColour(wxColour(255, 255, 255)); + header_row->SetBackgroundColour(mixed_rows_bg); auto *header_sizer = new wxBoxSizer(wxHORIZONTAL); auto *mixed_title = new wxStaticText(header_row, wxID_ANY, _L("Mixed Filaments")); mixed_title->SetFont(Label::Head_14); - header_sizer->Add(mixed_title, 1, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(8)); + header_sizer->Add(mixed_title, 1, wxALIGN_CENTER_VERTICAL | wxLEFT, compact_gap_x); auto add_custom_row = [this, preset_bundle, physical_colors, get_mixed_mode, get_mixed_int, get_mixed_float, get_mixed_bool, set_mixed_string, notify_mixed_change](bool pattern_row) { if (physical_colors.size() < 2) @@ -3329,25 +3337,25 @@ void Sidebar::update_mixed_filament_panel() }; auto *grad_label = new wxStaticText(header_row, wxID_ANY, _L("Gradient")); - header_sizer->Add(grad_label, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, FromDIP(4)); - auto *add_gradient_btn = new wxButton(header_row, wxID_ANY, "+", wxDefaultPosition, wxSize(FromDIP(24), FromDIP(22))); + header_sizer->Add(grad_label, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, FromDIP(3)); + auto *add_gradient_btn = new wxButton(header_row, wxID_ANY, "+", wxDefaultPosition, wxSize(FromDIP(22), FromDIP(20)), wxBU_EXACTFIT); add_gradient_btn->SetToolTip(_L("Add custom gradient mixed filament")); add_gradient_btn->Enable(num_physical >= 2); add_gradient_btn->Bind(wxEVT_BUTTON, [add_custom_row](wxCommandEvent &) { add_custom_row(false); }); - header_sizer->Add(add_gradient_btn, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, FromDIP(8)); + header_sizer->Add(add_gradient_btn, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, compact_gap_x); auto *pattern_label = new wxStaticText(header_row, wxID_ANY, _L("Pattern")); - header_sizer->Add(pattern_label, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, FromDIP(4)); - auto *add_pattern_btn = new wxButton(header_row, wxID_ANY, "+", wxDefaultPosition, wxSize(FromDIP(24), FromDIP(22))); + header_sizer->Add(pattern_label, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, FromDIP(3)); + auto *add_pattern_btn = new wxButton(header_row, wxID_ANY, "+", wxDefaultPosition, wxSize(FromDIP(22), FromDIP(20)), wxBU_EXACTFIT); add_pattern_btn->SetToolTip(_L("Add custom pattern mixed filament")); add_pattern_btn->Enable(num_physical >= 2); add_pattern_btn->Bind(wxEVT_BUTTON, [add_custom_row](wxCommandEvent &) { add_custom_row(true); }); - header_sizer->Add(add_pattern_btn, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, FromDIP(8)); + header_sizer->Add(add_pattern_btn, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, compact_gap_x); header_row->SetSizer(header_sizer); - p->m_sizer_mixed_filaments->AddSpacer(FromDIP(2)); - p->m_sizer_mixed_filaments->Add(header_row, 0, wxEXPAND); - p->m_sizer_mixed_filaments->AddSpacer(FromDIP(2)); + p->m_sizer_mixed_filaments->AddSpacer(FromDIP(1)); + p->m_sizer_mixed_filaments->Add(header_row, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(2)); + p->m_sizer_mixed_filaments->AddSpacer(FromDIP(1)); if (mixed.empty()) { p->m_panel_mixed_filaments->Hide(); @@ -3355,125 +3363,50 @@ void Sidebar::update_mixed_filament_panel() return; } - // Global gradient settings row. - auto *settings_row = new wxPanel(p->m_panel_mixed_filaments, wxID_ANY); - settings_row->SetBackgroundColour(wxColour(255, 255, 255)); - auto *settings_sizer = new wxBoxSizer(wxHORIZONTAL); - settings_sizer->AddSpacer(FromDIP(4)); - - settings_sizer->Add(new wxStaticText(settings_row, wxID_ANY, _L("Lower")), 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(6)); - auto *lower_spin = new wxSpinCtrlDouble(settings_row, wxID_ANY, wxEmptyString, wxDefaultPosition, - wxSize(FromDIP(70), -1), wxSP_ARROW_KEYS, 0.01, 10.0, lower_bound, 0.01); - settings_sizer->Add(lower_spin, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(3)); - - settings_sizer->Add(new wxStaticText(settings_row, wxID_ANY, _L("Upper")), 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(6)); - auto *upper_spin = new wxSpinCtrlDouble(settings_row, wxID_ANY, wxEmptyString, wxDefaultPosition, - wxSize(FromDIP(70), -1), wxSP_ARROW_KEYS, 0.01, 10.0, upper_bound, 0.01); - settings_sizer->Add(upper_spin, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(3)); - - settings_sizer->Add(new wxStaticText(settings_row, wxID_ANY, _L("Cycle")), 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(6)); - auto *cycle_spin = new wxSpinCtrl(settings_row, wxID_ANY, wxEmptyString, wxDefaultPosition, - wxSize(FromDIP(56), -1), wxSP_ARROW_KEYS, 2, 32, cycle_layers); - settings_sizer->Add(cycle_spin, 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, FromDIP(3)); - - settings_sizer->Add(new wxStaticText(settings_row, wxID_ANY, _L("Pixel")), 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(6)); - auto *pixel_spin = new wxSpinCtrlDouble(settings_row, wxID_ANY, wxEmptyString, wxDefaultPosition, - wxSize(FromDIP(70), -1), wxSP_ARROW_KEYS, 0.0, 10.0, pointillism_pixel_size, 0.01); - pixel_spin->SetToolTip(_L("Pointillisme segment length in mm. 0 means automatic nozzle-based sizing.")); - settings_sizer->Add(pixel_spin, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(3)); - - settings_sizer->Add(new wxStaticText(settings_row, wxID_ANY, _L("Gap")), 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(6)); - auto *gap_spin = new wxSpinCtrlDouble(settings_row, wxID_ANY, wxEmptyString, wxDefaultPosition, - wxSize(FromDIP(70), -1), wxSP_ARROW_KEYS, 0.0, 10.0, pointillism_line_gap, 0.01); - gap_spin->SetToolTip(_L("Optional spacing between pointillisme line segments (mm).")); - settings_sizer->Add(gap_spin, 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, FromDIP(3)); - settings_row->SetSizer(settings_sizer); - p->m_sizer_mixed_filaments->Add(settings_row, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(4)); - p->m_sizer_mixed_filaments->AddSpacer(FromDIP(2)); - auto *rows_scroller = new wxScrolledWindow(p->m_panel_mixed_filaments, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL | wxTAB_TRAVERSAL); - rows_scroller->SetScrollRate(0, FromDIP(8)); + rows_scroller->SetScrollRate(0, FromDIP(6)); rows_scroller->ShowScrollbars(wxSHOW_SB_NEVER, wxSHOW_SB_DEFAULT); - rows_scroller->SetBackgroundColour(wxColour(255, 255, 255)); + rows_scroller->SetBackgroundColour(mixed_rows_bg); auto *rows_sizer = new wxBoxSizer(wxVERTICAL); rows_scroller->SetSizer(rows_sizer); - auto apply_settings = [this, preset_bundle, get_mixed_mode, get_mixed_bool, lower_spin, upper_spin, cycle_spin, pixel_spin, gap_spin, - set_mixed_int, set_mixed_float, set_mixed_string, notify_mixed_change]() { - const int mode = get_mixed_mode(false) ? 1 : 0; - const bool advanced = get_mixed_bool("mixed_filament_advanced_dithering", false); - float lo = std::max(0.01f, float(lower_spin->GetValue())); - float hi = std::max(lo, float(upper_spin->GetValue())); - int cycle = std::max(2, cycle_spin->GetValue()); - float pixel = std::max(0.f, float(pixel_spin->GetValue())); - float gap = std::max(0.f, float(gap_spin->GetValue())); - if (pixel > 1e-6f) - gap = std::min(gap, pixel * 0.90f); - if (std::abs(float(upper_spin->GetValue()) - hi) > 1e-6f) - upper_spin->SetValue(hi); - if (std::abs(float(gap_spin->GetValue()) - gap) > 1e-6f) - gap_spin->SetValue(gap); - - set_mixed_float("mixed_filament_height_lower_bound", lo); - set_mixed_float("mixed_filament_height_upper_bound", hi); - set_mixed_int("mixed_filament_cycle_layers", cycle); - set_mixed_float("mixed_filament_pointillism_pixel_size", pixel); - set_mixed_float("mixed_filament_pointillism_line_gap", gap); - - auto &mgr = preset_bundle->mixed_filaments; - mgr.apply_gradient_settings(mode, lo, hi, cycle, advanced); - set_mixed_string("mixed_filament_definitions", mgr.serialize_custom_entries()); - notify_mixed_change(); - - this->CallAfter([this]() { - update_dynamic_filament_list(); - update_mixed_filament_panel(); - }); - }; - lower_spin->Bind(wxEVT_SPINCTRLDOUBLE, [apply_settings](wxSpinDoubleEvent &) { apply_settings(); }); - upper_spin->Bind(wxEVT_SPINCTRLDOUBLE, [apply_settings](wxSpinDoubleEvent &) { apply_settings(); }); - cycle_spin->Bind(wxEVT_SPINCTRL, [apply_settings](wxSpinEvent &) { apply_settings(); }); - pixel_spin->Bind(wxEVT_SPINCTRLDOUBLE, [apply_settings](wxSpinDoubleEvent &) { apply_settings(); }); - gap_spin->Bind(wxEVT_SPINCTRLDOUBLE, [apply_settings](wxSpinDoubleEvent &) { apply_settings(); }); - lower_spin->Bind(wxEVT_TEXT, [apply_settings](wxCommandEvent &) { apply_settings(); }); - upper_spin->Bind(wxEVT_TEXT, [apply_settings](wxCommandEvent &) { apply_settings(); }); - cycle_spin->Bind(wxEVT_TEXT, [apply_settings](wxCommandEvent &) { apply_settings(); }); - pixel_spin->Bind(wxEVT_TEXT, [apply_settings](wxCommandEvent &) { apply_settings(); }); - gap_spin->Bind(wxEVT_TEXT, [apply_settings](wxCommandEvent &) { apply_settings(); }); - lower_spin->Bind(wxEVT_TEXT_ENTER, [apply_settings](wxCommandEvent &) { apply_settings(); }); - upper_spin->Bind(wxEVT_TEXT_ENTER, [apply_settings](wxCommandEvent &) { apply_settings(); }); - cycle_spin->Bind(wxEVT_TEXT_ENTER, [apply_settings](wxCommandEvent &) { apply_settings(); }); - pixel_spin->Bind(wxEVT_TEXT_ENTER, [apply_settings](wxCommandEvent &) { apply_settings(); }); - gap_spin->Bind(wxEVT_TEXT_ENTER, [apply_settings](wxCommandEvent &) { apply_settings(); }); - lower_spin->Bind(wxEVT_KILL_FOCUS, [apply_settings](wxFocusEvent &evt) { apply_settings(); evt.Skip(); }); - upper_spin->Bind(wxEVT_KILL_FOCUS, [apply_settings](wxFocusEvent &evt) { apply_settings(); evt.Skip(); }); - cycle_spin->Bind(wxEVT_KILL_FOCUS, [apply_settings](wxFocusEvent &evt) { apply_settings(); evt.Skip(); }); - pixel_spin->Bind(wxEVT_KILL_FOCUS, [apply_settings](wxFocusEvent &evt) { apply_settings(); evt.Skip(); }); - gap_spin->Bind(wxEVT_KILL_FOCUS, [apply_settings](wxFocusEvent &evt) { apply_settings(); evt.Skip(); }); - for (size_t mixed_id = 0; mixed_id < mixed.size(); ++mixed_id) { MixedFilament &mf = mixed[mixed_id]; - auto *row = new wxPanel(rows_scroller, wxID_ANY); - row->SetBackgroundColour(wxColour(255, 255, 255)); + const std::string normalized_pattern = MixedFilamentManager::normalize_manual_pattern(mf.manual_pattern); + const bool pattern_row_mode = !normalized_pattern.empty(); + + auto *row = new wxPanel(rows_scroller, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBORDER_SIMPLE); + row->SetBackgroundColour(mixed_row_bg); auto *row_sizer = new wxBoxSizer(wxHORIZONTAL); auto *content_sizer = new wxBoxSizer(wxVERTICAL); auto *title_row = new wxBoxSizer(wxHORIZONTAL); wxColour swatch_color(mf.display_color); - auto *swatch = new wxPanel(row, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(14), FromDIP(14))); + auto *swatch = new wxPanel(row, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(12), FromDIP(12))); swatch->SetBackgroundColour(swatch_color); - swatch->SetMinSize(wxSize(FromDIP(14), FromDIP(14))); - title_row->Add(swatch, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(4)); + swatch->SetMinSize(wxSize(FromDIP(12), FromDIP(12))); + title_row->Add(swatch, 0, wxALIGN_CENTER_VERTICAL); const int virtual_filament_id = int(num_physical + mixed_id + 1); auto *name_label = new wxStaticText(row, wxID_ANY, wxString::Format("Mixed Filament %d", virtual_filament_id)); - title_row->Add(name_label, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(6)); + title_row->Add(name_label, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, compact_gap_x); if (!mf.custom) { auto *pair_label = new wxStaticText(row, wxID_ANY, wxString::Format("(Filament %u + Filament %u)", unsigned(mf.component_a), unsigned(mf.component_b))); - title_row->Add(pair_label, 1, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(6)); + pair_label->SetForegroundColour(wxColour(96, 96, 96)); + title_row->Add(pair_label, 1, wxALIGN_CENTER_VERTICAL | wxLEFT, compact_gap_x); + } else { + auto *pair_label = new wxStaticText(row, wxID_ANY, wxString::Format("F%u + F%u", + unsigned(mf.component_a), unsigned(mf.component_b))); + pair_label->SetForegroundColour(wxColour(96, 96, 96)); + title_row->Add(pair_label, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, compact_gap_x); + + title_row->AddStretchSpacer(1); + auto *kind_label = new wxStaticText(row, wxID_ANY, pattern_row_mode ? _L("Pattern") : _L("Gradient")); + kind_label->SetForegroundColour(wxColour(84, 84, 84)); + title_row->Add(kind_label, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, compact_gap_x); } content_sizer->Add(title_row, 0, wxEXPAND); @@ -3494,10 +3427,8 @@ void Sidebar::update_mixed_filament_panel() picker_row->Add(choice_a, 1, wxALIGN_CENTER_VERTICAL); picker_row->Add(new wxStaticText(row, wxID_ANY, "+"), 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, FromDIP(6)); picker_row->Add(choice_b, 1, wxALIGN_CENTER_VERTICAL); - content_sizer->Add(picker_row, 0, wxEXPAND | wxLEFT | wxTOP, FromDIP(8)); + content_sizer->Add(picker_row, 0, wxEXPAND | wxLEFT | wxTOP, compact_gap_y); - const std::string normalized_pattern = MixedFilamentManager::normalize_manual_pattern(mf.manual_pattern); - const bool pattern_row_mode = !normalized_pattern.empty(); MixedGradientSelector *blend_selector = nullptr; wxStaticText *blend_label = nullptr; wxTextCtrl *pattern_ctrl = nullptr; @@ -3523,20 +3454,21 @@ void Sidebar::update_mixed_filament_panel() distribution_row->Add(new wxStaticText(row, wxID_ANY, _L("Mode")), 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, FromDIP(6)); distribution_choice = new wxChoice(row, wxID_ANY, wxDefaultPosition, wxDefaultSize, distribution_choices); distribution_choice->SetSelection(row_distribution_mode); - distribution_choice->SetToolTip(_L("Choose whether this mixed row alternates by layer or interleaves colors on the same layer.")); + distribution_choice->SetToolTip(_L("Choose whether this mixed row alternates by layer or interleaves colors on the same layer.\n\n" + "Warning: Same-layer pointillisme is extremely experimental and may produce unusable results.")); distribution_row->Add(distribution_choice, 1, wxALIGN_CENTER_VERTICAL); - content_sizer->Add(distribution_row, 0, wxEXPAND | wxLEFT | wxTOP, FromDIP(8)); + content_sizer->Add(distribution_row, 0, wxEXPAND | wxLEFT | wxTOP, compact_gap_y); if (pattern_row_mode) { auto *pattern_row = new wxBoxSizer(wxHORIZONTAL); pattern_row->Add(new wxStaticText(row, wxID_ANY, _L("Pattern")), 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, FromDIP(6)); pattern_ctrl = new wxTextCtrl(row, wxID_ANY, from_u8(normalized_pattern), wxDefaultPosition, - wxSize(FromDIP(170), -1), wxTE_PROCESS_ENTER); + wxSize(FromDIP(152), -1), wxTE_PROCESS_ENTER); pattern_ctrl->SetToolTip(_L("Manual repeating pattern. Use 1/2 or A/B for component A/B, " "and 3..9 for direct physical filament IDs. " "Example: 1/1/1/1/2/2/2/2 or 1/2/3/4.")); pattern_row->Add(pattern_ctrl, 1, wxALIGN_CENTER_VERTICAL); - content_sizer->Add(pattern_row, 0, wxEXPAND | wxLEFT | wxTOP, FromDIP(8)); + content_sizer->Add(pattern_row, 0, wxEXPAND | wxLEFT | wxTOP, compact_gap_y); auto *insert_row = new wxBoxSizer(wxHORIZONTAL); insert_row->Add(new wxStaticText(row, wxID_ANY, _L("Insert")), 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, FromDIP(6)); @@ -3544,10 +3476,10 @@ void Sidebar::update_mixed_filament_panel() pattern_insert_choice->SetSelection(component_a - 1); pattern_insert_choice->SetToolTip(_L("Select a physical filament to append into the pattern.")); insert_row->Add(pattern_insert_choice, 1, wxALIGN_CENTER_VERTICAL); - pattern_insert_btn = new wxButton(row, wxID_ANY, "+", wxDefaultPosition, wxSize(FromDIP(28), FromDIP(24)), wxBU_EXACTFIT); + pattern_insert_btn = new wxButton(row, wxID_ANY, "+", wxDefaultPosition, wxSize(FromDIP(24), FromDIP(22)), wxBU_EXACTFIT); pattern_insert_btn->SetToolTip(_L("Append selected filament ID to pattern")); insert_row->Add(pattern_insert_btn, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(6)); - content_sizer->Add(insert_row, 0, wxEXPAND | wxLEFT | wxTOP, FromDIP(8)); + content_sizer->Add(insert_row, 0, wxEXPAND | wxLEFT | wxTOP, compact_gap_y); auto *quick_row = new wxBoxSizer(wxHORIZONTAL); quick_row->Add(new wxStaticText(row, wxID_ANY, _L("Filaments")), 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, FromDIP(6)); @@ -3562,7 +3494,7 @@ void Sidebar::update_mixed_filament_panel() pattern_quick_filament_buttons.emplace_back(btn); } quick_row->Add(quick_buttons, 1, wxALIGN_CENTER_VERTICAL); - content_sizer->Add(quick_row, 0, wxEXPAND | wxLEFT | wxTOP, FromDIP(8)); + content_sizer->Add(quick_row, 0, wxEXPAND | wxLEFT | wxTOP, compact_gap_y); } else { wxArrayString optional_filament_choices; optional_filament_choices.Add(_L("None")); @@ -3594,7 +3526,7 @@ void Sidebar::update_mixed_filament_panel() blend_selector = new MixedGradientSelector(row, color_a, color_b, std::clamp(mf.mix_b_percent, 0, 100)); auto *blend_row = new wxBoxSizer(wxHORIZONTAL); blend_row->Add(blend_selector, 1, wxEXPAND); - content_sizer->Add(blend_row, 0, wxEXPAND | wxLEFT | wxTOP, FromDIP(8)); + content_sizer->Add(blend_row, 0, wxEXPAND | wxLEFT | wxTOP, compact_gap_y); const bool same_layer_mode = row_distribution_mode == int(MixedFilament::SameLayerPointillisme); blend_label = new wxStaticText(row, wxID_ANY, multi_gradient_mode ? @@ -3607,7 +3539,7 @@ void Sidebar::update_mixed_filament_panel() auto *ratio_row = new wxBoxSizer(wxHORIZONTAL); ratio_row->AddStretchSpacer(1); ratio_row->Add(blend_label, 0, wxALIGN_CENTER_VERTICAL); - content_sizer->Add(ratio_row, 0, wxEXPAND | wxLEFT | wxTOP, FromDIP(8)); + content_sizer->Add(ratio_row, 0, wxEXPAND | wxLEFT | wxTOP, compact_gap_y); if (num_physical >= 3 && !simple_mode) { add_extra_color_btn = new wxButton(row, wxID_ANY, "+", wxDefaultPosition, wxSize(FromDIP(24), FromDIP(22)), wxBU_EXACTFIT); @@ -3624,7 +3556,7 @@ void Sidebar::update_mixed_filament_panel() extra_row->Add(choice_c, 1, wxALIGN_CENTER_VERTICAL); extra_row->Add(new wxStaticText(row, wxID_ANY, "+"), 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, FromDIP(6)); extra_row->Add(choice_d, 1, wxALIGN_CENTER_VERTICAL); - content_sizer->Add(extra_row, 0, wxEXPAND | wxLEFT | wxTOP, FromDIP(8)); + content_sizer->Add(extra_row, 0, wxEXPAND | wxLEFT | wxTOP, compact_gap_y); } if (blend_selector) { @@ -3640,14 +3572,14 @@ void Sidebar::update_mixed_filament_panel() } auto *preview_row = new wxBoxSizer(wxHORIZONTAL); - preview_row->Add(new wxStaticText(row, wxID_ANY, _L("Preview")), 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, FromDIP(6)); + preview_row->Add(new wxStaticText(row, wxID_ANY, _L("Preview")), 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, compact_gap_x); mix_preview = new MixedMixPreview(row); preview_row->Add(mix_preview, 1, wxALIGN_CENTER_VERTICAL); - content_sizer->Add(preview_row, 0, wxEXPAND | wxLEFT | wxTOP, FromDIP(8)); mix_summary_label = new wxStaticText(row, wxID_ANY, wxEmptyString); mix_summary_label->SetForegroundColour(wxColour(90, 90, 90)); - content_sizer->Add(mix_summary_label, 0, wxEXPAND | wxLEFT | wxTOP, FromDIP(8)); + preview_row->Add(mix_summary_label, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, compact_gap_x); + content_sizer->Add(preview_row, 0, wxEXPAND | wxLEFT | wxTOP, compact_gap_y); { std::vector initial_sequence; @@ -3978,11 +3910,11 @@ void Sidebar::update_mixed_filament_panel() } } - row_sizer->Add(content_sizer, 1, wxEXPAND); + row_sizer->Add(content_sizer, 1, wxEXPAND | wxALL, compact_row_pad); auto *chk = new wxCheckBox(row, wxID_ANY, wxEmptyString); chk->SetValue(mf.enabled); - row_sizer->Add(chk, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxTOP, FromDIP(2)); + row_sizer->Add(chk, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, compact_row_pad); chk->Bind(wxEVT_CHECKBOX, [this, preset_bundle, mixed_id, chk, set_mixed_string, notify_mixed_change](wxCommandEvent &) { auto &mgr = preset_bundle->mixed_filaments; auto &mfs = mgr.mixed_filaments(); @@ -3999,16 +3931,16 @@ void Sidebar::update_mixed_filament_panel() }); row->SetSizer(row_sizer); - rows_sizer->Add(row, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(4)); - rows_sizer->AddSpacer(FromDIP(1)); + rows_sizer->Add(row, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, FromDIP(2)); + rows_sizer->AddSpacer(FromDIP(2)); } - rows_sizer->AddSpacer(FromDIP(4)); + rows_sizer->AddSpacer(FromDIP(2)); rows_scroller->Layout(); rows_scroller->FitInside(); - const int min_h = FromDIP(72); - const int max_h = FromDIP(260); + const int min_h = FromDIP(68); + const int max_h = FromDIP(220); const int content_h = std::max(0, rows_scroller->GetVirtualSize().GetHeight()); const int desired_h = std::clamp(content_h, min_h, max_h); rows_scroller->SetMinSize(wxSize(-1, desired_h)); @@ -4016,8 +3948,8 @@ void Sidebar::update_mixed_filament_panel() if (prev_rows_view_y > 0) rows_scroller->Scroll(0, prev_rows_view_y); - p->m_sizer_mixed_filaments->Add(rows_scroller, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(0)); - p->m_sizer_mixed_filaments->AddSpacer(FromDIP(4)); + p->m_sizer_mixed_filaments->Add(rows_scroller, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(2)); + p->m_sizer_mixed_filaments->AddSpacer(FromDIP(2)); p->m_panel_mixed_filaments->Show(); p->m_panel_mixed_filaments->Layout(); Layout(); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 34c128c532..3795fd79a0 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1791,6 +1791,7 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value) opt_key == "mixed_filament_advanced_dithering" || opt_key == "mixed_filament_pointillism_pixel_size" || opt_key == "mixed_filament_pointillism_line_gap" || + opt_key == "mixed_filament_surface_indentation" || opt_key == "dithering_z_step_size" || opt_key == "dithering_local_z_mode" || opt_key == "dithering_step_painted_zones_only" || @@ -2522,6 +2523,7 @@ optgroup->append_single_option_line("skirt_loops", "others_settings_skirt#loops" optgroup->append_single_option_line("mixed_filament_advanced_dithering"); optgroup->append_single_option_line("mixed_filament_pointillism_pixel_size"); optgroup->append_single_option_line("mixed_filament_pointillism_line_gap"); + optgroup->append_single_option_line("mixed_filament_surface_indentation"); optgroup->append_single_option_line("dithering_z_step_size"); optgroup->append_single_option_line("dithering_local_z_mode"); optgroup->append_single_option_line("dithering_step_painted_zones_only");