From afbcab395f26c9ed5d569fdff5e767e4f3d7e2f9 Mon Sep 17 00:00:00 2001 From: sentientstardust Date: Thu, 28 May 2026 21:12:29 +0100 Subject: [PATCH] Add 4x resolution option for polygon tracing. Ensure opaque filaments are sorted deeper than translucent ones even if colors are re-ordered --- src/libslic3r/Fill/Fill.cpp | 157 ++----------- src/libslic3r/TextureMapping.cpp | 16 +- src/libslic3r/TextureMapping.hpp | 11 +- src/libslic3r/TextureMappingContoning.cpp | 271 ++++++++++++++++++++-- src/libslic3r/TextureMappingContoning.hpp | 3 + src/slic3r/GUI/Plater.cpp | 51 ++-- 6 files changed, 325 insertions(+), 184 deletions(-) diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index 9f034392983..b1fd47796e5 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -502,7 +502,7 @@ struct TopSurfaceImageRegionPlan { bool contoning_blue_noise_error_diffusion_enabled = false; bool contoning_supersampled_cells_enabled = false; bool contoning_polygonize_color_regions_enabled = false; - bool contoning_polygonize_high_resolution_enabled = TextureMappingZone::DefaultTopSurfaceContoningPolygonizeHighResolutionEnabled; + int contoning_polygonize_resolution = TextureMappingZone::DefaultTopSurfaceContoningPolygonizeResolution; bool contoning_surface_anchored_stacks_enabled = false; int contoning_flat_surface_infill_mode = TextureMappingZone::SlicerDefaultTopSurfaceContoningFlatSurfaceInfillMode; }; @@ -1165,7 +1165,7 @@ struct TopSurfaceImageContoningStackPlanKey { bool supersampled { false }; bool blue_noise { false }; bool polygonize { false }; - bool polygonize_high_resolution { false }; + int polygonize_resolution { 1 }; bool operator<(const TopSurfaceImageContoningStackPlanKey &rhs) const { @@ -1192,7 +1192,7 @@ struct TopSurfaceImageContoningStackPlanKey { supersampled, blue_noise, polygonize, - polygonize_high_resolution) < + polygonize_resolution) < std::tie(rhs.source_layer, rhs.source_layer_id, rhs.target_layer, @@ -1216,7 +1216,7 @@ struct TopSurfaceImageContoningStackPlanKey { rhs.supersampled, rhs.blue_noise, rhs.polygonize, - rhs.polygonize_high_resolution); + rhs.polygonize_resolution); } }; @@ -1343,144 +1343,21 @@ static std::optional> top_surface_image_contoning_stack_rgb return mix_color_solver_components(colors, weights, ColorSolverMixModel::PigmentPainter); } -static std::optional> top_surface_image_contoning_component_rgb( - unsigned int component_id, - const PrintConfig &print_config) -{ - if (component_id == 0 || component_id > print_config.filament_colour.values.size()) - return std::nullopt; - ColorRGB color; - if (!decode_color(print_config.filament_colour.get_at(size_t(component_id - 1)), color)) - return std::nullopt; - return std::array{ color.r(), color.g(), color.b() }; -} - -static void top_surface_image_contoning_sort_stack_for_top_color(std::vector &bottom_to_top, - const std::array &mix_rgb, - const PrintConfig &print_config) -{ - if (bottom_to_top.size() < 2) - return; - const std::array mix_oklab = color_solver_oklab_from_srgb(mix_rgb); - std::stable_sort(bottom_to_top.begin(), bottom_to_top.end(), [&mix_oklab, &print_config](unsigned int lhs, unsigned int rhs) { - const std::optional> lhs_rgb = - top_surface_image_contoning_component_rgb(lhs, print_config); - const std::optional> rhs_rgb = - top_surface_image_contoning_component_rgb(rhs, print_config); - const float lhs_error = lhs_rgb ? - top_surface_image_contoning_oklab_error(color_solver_oklab_from_srgb(*lhs_rgb), mix_oklab) : - std::numeric_limits::max(); - const float rhs_error = rhs_rgb ? - top_surface_image_contoning_oklab_error(color_solver_oklab_from_srgb(*rhs_rgb), mix_oklab) : - std::numeric_limits::max(); - return lhs_error > rhs_error; - }); -} - -static bool top_surface_image_contoning_can_finish_without_repeat(const std::vector &ids, - const std::vector &counts, - unsigned int previous_id, - int remaining) -{ - for (size_t idx = 0; idx < ids.size() && idx < counts.size(); ++idx) { - const int limit = ids[idx] == previous_id ? remaining / 2 : (remaining + 1) / 2; - if (counts[idx] > limit) - return false; - } - return true; -} - -static void top_surface_image_contoning_spread_stack_repeats(std::vector &bottom_to_top) -{ - if (bottom_to_top.size() < 3) - return; - - std::vector ids; - std::vector counts; - std::vector ranks; - for (size_t pos = 0; pos < bottom_to_top.size(); ++pos) { - const unsigned int id = bottom_to_top[pos]; - auto it = std::find(ids.begin(), ids.end(), id); - if (it == ids.end()) { - ids.emplace_back(id); - counts.emplace_back(1); - ranks.emplace_back(int(pos)); - } else { - const size_t idx = size_t(it - ids.begin()); - ++counts[idx]; - ranks[idx] = int(pos); - } - } - if (ids.size() < 2) - return; - - std::vector top_to_bottom; - top_to_bottom.reserve(bottom_to_top.size()); - unsigned int previous_id = 0; - int remaining = int(bottom_to_top.size()); - - auto better_candidate = [&counts, &ranks, &ids](int lhs, int rhs) { - if (rhs < 0) - return true; - if (ranks[size_t(lhs)] != ranks[size_t(rhs)]) - return ranks[size_t(lhs)] > ranks[size_t(rhs)]; - if (counts[size_t(lhs)] != counts[size_t(rhs)]) - return counts[size_t(lhs)] > counts[size_t(rhs)]; - return ids[size_t(lhs)] < ids[size_t(rhs)]; - }; - - auto select_candidate = [&](bool require_feasible, bool allow_repeat) { - int best = -1; - for (size_t idx = 0; idx < ids.size(); ++idx) { - if (counts[idx] <= 0 || (!allow_repeat && ids[idx] == previous_id)) - continue; - --counts[idx]; - const bool feasible = - top_surface_image_contoning_can_finish_without_repeat(ids, counts, ids[idx], remaining - 1); - ++counts[idx]; - if (require_feasible && !feasible) - continue; - if (better_candidate(int(idx), best)) - best = int(idx); - } - return best; - }; - - while (remaining > 0) { - int selected = select_candidate(true, false); - if (selected < 0) - selected = select_candidate(false, false); - if (selected < 0) - selected = select_candidate(false, true); - if (selected < 0) - break; - top_to_bottom.emplace_back(ids[size_t(selected)]); - --counts[size_t(selected)]; - previous_id = ids[size_t(selected)]; - --remaining; - } - - if (top_to_bottom.size() != bottom_to_top.size()) - return; - std::reverse(top_to_bottom.begin(), top_to_bottom.end()); - bottom_to_top = std::move(top_to_bottom); -} - static float top_surface_image_contoning_sample_pitch_mm(const TopSurfaceImageRegionPlan &plan, const BoundingBox &bbox) { float pitch = std::clamp(plan.contoning_external_width_mm, 0.25f, std::max(0.25f, plan.contoning_min_feature_mm * 0.5f)); - const bool high_resolution = - plan.contoning_polygonize_color_regions_enabled && - plan.contoning_polygonize_high_resolution_enabled; - const float min_pitch = high_resolution ? 0.125f : 0.25f; - if (high_resolution) - pitch = std::max(min_pitch, pitch * 0.5f); + const int polygonize_resolution = plan.contoning_polygonize_color_regions_enabled ? + TextureMappingZone::normalize_top_surface_contoning_polygonize_resolution(plan.contoning_polygonize_resolution) : + 1; + const float min_pitch = 0.25f / float(polygonize_resolution); + if (polygonize_resolution > 1) + pitch = std::max(min_pitch, pitch / float(polygonize_resolution)); const double width_mm = unscale(bbox.max.x() - bbox.min.x()); const double height_mm = unscale(bbox.max.y() - bbox.min.y()); - const double max_samples = high_resolution ? 2600000.0 : 650000.0; + const double max_samples = 650000.0 * double(polygonize_resolution) * double(polygonize_resolution); if (width_mm > 0.0 && height_mm > 0.0) { const double estimated = std::ceil(width_mm / double(pitch)) * std::ceil(height_mm / double(pitch)); if (estimated > max_samples) @@ -2103,8 +1980,6 @@ static std::optional top_surface_image_cont top_surface_image_contoning_stack_rgb(stack.bottom_to_top, print_config); if (!stack_rgb) return std::nullopt; - top_surface_image_contoning_sort_stack_for_top_color(stack.bottom_to_top, *stack_rgb, print_config); - top_surface_image_contoning_spread_stack_repeats(stack.bottom_to_top); auto label_it = label_by_stack.find(stack.bottom_to_top); int label = -1; if (label_it == label_by_stack.end()) { @@ -2592,9 +2467,9 @@ static TopSurfaceImageContoningStackPlanKey top_surface_image_contoning_stack_pl key.supersampled = plan.contoning_supersampled_cells_enabled; key.blue_noise = use_blue_noise_error_diffusion; key.polygonize = plan.contoning_polygonize_color_regions_enabled; - key.polygonize_high_resolution = - plan.contoning_polygonize_color_regions_enabled && - plan.contoning_polygonize_high_resolution_enabled; + key.polygonize_resolution = plan.contoning_polygonize_color_regions_enabled ? + TextureMappingZone::normalize_top_surface_contoning_polygonize_resolution(plan.contoning_polygonize_resolution) : + 1; return key; } @@ -3243,8 +3118,8 @@ static std::vector top_surface_image_region_plans( plan.contoning_blue_noise_error_diffusion_enabled = zone->top_surface_contoning_blue_noise_error_diffusion_enabled; plan.contoning_supersampled_cells_enabled = zone->top_surface_contoning_supersampled_cells_enabled; plan.contoning_polygonize_color_regions_enabled = zone->top_surface_contoning_polygonize_color_regions_enabled; - plan.contoning_polygonize_high_resolution_enabled = - zone->top_surface_contoning_polygonize_high_resolution_enabled; + plan.contoning_polygonize_resolution = + TextureMappingZone::normalize_top_surface_contoning_polygonize_resolution(zone->top_surface_contoning_polygonize_resolution); plan.contoning_surface_anchored_stacks_enabled = zone->effective_top_surface_contoning_surface_anchored_stacks_enabled(); const TextureMappingContoningSolver contoning_solver(*zone, print_config, components); diff --git a/src/libslic3r/TextureMapping.cpp b/src/libslic3r/TextureMapping.cpp index 2fcaa5288ae..09cd1cc709f 100644 --- a/src/libslic3r/TextureMapping.cpp +++ b/src/libslic3r/TextureMapping.cpp @@ -1145,7 +1145,8 @@ bool TextureMappingZone::operator==(const TextureMappingZone &rhs) const top_surface_contoning_blue_noise_error_diffusion_enabled == rhs.top_surface_contoning_blue_noise_error_diffusion_enabled && top_surface_contoning_supersampled_cells_enabled == rhs.top_surface_contoning_supersampled_cells_enabled && top_surface_contoning_polygonize_color_regions_enabled == rhs.top_surface_contoning_polygonize_color_regions_enabled && - top_surface_contoning_polygonize_high_resolution_enabled == rhs.top_surface_contoning_polygonize_high_resolution_enabled && + TextureMappingZone::normalize_top_surface_contoning_polygonize_resolution(top_surface_contoning_polygonize_resolution) == + TextureMappingZone::normalize_top_surface_contoning_polygonize_resolution(rhs.top_surface_contoning_polygonize_resolution) && effective_top_surface_contoning_surface_anchored_stacks_enabled() == rhs.effective_top_surface_contoning_surface_anchored_stacks_enabled() && compact_offset_mode == rhs.compact_offset_mode && use_legacy_fixed_color_mode == rhs.use_legacy_fixed_color_mode && @@ -1543,8 +1544,8 @@ std::string TextureMappingManager::serialize_entries() zone.top_surface_contoning_supersampled_cells_enabled; texture["top_surface_contoning_polygonize_color_regions_enabled"] = zone.top_surface_contoning_polygonize_color_regions_enabled; - texture["top_surface_contoning_polygonize_high_resolution_enabled"] = - zone.top_surface_contoning_polygonize_high_resolution_enabled; + texture["top_surface_contoning_polygonize_resolution"] = + TextureMappingZone::normalize_top_surface_contoning_polygonize_resolution(zone.top_surface_contoning_polygonize_resolution); texture["top_surface_contoning_surface_anchored_stacks_enabled"] = zone.effective_top_surface_contoning_surface_anchored_stacks_enabled(); texture["compact_offset_mode"] = zone.compact_offset_mode; @@ -1834,9 +1835,12 @@ void TextureMappingManager::load_entries(const std::string &serialized, zone.top_surface_contoning_polygonize_color_regions_enabled = texture.value("top_surface_contoning_polygonize_color_regions_enabled", TextureMappingZone::DefaultTopSurfaceContoningPolygonizeColorRegionsEnabled); - zone.top_surface_contoning_polygonize_high_resolution_enabled = - texture.value("top_surface_contoning_polygonize_high_resolution_enabled", - TextureMappingZone::DefaultTopSurfaceContoningPolygonizeHighResolutionEnabled); + auto polygonize_resolution_it = texture.find("top_surface_contoning_polygonize_resolution"); + zone.top_surface_contoning_polygonize_resolution = + TextureMappingZone::normalize_top_surface_contoning_polygonize_resolution( + polygonize_resolution_it != texture.end() && polygonize_resolution_it->is_number_integer() ? + polygonize_resolution_it->get() : + TextureMappingZone::DefaultTopSurfaceContoningPolygonizeResolution); zone.top_surface_contoning_surface_anchored_stacks_enabled = texture.value("top_surface_contoning_surface_anchored_stacks_enabled", TextureMappingZone::DefaultTopSurfaceContoningSurfaceAnchoredStacksEnabled); diff --git a/src/libslic3r/TextureMapping.hpp b/src/libslic3r/TextureMapping.hpp index bc047de69d7..17d0fb9cf58 100644 --- a/src/libslic3r/TextureMapping.hpp +++ b/src/libslic3r/TextureMapping.hpp @@ -202,7 +202,7 @@ struct TextureMappingZone static constexpr bool DefaultTopSurfaceContoningBlueNoiseErrorDiffusionEnabled = false; static constexpr bool DefaultTopSurfaceContoningSupersampledCellsEnabled = false; static constexpr bool DefaultTopSurfaceContoningPolygonizeColorRegionsEnabled = false; - static constexpr bool DefaultTopSurfaceContoningPolygonizeHighResolutionEnabled = true; + static constexpr int DefaultTopSurfaceContoningPolygonizeResolution = 2; static constexpr bool DefaultTopSurfaceContoningSurfaceAnchoredStacksEnabled = false; static constexpr bool DefaultCompactOffsetMode = true; static constexpr bool DefaultUseLegacyFixedColorMode = false; @@ -278,6 +278,11 @@ struct TextureMappingZone return ShowExperimentalTopSurfaceContoningOptions && value; } + static constexpr int normalize_top_surface_contoning_polygonize_resolution(int value) + { + return value <= 1 ? 1 : (value >= 4 ? 4 : 2); + } + struct LinearGradientAnchor { bool valid = false; size_t object_id = 0; @@ -355,7 +360,7 @@ struct TextureMappingZone bool top_surface_contoning_blue_noise_error_diffusion_enabled = DefaultTopSurfaceContoningBlueNoiseErrorDiffusionEnabled; bool top_surface_contoning_supersampled_cells_enabled = DefaultTopSurfaceContoningSupersampledCellsEnabled; bool top_surface_contoning_polygonize_color_regions_enabled = DefaultTopSurfaceContoningPolygonizeColorRegionsEnabled; - bool top_surface_contoning_polygonize_high_resolution_enabled = DefaultTopSurfaceContoningPolygonizeHighResolutionEnabled; + int top_surface_contoning_polygonize_resolution = DefaultTopSurfaceContoningPolygonizeResolution; bool top_surface_contoning_surface_anchored_stacks_enabled = DefaultTopSurfaceContoningSurfaceAnchoredStacksEnabled; bool compact_offset_mode = DefaultCompactOffsetMode; bool use_legacy_fixed_color_mode = DefaultUseLegacyFixedColorMode; @@ -546,7 +551,7 @@ struct TextureMappingZone top_surface_contoning_blue_noise_error_diffusion_enabled = DefaultTopSurfaceContoningBlueNoiseErrorDiffusionEnabled; top_surface_contoning_supersampled_cells_enabled = DefaultTopSurfaceContoningSupersampledCellsEnabled; top_surface_contoning_polygonize_color_regions_enabled = DefaultTopSurfaceContoningPolygonizeColorRegionsEnabled; - top_surface_contoning_polygonize_high_resolution_enabled = DefaultTopSurfaceContoningPolygonizeHighResolutionEnabled; + top_surface_contoning_polygonize_resolution = DefaultTopSurfaceContoningPolygonizeResolution; top_surface_contoning_surface_anchored_stacks_enabled = DefaultTopSurfaceContoningSurfaceAnchoredStacksEnabled; compact_offset_mode = DefaultCompactOffsetMode; use_legacy_fixed_color_mode = DefaultUseLegacyFixedColorMode; diff --git a/src/libslic3r/TextureMappingContoning.cpp b/src/libslic3r/TextureMappingContoning.cpp index b79506a15b0..e8e6ed688bd 100644 --- a/src/libslic3r/TextureMappingContoning.cpp +++ b/src/libslic3r/TextureMappingContoning.cpp @@ -15,6 +15,9 @@ namespace Slic3r { namespace { +constexpr float OPAQUE_CONTONING_TD_THRESHOLD_MM = 0.5f; +constexpr float INFERRED_BLACK_TD_MM = 0.1f; + float clamp01(float value) { if (!std::isfinite(value)) @@ -31,6 +34,81 @@ float filament_luminance(const PrintConfig &config, unsigned int component_id) return 0.2126f * color.r() + 0.7152f * color.g() + 0.0722f * color.b(); } +bool explicit_transmission_distance_mm(const TextureMappingZone &zone, unsigned int component_id, float &td_mm) +{ + if (component_id == 0) + return false; + const size_t idx = size_t(component_id - 1); + if (idx >= zone.filament_transmission_distances_mm.size()) + return false; + const float td = zone.filament_transmission_distances_mm[idx]; + if (!std::isfinite(td) || td <= 0.f) + return false; + td_mm = td; + return true; +} + +bool black_role_component(int filament_color_mode, size_t component_idx, size_t component_count) +{ + switch (std::clamp(filament_color_mode, + int(TextureMappingZone::FilamentColorAny), + int(TextureMappingZone::FilamentColorRGBKW))) { + case int(TextureMappingZone::FilamentColorCMYK): + case int(TextureMappingZone::FilamentColorRGBK): + return component_count == 4 && component_idx == 3; + case int(TextureMappingZone::FilamentColorBW): + return component_count == 2 && component_idx == 0; + case int(TextureMappingZone::FilamentColorCMYKW): + case int(TextureMappingZone::FilamentColorRGBKW): + return component_count == 5 && component_idx == 3; + default: + return false; + } +} + +bool is_black_color_filament(const PrintConfig &config, unsigned int component_id) +{ + ColorRGB color; + if (component_id == 0 || component_id > config.filament_colour.values.size() || + !decode_color(config.filament_colour.get_at(size_t(component_id - 1)), color)) + return false; + const float r = clamp01(color.r()); + const float g = clamp01(color.g()); + const float b = clamp01(color.b()); + const float max_channel = std::max({ r, g, b }); + const float min_channel = std::min({ r, g, b }); + const float luminance = 0.2126f * r + 0.7152f * g + 0.0722f * b; + return max_channel <= 0.20f && luminance <= 0.12f && max_channel - min_channel <= 0.08f; +} + +std::vector effective_transmission_distances_mm(const TextureMappingZone &zone, + const PrintConfig &config, + const std::vector &component_ids) +{ + std::vector out(component_ids.size(), 0.f); + for (size_t idx = 0; idx < component_ids.size(); ++idx) { + float td_mm = 0.f; + if (explicit_transmission_distance_mm(zone, component_ids[idx], td_mm)) { + out[idx] = td_mm; + } else if (black_role_component(zone.filament_color_mode, idx, component_ids.size()) || + is_black_color_filament(config, component_ids[idx])) { + out[idx] = INFERRED_BLACK_TD_MM; + } + } + return out; +} + +float effective_transmission_distance_for_component(const std::vector &component_ids, + const std::vector &effective_tds, + unsigned int component_id) +{ + const auto it = std::find(component_ids.begin(), component_ids.end(), component_id); + if (it == component_ids.end()) + return 0.f; + const size_t idx = size_t(it - component_ids.begin()); + return idx < effective_tds.size() ? effective_tds[idx] : 0.f; +} + float perceptual_error(const std::array &lhs, const std::array &rhs) { const float dl = lhs[0] - rhs[0]; @@ -55,6 +133,101 @@ void enumerate_counts(size_t component_idx, } } +bool can_finish_without_repeat(const std::vector &ids, + const std::vector &counts, + unsigned int previous_id, + int remaining) +{ + for (size_t idx = 0; idx < ids.size() && idx < counts.size(); ++idx) { + const int limit = ids[idx] == previous_id ? remaining / 2 : (remaining + 1) / 2; + if (counts[idx] > limit) + return false; + } + return true; +} + +void spread_surface_repeats(std::vector &surface_to_deep) +{ + if (surface_to_deep.size() < 3) + return; + + std::vector ids; + std::vector counts; + std::vector ranks; + ids.reserve(surface_to_deep.size()); + counts.reserve(surface_to_deep.size()); + ranks.reserve(surface_to_deep.size()); + for (size_t pos = 0; pos < surface_to_deep.size(); ++pos) { + const unsigned int id = surface_to_deep[pos]; + auto it = std::find(ids.begin(), ids.end(), id); + if (it == ids.end()) { + ids.emplace_back(id); + counts.emplace_back(1); + ranks.emplace_back(int(pos)); + } else { + ++counts[size_t(it - ids.begin())]; + } + } + if (ids.size() < 2) + return; + + std::vector reordered; + reordered.reserve(surface_to_deep.size()); + unsigned int previous_id = surface_to_deep.front(); + int remaining = int(surface_to_deep.size()); + const auto first_it = std::find(ids.begin(), ids.end(), previous_id); + if (first_it != ids.end()) { + const size_t first_idx = size_t(first_it - ids.begin()); + reordered.emplace_back(previous_id); + --counts[first_idx]; + --remaining; + } + + auto better_candidate = [&counts, &ranks, &ids](int lhs, int rhs) { + if (rhs < 0) + return true; + if (ranks[size_t(lhs)] != ranks[size_t(rhs)]) + return ranks[size_t(lhs)] < ranks[size_t(rhs)]; + if (counts[size_t(lhs)] != counts[size_t(rhs)]) + return counts[size_t(lhs)] > counts[size_t(rhs)]; + return ids[size_t(lhs)] < ids[size_t(rhs)]; + }; + + auto select_candidate = [&](bool require_feasible, bool allow_repeat) { + int best = -1; + for (size_t idx = 0; idx < ids.size(); ++idx) { + if (counts[idx] <= 0 || (!allow_repeat && ids[idx] == previous_id)) + continue; + --counts[idx]; + const bool feasible = + can_finish_without_repeat(ids, counts, ids[idx], remaining - 1); + ++counts[idx]; + if (require_feasible && !feasible) + continue; + if (better_candidate(int(idx), best)) + best = int(idx); + } + return best; + }; + + while (remaining > 0) { + int selected = select_candidate(true, false); + if (selected < 0) + selected = select_candidate(false, false); + if (selected < 0) + selected = select_candidate(false, true); + if (selected < 0) + break; + reordered.emplace_back(ids[size_t(selected)]); + --counts[size_t(selected)]; + previous_id = ids[size_t(selected)]; + --remaining; + } + + if (reordered.size() == surface_to_deep.size()) + surface_to_deep = std::move(reordered); +} + } std::optional> texture_mapping_contoning_component_colors( @@ -89,26 +262,27 @@ std::vector texture_mapping_contoning_components_bottom_to_top( if (component_ids.empty()) return component_ids; - bool has_complete_td = true; - for (unsigned int component_id : component_ids) { - const size_t idx = size_t(component_id - 1); - const float td = idx < zone.filament_transmission_distances_mm.size() ? - zone.filament_transmission_distances_mm[idx] : - 0.f; - if (!std::isfinite(td) || td <= 0.f) { - has_complete_td = false; - break; - } - } + const std::vector td_component_ids = component_ids; + const std::vector effective_tds = effective_transmission_distances_mm(zone, config, td_component_ids); + const bool has_complete_td = + std::all_of(effective_tds.begin(), effective_tds.end(), [](float td) { return std::isfinite(td) && td > 0.f; }); if (has_complete_td) { - std::stable_sort(component_ids.begin(), component_ids.end(), [&zone](unsigned int lhs, unsigned int rhs) { - return zone.filament_transmission_distances_mm[size_t(lhs - 1)] < - zone.filament_transmission_distances_mm[size_t(rhs - 1)]; + std::stable_sort(component_ids.begin(), component_ids.end(), [&effective_tds, &td_component_ids](unsigned int lhs, unsigned int rhs) { + return effective_transmission_distance_for_component(td_component_ids, effective_tds, lhs) < + effective_transmission_distance_for_component(td_component_ids, effective_tds, rhs); }); return component_ids; } - std::stable_sort(component_ids.begin(), component_ids.end(), [&config](unsigned int lhs, unsigned int rhs) { + std::stable_sort(component_ids.begin(), component_ids.end(), [&config, &effective_tds, &td_component_ids](unsigned int lhs, unsigned int rhs) { + const float lhs_td = effective_transmission_distance_for_component(td_component_ids, effective_tds, lhs); + const float rhs_td = effective_transmission_distance_for_component(td_component_ids, effective_tds, rhs); + const bool lhs_opaque = lhs_td > 0.f && lhs_td < OPAQUE_CONTONING_TD_THRESHOLD_MM; + const bool rhs_opaque = rhs_td > 0.f && rhs_td < OPAQUE_CONTONING_TD_THRESHOLD_MM; + if (lhs_opaque != rhs_opaque) + return lhs_opaque; + if (lhs_opaque && std::abs(lhs_td - rhs_td) > 1e-6f) + return lhs_td < rhs_td; return filament_luminance(config, lhs) < filament_luminance(config, rhs); }); return component_ids; @@ -168,6 +342,7 @@ TextureMappingContoningSolver::TextureMappingContoningSolver(const TextureMappin m_component_luminance.reserve(m_component_ids.size()); for (const unsigned int id : m_component_ids) m_component_luminance.emplace_back(filament_luminance(config, id)); + m_effective_transmission_distances_mm = effective_transmission_distances_mm(zone, config, m_component_ids); m_components_bottom_to_top = texture_mapping_contoning_components_bottom_to_top(zone, config, m_component_ids); } @@ -206,6 +381,71 @@ TextureMappingContoningSolver::candidates_for_depth(int stack_layers) const return m_candidates_by_depth.emplace(depth, std::move(candidates)).first->second; } +void TextureMappingContoningSolver::arrange_stack_for_light_path(std::vector &bottom_to_top, + const std::array &target_rgb) const +{ + if (bottom_to_top.size() < 2) + return; + + const std::array target_oklab = color_solver_oklab_from_srgb(target_rgb); + auto component_error = [this, &target_oklab](unsigned int component_id) { + const auto it = std::find(m_component_ids.begin(), m_component_ids.end(), component_id); + if (it == m_component_ids.end()) + return std::numeric_limits::max(); + const size_t idx = size_t(it - m_component_ids.begin()); + if (idx >= m_component_colors.size()) + return std::numeric_limits::max(); + return perceptual_error(color_solver_oklab_from_srgb(m_component_colors[idx]), target_oklab); + }; + + std::stable_sort(bottom_to_top.begin(), bottom_to_top.end(), [&component_error](unsigned int lhs, unsigned int rhs) { + return component_error(lhs) > component_error(rhs); + }); + + std::vector surface_to_deep; + surface_to_deep.reserve(bottom_to_top.size()); + for (auto it = bottom_to_top.rbegin(); it != bottom_to_top.rend(); ++it) + surface_to_deep.emplace_back(*it); + + struct OpaqueItem { + unsigned int component_id = 0; + float td_mm = 0.f; + size_t order = 0; + }; + + std::vector translucent; + std::vector opaque; + translucent.reserve(surface_to_deep.size()); + opaque.reserve(surface_to_deep.size()); + for (size_t idx = 0; idx < surface_to_deep.size(); ++idx) { + const unsigned int component_id = surface_to_deep[idx]; + const float td_mm = + effective_transmission_distance_for_component(m_component_ids, m_effective_transmission_distances_mm, component_id); + if (td_mm > 0.f && td_mm < OPAQUE_CONTONING_TD_THRESHOLD_MM) + opaque.push_back({ component_id, td_mm, idx }); + else + translucent.emplace_back(component_id); + } + + spread_surface_repeats(translucent); + std::stable_sort(opaque.begin(), opaque.end(), [](const OpaqueItem &lhs, const OpaqueItem &rhs) { + if (std::abs(lhs.td_mm - rhs.td_mm) > 1e-6f) + return lhs.td_mm > rhs.td_mm; + return lhs.order < rhs.order; + }); + + surface_to_deep.clear(); + surface_to_deep.reserve(translucent.size() + opaque.size()); + surface_to_deep.insert(surface_to_deep.end(), translucent.begin(), translucent.end()); + for (const OpaqueItem &item : opaque) + surface_to_deep.emplace_back(item.component_id); + + bottom_to_top.clear(); + bottom_to_top.reserve(surface_to_deep.size()); + for (auto it = surface_to_deep.rbegin(); it != surface_to_deep.rend(); ++it) + bottom_to_top.emplace_back(*it); +} + TextureMappingContoningStack TextureMappingContoningSolver::solve(const std::array &target_rgb, int stack_layers) const { TextureMappingContoningStack out; @@ -251,6 +491,7 @@ TextureMappingContoningStack TextureMappingContoningSolver::solve(const std::arr out.bottom_to_top.emplace_back(m_components_bottom_to_top.empty() ? m_component_ids.front() : m_components_bottom_to_top.back()); if (int(out.bottom_to_top.size()) > depth) out.bottom_to_top.resize(size_t(depth)); + arrange_stack_for_light_path(out.bottom_to_top, target_rgb); return out; } diff --git a/src/libslic3r/TextureMappingContoning.hpp b/src/libslic3r/TextureMappingContoning.hpp index 6d8307932fd..f54f307f7c7 100644 --- a/src/libslic3r/TextureMappingContoning.hpp +++ b/src/libslic3r/TextureMappingContoning.hpp @@ -42,11 +42,14 @@ private: }; const std::vector& candidates_for_depth(int stack_layers) const; + void arrange_stack_for_light_path(std::vector &bottom_to_top, + const std::array &target_rgb) const; std::vector m_component_ids; std::vector m_components_bottom_to_top; std::vector> m_component_colors; std::vector m_component_luminance; + std::vector m_effective_transmission_distances_mm; mutable std::map> m_candidates_by_depth; }; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index af596118fd5..b73380bf426 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1982,7 +1982,7 @@ public: bool top_surface_contoning_blue_noise_error_diffusion_enabled, bool top_surface_contoning_supersampled_cells_enabled, bool top_surface_contoning_polygonize_color_regions_enabled, - bool top_surface_contoning_polygonize_high_resolution_enabled, + int top_surface_contoning_polygonize_resolution, bool top_surface_contoning_surface_anchored_stacks_enabled, const TextureMappingManager &texture_mapping_zones, const TextureMappingGlobalSettings &global_settings, @@ -2769,12 +2769,23 @@ public: 0, wxEXPAND | wxTOP | wxBOTTOM, gap / 2); - m_top_surface_contoning_polygonize_high_resolution_checkbox = - new wxCheckBox(m_top_surface_contoning_checkboxes_panel, wxID_ANY, _L("High-resolution polygon tracing")); - m_top_surface_contoning_polygonize_high_resolution_checkbox->SetValue(top_surface_contoning_polygonize_high_resolution_enabled); - m_top_surface_contoning_polygonize_high_resolution_checkbox->SetMinSize( - wxSize(-1, std::max(m_top_surface_contoning_polygonize_high_resolution_checkbox->GetBestSize().GetHeight(), FromDIP(24)))); - contoning_checkboxes_root->Add(m_top_surface_contoning_polygonize_high_resolution_checkbox, + m_top_surface_contoning_polygonize_resolution_panel = new wxPanel(m_top_surface_contoning_checkboxes_panel, wxID_ANY); + auto *contoning_polygonize_resolution_row = new wxBoxSizer(wxHORIZONTAL); + m_top_surface_contoning_polygonize_resolution_panel->SetSizer(contoning_polygonize_resolution_row); + contoning_polygonize_resolution_row->Add(new wxStaticText(m_top_surface_contoning_polygonize_resolution_panel, wxID_ANY, _L("Polygon tracing resolution")), + 0, + wxALIGN_CENTER_VERTICAL | wxRIGHT, + gap); + wxArrayString contoning_polygonize_resolution_choices; + contoning_polygonize_resolution_choices.Add(_L("1x")); + contoning_polygonize_resolution_choices.Add(_L("2x")); + contoning_polygonize_resolution_choices.Add(_L("4x (slow)")); + m_top_surface_contoning_polygonize_resolution_choice = + new wxChoice(m_top_surface_contoning_polygonize_resolution_panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, contoning_polygonize_resolution_choices); + const int polygonize_resolution = TextureMappingZone::normalize_top_surface_contoning_polygonize_resolution(top_surface_contoning_polygonize_resolution); + m_top_surface_contoning_polygonize_resolution_choice->SetSelection(polygonize_resolution == 1 ? 0 : (polygonize_resolution == 4 ? 2 : 1)); + contoning_polygonize_resolution_row->Add(m_top_surface_contoning_polygonize_resolution_choice, 1, wxEXPAND); + contoning_checkboxes_root->Add(m_top_surface_contoning_polygonize_resolution_panel, 0, wxEXPAND | wxTOP | wxBOTTOM, gap / 2); @@ -3239,11 +3250,12 @@ public: return m_top_surface_contoning_polygonize_color_regions_checkbox != nullptr && m_top_surface_contoning_polygonize_color_regions_checkbox->GetValue(); } - bool top_surface_contoning_polygonize_high_resolution_enabled() const + int top_surface_contoning_polygonize_resolution() const { - return m_top_surface_contoning_polygonize_high_resolution_checkbox == nullptr ? - TextureMappingZone::DefaultTopSurfaceContoningPolygonizeHighResolutionEnabled : - m_top_surface_contoning_polygonize_high_resolution_checkbox->GetValue(); + if (m_top_surface_contoning_polygonize_resolution_choice == nullptr) + return TextureMappingZone::DefaultTopSurfaceContoningPolygonizeResolution; + const int selection = m_top_surface_contoning_polygonize_resolution_choice->GetSelection(); + return selection == 0 ? 1 : (selection == 2 ? 4 : 2); } bool top_surface_contoning_surface_anchored_stacks_enabled() const { @@ -3757,10 +3769,10 @@ private: contoning && m_top_surface_contoning_polygonize_color_regions_checkbox != nullptr && m_top_surface_contoning_polygonize_color_regions_checkbox->GetValue(); - if (m_top_surface_contoning_polygonize_high_resolution_checkbox != nullptr) { - m_top_surface_contoning_polygonize_high_resolution_checkbox->Show(polygonize_color_regions); - m_top_surface_contoning_polygonize_high_resolution_checkbox->Enable(polygonize_color_regions); - } + if (m_top_surface_contoning_polygonize_resolution_panel != nullptr) + m_top_surface_contoning_polygonize_resolution_panel->Show(polygonize_color_regions); + if (m_top_surface_contoning_polygonize_resolution_choice != nullptr) + m_top_surface_contoning_polygonize_resolution_choice->Enable(polygonize_color_regions); if (m_top_surface_contoning_surface_anchored_stacks_checkbox != nullptr) { m_top_surface_contoning_surface_anchored_stacks_checkbox->Show(contoning); m_top_surface_contoning_surface_anchored_stacks_checkbox->Enable(contoning); @@ -3839,7 +3851,8 @@ private: wxCheckBox *m_top_surface_contoning_blue_noise_error_diffusion_checkbox {nullptr}; wxCheckBox *m_top_surface_contoning_supersampled_cells_checkbox {nullptr}; wxCheckBox *m_top_surface_contoning_polygonize_color_regions_checkbox {nullptr}; - wxCheckBox *m_top_surface_contoning_polygonize_high_resolution_checkbox {nullptr}; + wxPanel *m_top_surface_contoning_polygonize_resolution_panel {nullptr}; + wxChoice *m_top_surface_contoning_polygonize_resolution_choice {nullptr}; wxCheckBox *m_top_surface_contoning_surface_anchored_stacks_checkbox {nullptr}; wxCheckBox *m_use_legacy_fixed_color_mode_checkbox {nullptr}; wxCheckBox *m_minimum_visibility_offset_checkbox {nullptr}; @@ -8863,7 +8876,7 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager) updated.top_surface_contoning_blue_noise_error_diffusion_enabled, updated.top_surface_contoning_supersampled_cells_enabled, updated.top_surface_contoning_polygonize_color_regions_enabled, - updated.top_surface_contoning_polygonize_high_resolution_enabled, + updated.top_surface_contoning_polygonize_resolution, updated.top_surface_contoning_surface_anchored_stacks_enabled, bundle->texture_mapping_zones, bundle->texture_mapping_global_settings, @@ -8936,8 +8949,8 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager) dlg.top_surface_contoning_supersampled_cells_enabled(); updated.top_surface_contoning_polygonize_color_regions_enabled = dlg.top_surface_contoning_polygonize_color_regions_enabled(); - updated.top_surface_contoning_polygonize_high_resolution_enabled = - dlg.top_surface_contoning_polygonize_high_resolution_enabled(); + updated.top_surface_contoning_polygonize_resolution = + dlg.top_surface_contoning_polygonize_resolution(); updated.top_surface_contoning_surface_anchored_stacks_enabled = dlg.top_surface_contoning_surface_anchored_stacks_enabled(); if (updated.top_surface_image_printing_enabled &&