From a5a5cad09be6a3793fccb66162b3b5096e53bba2 Mon Sep 17 00:00:00 2001 From: Rodrigo Faselli <162915171+RF47@users.noreply.github.com> Date: Wed, 18 Feb 2026 18:38:20 -0300 Subject: [PATCH 1/2] Fix dual seam fuzzy painted rev 2 (#11923) * 2 seam fuzzy 2nd attempt * Update FuzzySkin.cpp * Fix debug SVG * solve bump artifact in extrusion junction joint * minor fixes * cleaning Update FuzzySkin.cpp --- src/libslic3r/Feature/FuzzySkin/FuzzySkin.cpp | 68 ++++++++++++++++--- src/libslic3r/Feature/FuzzySkin/FuzzySkin.hpp | 2 +- 2 files changed, 59 insertions(+), 11 deletions(-) diff --git a/src/libslic3r/Feature/FuzzySkin/FuzzySkin.cpp b/src/libslic3r/Feature/FuzzySkin/FuzzySkin.cpp index 667d739c7e..6f878fd79f 100644 --- a/src/libslic3r/Feature/FuzzySkin/FuzzySkin.cpp +++ b/src/libslic3r/Feature/FuzzySkin/FuzzySkin.cpp @@ -111,7 +111,7 @@ void fuzzy_polyline(Points& poly, bool closed, coordf_t slice_z, const FuzzySkin } // Thanks Cura developers for this function. -void fuzzy_extrusion_line(Arachne::ExtrusionJunctions& ext_lines, coordf_t slice_z, const FuzzySkinConfig& cfg) +void fuzzy_extrusion_line(Arachne::ExtrusionJunctions& ext_lines, coordf_t slice_z, const FuzzySkinConfig& cfg, bool closed) { std::unique_ptr noise = get_noise_module(cfg); @@ -124,9 +124,12 @@ void fuzzy_extrusion_line(Arachne::ExtrusionJunctions& ext_lines, coordf_t slice Arachne::ExtrusionJunctions out; out.reserve(ext_lines.size()); for (auto& p1 : ext_lines) { - if (p0->p == p1.p) { // Connect endpoints. - out.emplace_back(p1.p, p1.w, p1.perimeter_index); - continue; + // Orca: only skip the first point for closed path, open path should not skip any point + if (closed) { + if (p0->p == p1.p) { // Connect endpoints. + out.emplace_back(p1.p, p1.w, p1.perimeter_index); + continue; + } } // 'a' is the (next) new point between p0 and p1 @@ -266,11 +269,11 @@ Polygon apply_fuzzy_skin(const Polygon& polygon, const PerimeterGenerator& perim BoundingBox bbox = get_extents(perimeter_generator.slices->surfaces); bbox.offset(scale_(1.)); ::Slic3r::SVG svg(debug_out_path("fuzzy_traverse_loops_%d_%d_%d_region_%d.svg", perimeter_generator.layer_id, - loop.is_contour ? 0 : 1, loop.depth, i) + is_contour ? 0 : 1, loop_idx, i) .c_str(), bbox); svg.draw_outline(perimeter_generator.slices->surfaces); - svg.draw_outline(loop.polygon, "green"); + svg.draw_outline(polygon, "green"); svg.draw(r.second, "red", 0.5); svg.draw_outline(r.second, "red"); svg.Close(); @@ -348,6 +351,35 @@ void apply_fuzzy_skin(Arachne::ExtrusionLine* extrusion, const PerimeterGenerato } } if (!fuzzified_regions.empty()) { + +#ifdef DEBUG_FUZZY + { + int i = 0; + for (const auto& r : fuzzified_regions) { + BoundingBox bbox = get_extents(perimeter_generator.slices->surfaces); + bbox.offset(scale_(1.)); + ::Slic3r::SVG svg(debug_out_path("fuzzy_traverse_loops_%d_%d_%d_region_%d.svg", perimeter_generator.layer_id, + is_contour ? 0 : 1, extrusion->inset_idx, i) + .c_str(), + bbox); + + // Convert extrusion line to polygon for visualization + Polygon extrusion_polygon; + extrusion_polygon.points.reserve(extrusion->junctions.size()); + for (const auto& junction : extrusion->junctions) { + extrusion_polygon.points.push_back(junction.p); + } + + svg.draw_outline(perimeter_generator.slices->surfaces); + svg.draw_outline(extrusion_polygon, "green"); + svg.draw(r.second, "red", 0.5); + svg.draw_outline(r.second, "red"); + svg.Close(); + i++; + } + } +#endif + // Split the loops into lines with different config, and fuzzy them separately for (const auto& r : fuzzified_regions) { const auto splitted = Algorithm::split_line(*extrusion, r.second, false); @@ -360,6 +392,7 @@ void apply_fuzzy_skin(Arachne::ExtrusionLine* extrusion, const PerimeterGenerato if (std::all_of(splitted.begin(), splitted.end(), [](const Algorithm::SplitLineJunction& j) { return j.clipped; })) { // The entire polygon is fuzzified fuzzy_extrusion_line(extrusion->junctions, slice_z, r.first); + continue; } else { const auto current_ext = extrusion->junctions; std::vector segment; @@ -367,11 +400,20 @@ void apply_fuzzy_skin(Arachne::ExtrusionLine* extrusion, const PerimeterGenerato extrusion->junctions.clear(); const auto fuzzy_current_segment = [&segment, &extrusion, &r, slice_z]() { - extrusion->junctions.push_back(segment.front()); - const auto back = segment.back(); - fuzzy_extrusion_line(segment, slice_z, r.first); + // Orca: non fuzzy points to isolate fuzzy region + const auto front = segment.front(); + const auto back = segment.back(); + + fuzzy_extrusion_line(segment, slice_z, r.first, false); + // Orca: only add non fuzzy point if it's not in the extrusion closing point. + if (extrusion->junctions.front().p != front.p) { + extrusion->junctions.push_back(front); + } extrusion->junctions.insert(extrusion->junctions.end(), segment.begin(), segment.end()); - extrusion->junctions.push_back(back); + // Orca: only add non fuzzy point if it's not in the extrusion closing point. + if (extrusion->junctions.back().p != front.p) { + extrusion->junctions.push_back(back); + } segment.clear(); }; @@ -398,6 +440,12 @@ void apply_fuzzy_skin(Arachne::ExtrusionLine* extrusion, const PerimeterGenerato if (!segment.empty()) { fuzzy_current_segment(); } + + //Orca: ensure the loop is closed after fuzzy + if (extrusion->junctions.front().p != extrusion->junctions.back().p) { + extrusion->junctions.back().p = extrusion->junctions.front().p; + extrusion->junctions.back().w = extrusion->junctions.front().w; + } } } } diff --git a/src/libslic3r/Feature/FuzzySkin/FuzzySkin.hpp b/src/libslic3r/Feature/FuzzySkin/FuzzySkin.hpp index be0b9750c1..e099139c90 100644 --- a/src/libslic3r/Feature/FuzzySkin/FuzzySkin.hpp +++ b/src/libslic3r/Feature/FuzzySkin/FuzzySkin.hpp @@ -9,7 +9,7 @@ namespace Slic3r::Feature::FuzzySkin { void fuzzy_polyline(Points& poly, bool closed, coordf_t slice_z, const FuzzySkinConfig& cfg); -void fuzzy_extrusion_line(Arachne::ExtrusionJunctions& ext_lines, coordf_t slice_z, const FuzzySkinConfig& cfg); +void fuzzy_extrusion_line(Arachne::ExtrusionJunctions& ext_lines, coordf_t slice_z, const FuzzySkinConfig& cfg, bool closed = true); void group_region_by_fuzzify(PerimeterGenerator& g); From a71759700308b803b215c80a959c69fc5606bd20 Mon Sep 17 00:00:00 2001 From: Kiss Lorand <50251547+kisslorand@users.noreply.github.com> Date: Thu, 19 Feb 2026 19:48:42 +0200 Subject: [PATCH 2/2] =?UTF-8?q?Fix=20filament=20override=20changes=20not?= =?UTF-8?q?=20appearing=20in=20Unsaved=20Changes=20and=20showing=20as=20?= =?UTF-8?q?=E2=80=9CUndef=20category=E2=80=9D=20in=20preset=20comparison?= =?UTF-8?q?=20(#12298)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/slic3r/GUI/UnsavedChangesDialog.cpp | 36 +++++++++++++++---------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index 54a0574fbc..c4d160ebc9 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -1661,7 +1661,8 @@ void UnsavedChangesDialog::update_tree(Preset::Type type, PresetCollection* pres //m_tree->model->AddPreset(type, from_u8(presets->get_edited_preset().name), old_pt); // Collect dirty options. - const bool deep_compare = (type == Preset::TYPE_PRINTER || type == Preset::TYPE_SLA_MATERIAL); + const bool deep_compare = (type == Preset::TYPE_PRINTER || + type == Preset::TYPE_FILAMENT || type == Preset::TYPE_SLA_MATERIAL); auto dirty_options = presets->current_dirty_options(deep_compare); // process changes of extruders count @@ -1682,11 +1683,15 @@ void UnsavedChangesDialog::update_tree(Preset::Type type, PresetCollection* pres } for (const std::string& opt_key : dirty_options) { - const Search::Option& option = searcher.get_option(opt_key, type); - if (option.opt_key() != opt_key) { - // When founded option isn't the correct one. - // It can be for dirty_options: "default_print_profile", "printer_model", "printer_settings_id", - // because of they don't exist in searcher + const std::string lookup_key = get_pure_opt_key(opt_key); + Search::Option option = searcher.get_option(lookup_key, type); + if (get_pure_opt_key(option.opt_key()) != lookup_key) + option = searcher.get_option(opt_key, get_full_label(opt_key, new_config), type); + if (get_pure_opt_key(option.opt_key()) != lookup_key) { + // When the found option is not the requested one. + // This can happen for dirty_options such as: + // "default_print_profile", "printer_model", "printer_settings_id", + // because they do not exist in the searcher. continue; } @@ -2190,7 +2195,8 @@ void DiffPresetDialog::update_tree() } // Collect dirty options. - const bool deep_compare = (type == Preset::TYPE_PRINTER || type == Preset::TYPE_SLA_MATERIAL); + const bool deep_compare = (type == Preset::TYPE_PRINTER || + type == Preset::TYPE_FILAMENT || type == Preset::TYPE_SLA_MATERIAL); auto dirty_options = type == Preset::TYPE_PRINTER && left_pt == ptFFF && left_config.opt("extruder_colour")->values.size() < right_congig.opt("extruder_colour")->values.size() ? presets->dirty_options(right_preset, left_preset, deep_compare) : @@ -2229,13 +2235,15 @@ void DiffPresetDialog::update_tree() wxString left_val = get_string_value(opt_key, left_config); wxString right_val = get_string_value(opt_key, right_congig); - Search::Option option = searcher.get_option(opt_key, get_full_label(opt_key, left_config), type); - if (option.opt_key() != opt_key) { - // temporary solution, just for testing - m_tree->Append(opt_key, type, "Undef category", "Undef group", opt_key, left_val, right_val, "undefined"); // ORCA: use low resolution compatible icon - // When founded option isn't the correct one. - // It can be for dirty_options: "default_print_profile", "printer_model", "printer_settings_id", - // because of they don't exist in searcher + const std::string lookup_key = get_pure_opt_key(opt_key); + Search::Option option = searcher.get_option(lookup_key, type); + if (get_pure_opt_key(option.opt_key()) != lookup_key) + option = searcher.get_option(opt_key, get_full_label(opt_key, left_config), type); + if (get_pure_opt_key(option.opt_key()) != lookup_key) { + // When the found option is not the requested one. + // This can happen for dirty_options such as: + // "default_print_profile", "printer_model", "printer_settings_id", + // because they do not exist in the searcher. continue; } m_tree->Append(opt_key, type, option.category_local, option.group_local, option.label_local,