From c519c107e7fe673bca8229ff535597f669e1fd85 Mon Sep 17 00:00:00 2001 From: sentientstardust Date: Tue, 26 May 2026 11:14:25 +0100 Subject: [PATCH] Add blue noise, phase, angle and supersampling options for top-surface coloring --- src/libslic3r/Fill/Fill.cpp | 439 +++++++++++++++++++++++++------ src/libslic3r/TextureMapping.cpp | 23 ++ src/libslic3r/TextureMapping.hpp | 12 + src/slic3r/GUI/Plater.cpp | 91 +++++++ 4 files changed, 487 insertions(+), 78 deletions(-) diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index a13807261df..754b066852b 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -433,6 +433,10 @@ struct TopSurfaceImageRegionPlan { bool contoning_replace_top_perimeters_with_infill = false; bool contoning_recolor_surrounding_perimeters = false; int contoning_perimeter_mode = TextureMappingZone::DefaultTopSurfaceContoningPerimeterMode; + bool contoning_layer_phase_enabled = false; + bool contoning_varied_infill_angles_enabled = false; + bool contoning_blue_noise_error_diffusion_enabled = false; + bool contoning_supersampled_cells_enabled = false; }; enum class TopSurfaceImageSourceSurface { @@ -670,6 +674,16 @@ struct TopSurfaceImageContoningVectorRegion { int cell_count { 0 }; }; +struct TopSurfaceImageContoningCellSample { + std::array rgb { { 0.f, 0.f, 0.f } }; + int solve_layers { 0 }; +}; + +struct TopSurfaceImageContoningSolvedLabel { + int label { -1 }; + std::array rgb { { 0.f, 0.f, 0.f } }; +}; + static float top_surface_image_contoning_oklab_error(const std::array &lhs, const std::array &rhs) { @@ -842,6 +856,68 @@ static float top_surface_image_contoning_sample_pitch_mm(const TopSurfaceImageRe return std::clamp(pitch, 0.25f, std::max(0.25f, plan.contoning_min_feature_mm)); } +static float top_surface_image_contoning_angle_rad(int depth, bool varied_angles) +{ + if (!varied_angles) + return (depth & 1) ? float(-PI / 4.0) : float(PI / 4.0); + switch ((depth % 4 + 4) % 4) { + case 0: return float(PI / 4.0); + case 1: return float(-PI / 4.0); + case 2: return 0.f; + default: return float(PI / 2.0); + } +} + +static coord_t top_surface_image_contoning_grid_min(coord_t value, coord_t step, coord_t phase) +{ + if (step <= 0) + return value; + const coord_t shifted = value - phase; + coord_t quotient = shifted / step; + if (shifted < 0 && shifted % step != 0) + --quotient; + return quotient * step + phase; +} + +static std::pair top_surface_image_contoning_grid_phase(coord_t step, int depth) +{ + if (step <= 0) + return { 0, 0 }; + static constexpr int phase_x[8] = { 0, 4, 2, 6, 1, 5, 3, 7 }; + static constexpr int phase_y[8] = { 0, 4, 6, 2, 5, 1, 7, 3 }; + const int idx = (depth % 8 + 8) % 8; + return { + coord_t((static_cast(step) * static_cast(phase_x[idx])) / 8), + coord_t((static_cast(step) * static_cast(phase_y[idx])) / 8) + }; +} + +static uint32_t top_surface_image_contoning_hash_u32(int col, int row, int depth, int channel) +{ + uint32_t h = 2166136261u; + auto mix = [&h](uint32_t value) { + h ^= value; + h *= 16777619u; + }; + mix(uint32_t(col) * 0x9e3779b9u); + mix(uint32_t(row) * 0x85ebca6bu); + mix(uint32_t(depth) * 0xc2b2ae35u); + mix(uint32_t(channel) * 0x27d4eb2fu); + h ^= h >> 16; + h *= 0x7feb352du; + h ^= h >> 15; + h *= 0x846ca68bu; + h ^= h >> 16; + return h; +} + +static float top_surface_image_contoning_jitter(int col, int row, int depth, int channel) +{ + const uint32_t h = top_surface_image_contoning_hash_u32(col, row, depth, channel); + const float unit = float(h & 0x00FFFFFFu) / float(0x00FFFFFFu); + return (unit - 0.5f) * 0.006f; +} + static bool top_surface_image_contoning_grid_component_printable(int cell_count, int min_col, int max_col, @@ -1028,6 +1104,114 @@ static int top_surface_image_contoning_pattern_filaments(int stack_layers, int c return std::max(1, std::min(clamped_stack_layers, clamped_pattern_filaments)); } +static std::optional top_surface_image_contoning_sample_cell( + const TextureMappingOffsetContext &context, + const ExPolygons &area, + const std::vector &source_stack_areas, + int stack_layers, + int pattern_filaments, + int depth, + coord_t x0, + coord_t y0, + coord_t x1, + coord_t y1, + float threshold_deg, + TopSurfaceImageSourceSurface source_surface, + bool supersampled) +{ + TopSurfaceImageContoningCellSample out; + int sample_count = 0; + auto add_sample = [&](const Point &sample_point) { + if (!top_surface_image_expolygons_contain_point(area, sample_point)) + return; + const int local_stack_layers = + top_surface_image_contoning_local_stack_layers_at_point(sample_point, source_stack_areas, stack_layers); + if (depth >= local_stack_layers) + return; + const int solve_layers = std::min({ local_stack_layers, stack_layers, pattern_filaments }); + if (solve_layers <= 0) + return; + const float sample_x_mm = unscale(sample_point.x()); + const float sample_y_mm = unscale(sample_point.y()); + if (!top_surface_image_contoning_sample_eligible(context, sample_x_mm, sample_y_mm, threshold_deg, source_surface)) + return; + const std::optional> rgb = + sample_weight_field_rgb(context.weight_field, + sample_x_mm, + sample_y_mm, + context.high_resolution_texture_sampling); + if (!rgb) + return; + out.rgb[0] += (*rgb)[0]; + out.rgb[1] += (*rgb)[1]; + out.rgb[2] += (*rgb)[2]; + out.solve_layers = sample_count == 0 ? solve_layers : std::min(out.solve_layers, solve_layers); + ++sample_count; + }; + + if (supersampled) { + static constexpr std::array, 5> offsets { + std::pair{ 0.5, 0.5 }, + std::pair{ 0.25, 0.25 }, + std::pair{ 0.75, 0.25 }, + std::pair{ 0.25, 0.75 }, + std::pair{ 0.75, 0.75 } + }; + for (const std::pair &offset : offsets) { + const coord_t x = coord_t(std::llround(double(x0) + double(x1 - x0) * offset.first)); + const coord_t y = coord_t(std::llround(double(y0) + double(y1 - y0) * offset.second)); + add_sample(Point(x, y)); + } + } else { + add_sample(Point((x0 + x1) / 2, (y0 + y1) / 2)); + } + + if (sample_count <= 0) + return std::nullopt; + out.rgb[0] = std::clamp(out.rgb[0] / float(sample_count), 0.f, 1.f); + out.rgb[1] = std::clamp(out.rgb[1] / float(sample_count), 0.f, 1.f); + out.rgb[2] = std::clamp(out.rgb[2] / float(sample_count), 0.f, 1.f); + if (out.solve_layers <= 0) + return std::nullopt; + return out; +} + +static std::optional top_surface_image_contoning_solve_label( + const std::array &rgb, + int solve_layers, + const TextureMappingContoningSolver &solver, + const PrintConfig &print_config, + std::vector &labels, + std::map, int> &label_by_stack) +{ + TextureMappingContoningStack stack = solver.solve(rgb, solve_layers); + if (stack.bottom_to_top.empty()) + return std::nullopt; + std::optional> stack_rgb = + 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()) { + TopSurfaceImageContoningVectorLabel label_data; + label_data.bottom_to_top = stack.bottom_to_top; + label_data.rgb = *stack_rgb; + label_data.oklab = color_solver_oklab_from_srgb(*stack_rgb); + label = int(labels.size()); + labels.emplace_back(std::move(label_data)); + label_by_stack.emplace(labels.back().bottom_to_top, label); + } else { + label = label_it->second; + } + TopSurfaceImageContoningSolvedLabel out; + out.label = label; + out.rgb = *stack_rgb; + return out; +} + static std::vector top_surface_image_contoning_vector_regions( const TopSurfaceImageRegionPlan &plan, const Layer &source_layer, @@ -1038,6 +1222,7 @@ static std::vector top_surface_image_conto int depth, const TextureMappingContoningSolver &solver, TopSurfaceImageSourceSurface source_surface, + bool use_blue_noise_error_diffusion, const ThrowIfCanceled *throw_if_canceled) { std::vector regions; @@ -1069,8 +1254,14 @@ static std::vector top_surface_image_conto const float pitch_mm = top_surface_image_contoning_sample_pitch_mm(plan, bbox); const coord_t step = std::max(1, scale_(double(pitch_mm))); - const coord_t min_x = (bbox.min.x() / step) * step; - const coord_t min_y = (bbox.min.y() / step) * step; + const std::pair grid_phase = + plan.contoning_layer_phase_enabled ? top_surface_image_contoning_grid_phase(step, depth) : std::pair{ 0, 0 }; + const coord_t min_x = plan.contoning_layer_phase_enabled ? + top_surface_image_contoning_grid_min(bbox.min.x(), step, grid_phase.first) : + (bbox.min.x() / step) * step; + const coord_t min_y = plan.contoning_layer_phase_enabled ? + top_surface_image_contoning_grid_min(bbox.min.y(), step, grid_phase.second) : + (bbox.min.y() / step) * step; const int cols = std::max(0, int(std::ceil(double(bbox.max.x() - min_x) / double(step)))); const int rows = std::max(0, int(std::ceil(double(bbox.max.y() - min_y) / double(step)))); if (cols <= 0 || rows <= 0) @@ -1091,60 +1282,102 @@ static std::vector top_surface_image_conto const std::vector source_stack_areas = top_surface_image_contoning_stack_areas(source_layer, plan.zone_id, stack_layers, source_surface, throw_if_canceled); + auto solve_cell = [&](int row, int col, const std::array &target_rgb, int solve_layers) { + std::optional solved = + top_surface_image_contoning_solve_label(target_rgb, solve_layers, solver, print_config, labels, label_by_stack); + if (!solved) + return std::optional(); + grid[size_t(row * cols + col)] = solved->label; + return solved; + }; + + auto sample_cell = [&](int row, int col) { + const coord_t x0 = min_x + coord_t(col) * step; + const coord_t y0 = min_y + coord_t(row) * step; + const coord_t x1 = std::min(x0 + step, bbox.max.x()); + const coord_t y1 = std::min(y0 + step, bbox.max.y()); + if (x1 <= x0 || y1 <= y0) + return std::optional(); + return top_surface_image_contoning_sample_cell(*context, + area, + source_stack_areas, + stack_layers, + pattern_filaments, + depth, + x0, + y0, + x1, + y1, + threshold_deg, + source_surface, + plan.contoning_supersampled_cells_enabled); + }; + + std::vector> cell_samples(grid.size()); for (int row = 0; row < rows; ++row) { if ((row & 15) == 0) check_canceled(throw_if_canceled); - for (int col = 0; col < cols; ++col) { - const coord_t x0 = min_x + coord_t(col) * step; - const coord_t y0 = min_y + coord_t(row) * step; - const coord_t x1 = std::min(x0 + step, bbox.max.x()); - const coord_t y1 = std::min(y0 + step, bbox.max.y()); - if (x1 <= x0 || y1 <= y0) - continue; - const Point sample_point((x0 + x1) / 2, (y0 + y1) / 2); - if (!top_surface_image_expolygons_contain_point(area, sample_point)) - continue; - const int local_stack_layers = - top_surface_image_contoning_local_stack_layers_at_point(sample_point, source_stack_areas, stack_layers); - if (depth >= local_stack_layers) - continue; - const int solve_layers = std::min({ local_stack_layers, stack_layers, pattern_filaments }); - if (solve_layers <= 0) - continue; - const float sample_x_mm = unscale(sample_point.x()); - const float sample_y_mm = unscale(sample_point.y()); - if (!top_surface_image_contoning_sample_eligible(*context, sample_x_mm, sample_y_mm, threshold_deg, source_surface)) - continue; - const std::optional> rgb = - sample_weight_field_rgb(context->weight_field, - sample_x_mm, - sample_y_mm, - context->high_resolution_texture_sampling); - if (!rgb) - continue; - TextureMappingContoningStack stack = solver.solve(*rgb, solve_layers); - if (stack.bottom_to_top.empty()) - continue; - std::optional> stack_rgb = - top_surface_image_contoning_stack_rgb(stack.bottom_to_top, print_config); - if (!stack_rgb) - continue; - 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()) { - TopSurfaceImageContoningVectorLabel label_data; - label_data.bottom_to_top = stack.bottom_to_top; - label_data.rgb = *stack_rgb; - label_data.oklab = color_solver_oklab_from_srgb(*stack_rgb); - label = int(labels.size()); - labels.emplace_back(std::move(label_data)); - label_by_stack.emplace(labels.back().bottom_to_top, label); - } else { - label = label_it->second; + for (int col = 0; col < cols; ++col) + cell_samples[size_t(row * cols + col)] = sample_cell(row, col); + } + + if (use_blue_noise_error_diffusion) { + std::vector> errors(grid.size(), std::array{ { 0.f, 0.f, 0.f } }); + auto add_error = [&](int row, int col, const std::array &error, float factor) { + if (row < 0 || row >= rows || col < 0 || col >= cols) + return; + std::array &dst = errors[size_t(row * cols + col)]; + dst[0] += error[0] * factor; + dst[1] += error[1] * factor; + dst[2] += error[2] * factor; + }; + for (int row = 0; row < rows; ++row) { + if ((row & 15) == 0) + check_canceled(throw_if_canceled); + const bool left_to_right = (row & 1) == 0; + for (int step_col = 0; step_col < cols; ++step_col) { + const int col = left_to_right ? step_col : cols - 1 - step_col; + const std::optional &sample = cell_samples[size_t(row * cols + col)]; + if (!sample) + continue; + const size_t grid_idx = size_t(row * cols + col); + std::array target_rgb { + std::clamp(sample->rgb[0] + errors[grid_idx][0] + top_surface_image_contoning_jitter(col, row, depth, 0), 0.f, 1.f), + std::clamp(sample->rgb[1] + errors[grid_idx][1] + top_surface_image_contoning_jitter(col, row, depth, 1), 0.f, 1.f), + std::clamp(sample->rgb[2] + errors[grid_idx][2] + top_surface_image_contoning_jitter(col, row, depth, 2), 0.f, 1.f) + }; + const std::optional solved = + solve_cell(row, col, target_rgb, sample->solve_layers); + if (!solved) + continue; + const std::array error { + target_rgb[0] - solved->rgb[0], + target_rgb[1] - solved->rgb[1], + target_rgb[2] - solved->rgb[2] + }; + if (left_to_right) { + add_error(row, col + 1, error, 7.f / 16.f); + add_error(row + 1, col - 1, error, 3.f / 16.f); + add_error(row + 1, col, error, 5.f / 16.f); + add_error(row + 1, col + 1, error, 1.f / 16.f); + } else { + add_error(row, col - 1, error, 7.f / 16.f); + add_error(row + 1, col + 1, error, 3.f / 16.f); + add_error(row + 1, col, error, 5.f / 16.f); + add_error(row + 1, col - 1, error, 1.f / 16.f); + } + } + } + } else { + for (int row = 0; row < rows; ++row) { + if ((row & 15) == 0) + check_canceled(throw_if_canceled); + for (int col = 0; col < cols; ++col) { + const std::optional &sample = cell_samples[size_t(row * cols + col)]; + if (!sample) + continue; + solve_cell(row, col, sample->rgb, sample->solve_layers); } - grid[size_t(row * cols + col)] = label; } } @@ -1233,32 +1466,71 @@ static void top_surface_image_append_contoning_slices(TopSurfaceImageRegionPlan std::vector by_component(print_config.filament_colour.values.size() + 1); std::vector perimeter_by_component(print_config.filament_colour.values.size() + 1); - const std::vector stack_regions = - top_surface_image_contoning_vector_regions(plan, - source_layer, - perimeter_area, - object, - zone, - print_config, - depth, - solver, - source_surface, - throw_if_canceled); - for (const TopSurfaceImageContoningVectorRegion ®ion : stack_regions) { - check_canceled(throw_if_canceled); - if (depth < 0 || region.bottom_to_top.empty() || region.area.empty()) - continue; - const int pattern_depth = depth % int(region.bottom_to_top.size()); - const unsigned int component_id = - region.bottom_to_top[size_t(int(region.bottom_to_top.size()) - 1 - pattern_depth)]; - if (component_id == 0 || component_id >= by_component.size()) - continue; - append(perimeter_by_component[component_id], region.area); - if (!area.empty()) { - ExPolygons component_area = intersection_ex(region.area, area, ApplySafetyOffset::Yes); - if (!component_area.empty()) - append(by_component[component_id], std::move(component_area)); + auto append_regions = [&](const std::vector ®ions, + bool for_fill, + bool for_perimeter) { + for (const TopSurfaceImageContoningVectorRegion ®ion : regions) { + check_canceled(throw_if_canceled); + if (depth < 0 || region.bottom_to_top.empty() || region.area.empty()) + continue; + const int pattern_depth = depth % int(region.bottom_to_top.size()); + const unsigned int component_id = + region.bottom_to_top[size_t(int(region.bottom_to_top.size()) - 1 - pattern_depth)]; + if (component_id == 0 || component_id >= by_component.size()) + continue; + if (for_perimeter) + append(perimeter_by_component[component_id], region.area); + if (for_fill && !area.empty()) { + ExPolygons component_area = intersection_ex(region.area, area, ApplySafetyOffset::Yes); + if (!component_area.empty()) + append(by_component[component_id], std::move(component_area)); + } } + }; + + if (plan.contoning_blue_noise_error_diffusion_enabled) { + if (!area.empty()) { + const std::vector fill_regions = + top_surface_image_contoning_vector_regions(plan, + source_layer, + area, + object, + zone, + print_config, + depth, + solver, + source_surface, + true, + throw_if_canceled); + append_regions(fill_regions, true, false); + } + const std::vector perimeter_regions = + top_surface_image_contoning_vector_regions(plan, + source_layer, + perimeter_area, + object, + zone, + print_config, + depth, + solver, + source_surface, + false, + throw_if_canceled); + append_regions(perimeter_regions, false, true); + } else { + const std::vector stack_regions = + top_surface_image_contoning_vector_regions(plan, + source_layer, + perimeter_area, + object, + zone, + print_config, + depth, + solver, + source_surface, + false, + throw_if_canceled); + append_regions(stack_regions, true, true); } ExPolygons depth_taken; @@ -1287,7 +1559,7 @@ static void top_surface_image_append_contoning_slices(TopSurfaceImageRegionPlan slice.component_count = size_t(pattern_filaments); slice.contoning = true; slice.lower_surface = source_surface == TopSurfaceImageSourceSurface::Bottom; - slice.angle_rad = (depth & 1) ? float(-PI / 4.0) : float(PI / 4.0); + slice.angle_rad = top_surface_image_contoning_angle_rad(depth, plan.contoning_varied_infill_angles_enabled); slice.area = std::move(component_area); slice.perimeter_area = std::move(component_perimeter_area); plan.slices.emplace_back(std::move(slice)); @@ -1382,6 +1654,10 @@ static std::vector top_surface_image_region_plans(con std::clamp(zone->top_surface_contoning_perimeter_mode, int(TextureMappingZone::ContoningPerimeterSegmentBlocks), int(TextureMappingZone::ContoningPerimeterSegmentInfill)); + plan.contoning_layer_phase_enabled = zone->top_surface_contoning_layer_phase_enabled; + plan.contoning_varied_infill_angles_enabled = zone->top_surface_contoning_varied_infill_angles_enabled; + 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; const TextureMappingContoningSolver contoning_solver(*zone, print_config, components); const int stack_depth = plan.contoning ? @@ -3111,8 +3387,11 @@ std::vector group_fills(const Layer &layer, params.flow = layerm.flow(frSolidInfill); params.spacing = params.flow.spacing(); surface_fills.emplace_back(params); + surface_fills.back().region_id = region_id; surface_fills.back().surface.surface_type = stInternalSolid; surface_fills.back().surface.thickness = layer.height; + surface_fills.back().region_id_group.push_back(region_id); + surface_fills.back().no_overlap_expolygons = layerm.fill_no_overlap_expolygons; surface_fills.back().expolygons = std::move(extensions); } else { append(extensions, std::move(internal_solid_fill->expolygons)); @@ -3216,6 +3495,10 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, for (SurfaceFill &surface_fill : surface_fills) { check_canceled(throw_if_canceled_ptr); + if (surface_fill.expolygons.empty() || + surface_fill.region_id >= this->m_regions.size() || + this->m_regions[surface_fill.region_id] == nullptr) + continue; // Create the filler object. std::unique_ptr f = std::unique_ptr(Fill::new_from_type(surface_fill.params.pattern)); f->set_bounding_box(bbox); diff --git a/src/libslic3r/TextureMapping.cpp b/src/libslic3r/TextureMapping.cpp index 87f8d27c2ee..2ed9d391bbf 100644 --- a/src/libslic3r/TextureMapping.cpp +++ b/src/libslic3r/TextureMapping.cpp @@ -1057,6 +1057,10 @@ bool TextureMappingZone::operator==(const TextureMappingZone &rhs) const top_surface_contoning_replace_top_perimeters_with_infill == rhs.top_surface_contoning_replace_top_perimeters_with_infill && top_surface_contoning_recolor_surrounding_perimeters == rhs.top_surface_contoning_recolor_surrounding_perimeters && top_surface_contoning_perimeter_mode == rhs.top_surface_contoning_perimeter_mode && + top_surface_contoning_layer_phase_enabled == rhs.top_surface_contoning_layer_phase_enabled && + top_surface_contoning_varied_infill_angles_enabled == rhs.top_surface_contoning_varied_infill_angles_enabled && + 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 && compact_offset_mode == rhs.compact_offset_mode && use_legacy_fixed_color_mode == rhs.use_legacy_fixed_color_mode && high_speed_image_texture_sampling == rhs.high_speed_image_texture_sampling && @@ -1443,6 +1447,13 @@ std::string TextureMappingManager::serialize_entries() clamp_int(zone.top_surface_contoning_perimeter_mode, int(TextureMappingZone::ContoningPerimeterSegmentBlocks), int(TextureMappingZone::ContoningPerimeterSegmentInfill)); + texture["top_surface_contoning_layer_phase_enabled"] = zone.top_surface_contoning_layer_phase_enabled; + texture["top_surface_contoning_varied_infill_angles_enabled"] = + zone.top_surface_contoning_varied_infill_angles_enabled; + texture["top_surface_contoning_blue_noise_error_diffusion_enabled"] = + zone.top_surface_contoning_blue_noise_error_diffusion_enabled; + texture["top_surface_contoning_supersampled_cells_enabled"] = + zone.top_surface_contoning_supersampled_cells_enabled; texture["compact_offset_mode"] = zone.compact_offset_mode; texture["use_legacy_fixed_color_mode"] = zone.use_legacy_fixed_color_mode; texture["high_speed_image_texture_sampling"] = true; @@ -1709,6 +1720,18 @@ void TextureMappingManager::load_entries(const std::string &serialized, int(TextureMappingZone::ContoningPerimeterSegmentInfill)); if (zone.top_surface_contoning_replace_top_perimeters_with_infill) zone.top_surface_contoning_recolor_surrounding_perimeters = false; + zone.top_surface_contoning_layer_phase_enabled = + texture.value("top_surface_contoning_layer_phase_enabled", + TextureMappingZone::DefaultTopSurfaceContoningLayerPhaseEnabled); + zone.top_surface_contoning_varied_infill_angles_enabled = + texture.value("top_surface_contoning_varied_infill_angles_enabled", + TextureMappingZone::DefaultTopSurfaceContoningVariedInfillAnglesEnabled); + zone.top_surface_contoning_blue_noise_error_diffusion_enabled = + texture.value("top_surface_contoning_blue_noise_error_diffusion_enabled", + TextureMappingZone::DefaultTopSurfaceContoningBlueNoiseErrorDiffusionEnabled); + zone.top_surface_contoning_supersampled_cells_enabled = + texture.value("top_surface_contoning_supersampled_cells_enabled", + TextureMappingZone::DefaultTopSurfaceContoningSupersampledCellsEnabled); zone.compact_offset_mode = texture.value("compact_offset_mode", TextureMappingZone::DefaultCompactOffsetMode); zone.use_legacy_fixed_color_mode = texture.value("use_legacy_fixed_color_mode", TextureMappingZone::DefaultUseLegacyFixedColorMode); diff --git a/src/libslic3r/TextureMapping.hpp b/src/libslic3r/TextureMapping.hpp index 3803f06927c..839961c46ad 100644 --- a/src/libslic3r/TextureMapping.hpp +++ b/src/libslic3r/TextureMapping.hpp @@ -179,6 +179,10 @@ struct TextureMappingZone static constexpr bool DefaultTopSurfaceContoningReplaceTopPerimetersWithInfill = false; static constexpr bool DefaultTopSurfaceContoningRecolorSurroundingPerimeters = false; static constexpr int DefaultTopSurfaceContoningPerimeterMode = int(ContoningPerimeterDividedLine); + static constexpr bool DefaultTopSurfaceContoningLayerPhaseEnabled = false; + static constexpr bool DefaultTopSurfaceContoningVariedInfillAnglesEnabled = false; + static constexpr bool DefaultTopSurfaceContoningBlueNoiseErrorDiffusionEnabled = false; + static constexpr bool DefaultTopSurfaceContoningSupersampledCellsEnabled = false; static constexpr bool DefaultCompactOffsetMode = true; static constexpr bool DefaultUseLegacyFixedColorMode = false; static constexpr bool DefaultHighSpeedImageTextureSampling = true; @@ -282,6 +286,10 @@ struct TextureMappingZone bool top_surface_contoning_replace_top_perimeters_with_infill = DefaultTopSurfaceContoningReplaceTopPerimetersWithInfill; bool top_surface_contoning_recolor_surrounding_perimeters = DefaultTopSurfaceContoningRecolorSurroundingPerimeters; int top_surface_contoning_perimeter_mode = DefaultTopSurfaceContoningPerimeterMode; + bool top_surface_contoning_layer_phase_enabled = DefaultTopSurfaceContoningLayerPhaseEnabled; + bool top_surface_contoning_varied_infill_angles_enabled = DefaultTopSurfaceContoningVariedInfillAnglesEnabled; + bool top_surface_contoning_blue_noise_error_diffusion_enabled = DefaultTopSurfaceContoningBlueNoiseErrorDiffusionEnabled; + bool top_surface_contoning_supersampled_cells_enabled = DefaultTopSurfaceContoningSupersampledCellsEnabled; bool compact_offset_mode = DefaultCompactOffsetMode; bool use_legacy_fixed_color_mode = DefaultUseLegacyFixedColorMode; bool high_speed_image_texture_sampling = DefaultHighSpeedImageTextureSampling; @@ -401,6 +409,10 @@ struct TextureMappingZone top_surface_contoning_replace_top_perimeters_with_infill = DefaultTopSurfaceContoningReplaceTopPerimetersWithInfill; top_surface_contoning_recolor_surrounding_perimeters = DefaultTopSurfaceContoningRecolorSurroundingPerimeters; top_surface_contoning_perimeter_mode = DefaultTopSurfaceContoningPerimeterMode; + top_surface_contoning_layer_phase_enabled = DefaultTopSurfaceContoningLayerPhaseEnabled; + top_surface_contoning_varied_infill_angles_enabled = DefaultTopSurfaceContoningVariedInfillAnglesEnabled; + top_surface_contoning_blue_noise_error_diffusion_enabled = DefaultTopSurfaceContoningBlueNoiseErrorDiffusionEnabled; + top_surface_contoning_supersampled_cells_enabled = DefaultTopSurfaceContoningSupersampledCellsEnabled; compact_offset_mode = DefaultCompactOffsetMode; use_legacy_fixed_color_mode = DefaultUseLegacyFixedColorMode; high_speed_image_texture_sampling = DefaultHighSpeedImageTextureSampling; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 529494e456a..b0f64ccdbdc 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1908,6 +1908,10 @@ public: bool top_surface_contoning_replace_top_perimeters_with_infill, bool top_surface_contoning_recolor_surrounding_perimeters, int top_surface_contoning_perimeter_mode, + bool top_surface_contoning_layer_phase_enabled, + bool top_surface_contoning_varied_infill_angles_enabled, + bool top_surface_contoning_blue_noise_error_diffusion_enabled, + bool top_surface_contoning_supersampled_cells_enabled, const TextureMappingManager &texture_mapping_zones, const TextureMappingGlobalSettings &global_settings, const TextureMappingPrimeTowerImage &prime_tower_image, @@ -2594,6 +2598,42 @@ public: 0, wxEXPAND | wxTOP | wxBOTTOM, gap / 2); + m_top_surface_contoning_layer_phase_checkbox = + new wxCheckBox(m_top_surface_contoning_checkboxes_panel, wxID_ANY, _L("Layer phase detail")); + m_top_surface_contoning_layer_phase_checkbox->SetValue(top_surface_contoning_layer_phase_enabled); + m_top_surface_contoning_layer_phase_checkbox->SetMinSize( + wxSize(-1, std::max(m_top_surface_contoning_layer_phase_checkbox->GetBestSize().GetHeight(), FromDIP(24)))); + contoning_checkboxes_root->Add(m_top_surface_contoning_layer_phase_checkbox, + 0, + wxEXPAND | wxTOP | wxBOTTOM, + gap / 2); + m_top_surface_contoning_varied_infill_angles_checkbox = + new wxCheckBox(m_top_surface_contoning_checkboxes_panel, wxID_ANY, _L("Varied infill angles")); + m_top_surface_contoning_varied_infill_angles_checkbox->SetValue(top_surface_contoning_varied_infill_angles_enabled); + m_top_surface_contoning_varied_infill_angles_checkbox->SetMinSize( + wxSize(-1, std::max(m_top_surface_contoning_varied_infill_angles_checkbox->GetBestSize().GetHeight(), FromDIP(24)))); + contoning_checkboxes_root->Add(m_top_surface_contoning_varied_infill_angles_checkbox, + 0, + wxEXPAND | wxTOP | wxBOTTOM, + gap / 2); + m_top_surface_contoning_blue_noise_error_diffusion_checkbox = + new wxCheckBox(m_top_surface_contoning_checkboxes_panel, wxID_ANY, _L("Blue-noise error diffusion")); + m_top_surface_contoning_blue_noise_error_diffusion_checkbox->SetValue(top_surface_contoning_blue_noise_error_diffusion_enabled); + m_top_surface_contoning_blue_noise_error_diffusion_checkbox->SetMinSize( + wxSize(-1, std::max(m_top_surface_contoning_blue_noise_error_diffusion_checkbox->GetBestSize().GetHeight(), FromDIP(24)))); + contoning_checkboxes_root->Add(m_top_surface_contoning_blue_noise_error_diffusion_checkbox, + 0, + wxEXPAND | wxTOP | wxBOTTOM, + gap / 2); + m_top_surface_contoning_supersampled_cells_checkbox = + new wxCheckBox(m_top_surface_contoning_checkboxes_panel, wxID_ANY, _L("Supersampled cell colors")); + m_top_surface_contoning_supersampled_cells_checkbox->SetValue(top_surface_contoning_supersampled_cells_enabled); + m_top_surface_contoning_supersampled_cells_checkbox->SetMinSize( + wxSize(-1, std::max(m_top_surface_contoning_supersampled_cells_checkbox->GetBestSize().GetHeight(), FromDIP(24)))); + contoning_checkboxes_root->Add(m_top_surface_contoning_supersampled_cells_checkbox, + 0, + wxEXPAND | wxTOP | wxBOTTOM, + gap / 2); top_surface_box->Add(m_top_surface_contoning_checkboxes_panel, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP | wxBOTTOM, gap); m_top_surface_contoning_only_color_surface_infill_checkbox->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &) { update_top_surface_image_options_visibility(true); @@ -2986,6 +3026,26 @@ public: int(TextureMappingZone::ContoningPerimeterSegmentInfill)) : TextureMappingZone::DefaultTopSurfaceContoningPerimeterMode; } + bool top_surface_contoning_layer_phase_enabled() const + { + return m_top_surface_contoning_layer_phase_checkbox != nullptr && + m_top_surface_contoning_layer_phase_checkbox->GetValue(); + } + bool top_surface_contoning_varied_infill_angles_enabled() const + { + return m_top_surface_contoning_varied_infill_angles_checkbox != nullptr && + m_top_surface_contoning_varied_infill_angles_checkbox->GetValue(); + } + bool top_surface_contoning_blue_noise_error_diffusion_enabled() const + { + return m_top_surface_contoning_blue_noise_error_diffusion_checkbox != nullptr && + m_top_surface_contoning_blue_noise_error_diffusion_checkbox->GetValue(); + } + bool top_surface_contoning_supersampled_cells_enabled() const + { + return m_top_surface_contoning_supersampled_cells_checkbox != nullptr && + m_top_surface_contoning_supersampled_cells_checkbox->GetValue(); + } bool minimum_visibility_offset_enabled() const { return m_minimum_visibility_offset_checkbox && m_minimum_visibility_offset_checkbox->GetValue(); @@ -3457,6 +3517,22 @@ private: m_top_surface_contoning_perimeter_mode_panel->Show(show_perimeter_mode); if (m_top_surface_contoning_perimeter_mode_choice != nullptr) m_top_surface_contoning_perimeter_mode_choice->Enable(show_perimeter_mode); + if (m_top_surface_contoning_layer_phase_checkbox != nullptr) { + m_top_surface_contoning_layer_phase_checkbox->Show(contoning); + m_top_surface_contoning_layer_phase_checkbox->Enable(contoning); + } + if (m_top_surface_contoning_varied_infill_angles_checkbox != nullptr) { + m_top_surface_contoning_varied_infill_angles_checkbox->Show(contoning); + m_top_surface_contoning_varied_infill_angles_checkbox->Enable(contoning); + } + if (m_top_surface_contoning_blue_noise_error_diffusion_checkbox != nullptr) { + m_top_surface_contoning_blue_noise_error_diffusion_checkbox->Show(contoning); + m_top_surface_contoning_blue_noise_error_diffusion_checkbox->Enable(contoning); + } + if (m_top_surface_contoning_supersampled_cells_checkbox != nullptr) { + m_top_surface_contoning_supersampled_cells_checkbox->Show(contoning); + m_top_surface_contoning_supersampled_cells_checkbox->Enable(contoning); + } if (m_top_surface_image_fixed_coloring_filaments_checkbox != nullptr) { m_top_surface_image_fixed_coloring_filaments_checkbox->Show(!contoning_selected); m_top_surface_image_fixed_coloring_filaments_checkbox->Enable(enabled && !contoning_selected); @@ -3523,6 +3599,10 @@ private: wxCheckBox *m_top_surface_contoning_recolor_surrounding_perimeters_checkbox {nullptr}; wxPanel *m_top_surface_contoning_perimeter_mode_panel {nullptr}; wxChoice *m_top_surface_contoning_perimeter_mode_choice {nullptr}; + wxCheckBox *m_top_surface_contoning_layer_phase_checkbox {nullptr}; + wxCheckBox *m_top_surface_contoning_varied_infill_angles_checkbox {nullptr}; + wxCheckBox *m_top_surface_contoning_blue_noise_error_diffusion_checkbox {nullptr}; + wxCheckBox *m_top_surface_contoning_supersampled_cells_checkbox {nullptr}; wxCheckBox *m_use_legacy_fixed_color_mode_checkbox {nullptr}; wxCheckBox *m_minimum_visibility_offset_checkbox {nullptr}; wxSpinCtrl *m_minimum_visibility_offset_spin {nullptr}; @@ -8527,6 +8607,10 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager) updated.top_surface_contoning_replace_top_perimeters_with_infill, updated.top_surface_contoning_recolor_surrounding_perimeters, updated.top_surface_contoning_perimeter_mode, + updated.top_surface_contoning_layer_phase_enabled, + updated.top_surface_contoning_varied_infill_angles_enabled, + updated.top_surface_contoning_blue_noise_error_diffusion_enabled, + updated.top_surface_contoning_supersampled_cells_enabled, bundle->texture_mapping_zones, bundle->texture_mapping_global_settings, wxGetApp().model().texture_mapping_prime_tower_image, @@ -8587,6 +8671,13 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager) updated.top_surface_contoning_recolor_surrounding_perimeters = dlg.top_surface_contoning_recolor_surrounding_perimeters(); updated.top_surface_contoning_perimeter_mode = dlg.top_surface_contoning_perimeter_mode(); + updated.top_surface_contoning_layer_phase_enabled = dlg.top_surface_contoning_layer_phase_enabled(); + updated.top_surface_contoning_varied_infill_angles_enabled = + dlg.top_surface_contoning_varied_infill_angles_enabled(); + updated.top_surface_contoning_blue_noise_error_diffusion_enabled = + dlg.top_surface_contoning_blue_noise_error_diffusion_enabled(); + updated.top_surface_contoning_supersampled_cells_enabled = + dlg.top_surface_contoning_supersampled_cells_enabled(); if (updated.top_surface_image_printing_enabled && updated.top_surface_image_printing_method == int(TextureMappingZone::TopSurfaceImageContoning)) { updated.modulation_mode = int(TextureMappingZone::ModulationPerimeterPathV2);