diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index fba3c0b826f..b85e58e8cf4 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -2098,6 +2098,87 @@ static int top_surface_image_contoning_label_valid_depth(const TopSurfaceImageCo return label.valid_depth > 0 ? label.valid_depth : int(label.bottom_to_top.size()); } +static double top_surface_image_contoning_grid_cell_area_mm2( + int row, + int col, + coord_t min_x, + coord_t min_y, + coord_t step, + const BoundingBox &bbox) +{ + 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 0.; + return unscale(x1 - x0) * unscale(y1 - y0); +} + +static void top_surface_image_contoning_record_nearest_measured_sample_fallback_area( + const std::vector &label_grid, + int cols, + int rows, + const std::vector &labels, + const std::vector> &cell_samples, + const TextureMappingContoningSolver &solver, + bool lower_surface, + const std::vector &surface_to_deep_layer_heights_mm, + const std::vector &surface_to_deep_layer_ids, + coord_t min_x, + coord_t min_y, + coord_t step, + const BoundingBox &bbox, + const ThrowIfCanceled *throw_if_canceled) +{ + if (!solver.nearest_measured_sample_mode() || + label_grid.empty() || + labels.empty() || + cols <= 0 || + rows <= 0 || + label_grid.size() != size_t(cols) * size_t(rows) || + cell_samples.size() != label_grid.size()) + return; + + std::vector fallback_labels(labels.size(), 0); + for (size_t label_idx = 0; label_idx < labels.size(); ++label_idx) { + const TopSurfaceImageContoningVectorLabel &label = labels[label_idx]; + if (label.bottom_to_top.empty()) + continue; + const int visible_depth = top_surface_image_contoning_label_valid_depth(label); + fallback_labels[label_idx] = + !solver.nearest_measured_sample_stack_compatible(int(label.bottom_to_top.size()), + visible_depth, + surface_to_deep_layer_heights_mm, + surface_to_deep_layer_ids, + lower_surface); + } + + double fallback_area_mm2 = 0.; + double total_area_mm2 = 0.; + for (int row = 0; row < rows; ++row) { + if ((row & 15) == 0) + check_canceled(throw_if_canceled); + for (int col = 0; col < cols; ++col) { + const size_t grid_idx = size_t(row * cols + col); + if (!cell_samples[grid_idx]) + continue; + const int label = label_grid[grid_idx]; + if (label < 0 || label >= int(labels.size())) + continue; + const double area_mm2 = + top_surface_image_contoning_grid_cell_area_mm2(row, col, min_x, min_y, step, bbox); + if (area_mm2 <= 0.) + continue; + total_area_mm2 += area_mm2; + if (fallback_labels[size_t(label)]) + fallback_area_mm2 += area_mm2; + } + } + + solver.record_nearest_measured_sample_fallback_area(lower_surface, fallback_area_mm2, total_area_mm2); +} + struct TopSurfaceImageContoningDepthRegionPlan { std::vector> fill_regions_by_depth; std::vector> perimeter_regions_by_depth; @@ -6363,6 +6444,20 @@ static void top_surface_image_contoning_solve_anchored_region( top_surface_image_debug_elapsed_ms(debug_step_start), grid_cells, true); + top_surface_image_contoning_record_nearest_measured_sample_fallback_area(grid, + cols, + rows, + labels, + cell_samples, + solver, + lower_surface, + source_context.surface_to_deep_layer_heights_mm, + source_context.surface_to_deep_layer_ids, + min_x, + min_y, + step, + bbox, + throw_if_canceled); } check_canceled(throw_if_canceled); if (debug_enabled) @@ -8016,6 +8111,20 @@ static std::vector top_surface_image_conto source->surface_to_deep_layer_heights_mm, source->surface_to_deep_layer_ids, throw_if_canceled); + top_surface_image_contoning_record_nearest_measured_sample_fallback_area(grid, + cols, + rows, + labels, + cell_samples, + solver, + source_surface == TopSurfaceImageSourceSurface::Bottom, + source->surface_to_deep_layer_heights_mm, + source->surface_to_deep_layer_ids, + min_x, + min_y, + step, + bbox, + throw_if_canceled); } check_canceled(throw_if_canceled); @@ -8247,6 +8356,20 @@ static std::shared_ptr top_surface_imag source_context->surface_to_deep_layer_heights_mm, source_context->surface_to_deep_layer_ids, throw_if_canceled); + top_surface_image_contoning_record_nearest_measured_sample_fallback_area(grid, + cols, + rows, + out->labels, + cell_samples, + solver, + source_surface == TopSurfaceImageSourceSurface::Bottom, + source_context->surface_to_deep_layer_heights_mm, + source_context->surface_to_deep_layer_ids, + min_x, + min_y, + step, + bbox, + throw_if_canceled); } for (size_t idx = 0; idx < out->cells.size(); ++idx) out->cells[idx].label = grid[idx]; @@ -8663,6 +8786,95 @@ static std::string top_surface_image_contoning_nearest_measured_sample_fallback_ return ss.str(); } +static std::string top_surface_image_contoning_format_area_value(double value, int large_precision, int medium_precision, int small_precision) +{ + std::ostringstream ss; + const double abs_value = std::abs(value); + int precision = small_precision; + if (abs_value >= 100.) + precision = large_precision; + else if (abs_value >= 1.) + precision = medium_precision; + ss << std::fixed << std::setprecision(precision) << value; + return ss.str(); +} + +static std::string top_surface_image_contoning_format_fallback_percentage(double fallback_area_mm2, double total_area_mm2) +{ + if (!std::isfinite(fallback_area_mm2) || + !std::isfinite(total_area_mm2) || + total_area_mm2 <= 0.) + return {}; + const double pct = std::clamp(100. * fallback_area_mm2 / total_area_mm2, 0., 100.); + std::ostringstream ss; + int precision = 5; + if (pct >= 10.) + precision = 1; + else if (pct >= 1.) + precision = 2; + else if (pct >= 0.01) + precision = 3; + ss << std::fixed << std::setprecision(precision) << pct; + return ss.str(); +} + +static double top_surface_image_contoning_fallback_percentage_value( + const TextureMappingContoningNearestMeasuredSampleFallbackArea &area) +{ + if (!std::isfinite(area.fallback_area_mm2) || + !std::isfinite(area.total_area_mm2) || + area.total_area_mm2 <= 0.) + return std::numeric_limits::quiet_NaN(); + return std::clamp(100. * area.fallback_area_mm2 / area.total_area_mm2, 0., 100.); +} + +static std::optional +top_surface_image_contoning_nearest_measured_sample_total_fallback_area( + const TextureMappingContoningSolver &solver, + bool upper_nearest_sample_fallback, + bool lower_nearest_sample_fallback) +{ + TextureMappingContoningNearestMeasuredSampleFallbackArea total_area; + bool has_area = false; + auto add_area = [&](bool lower_surface) { + const TextureMappingContoningNearestMeasuredSampleFallbackArea area = + solver.nearest_measured_sample_fallback_area(lower_surface); + if (!std::isfinite(area.total_area_mm2) || area.total_area_mm2 <= 0.) + return; + total_area.fallback_area_mm2 += std::max(0., area.fallback_area_mm2); + total_area.total_area_mm2 += area.total_area_mm2; + has_area = true; + }; + + if (upper_nearest_sample_fallback) + add_area(false); + if (lower_nearest_sample_fallback) + add_area(true); + if (!has_area || total_area.total_area_mm2 <= 0.) + return std::nullopt; + return total_area; +} + +static std::string top_surface_image_contoning_nearest_measured_sample_fallback_area_details( + const TextureMappingContoningNearestMeasuredSampleFallbackArea &total_area) +{ + if (total_area.total_area_mm2 <= 0.) + return {}; + + const std::string pct = + top_surface_image_contoning_format_fallback_percentage(total_area.fallback_area_mm2, total_area.total_area_mm2); + if (pct.empty()) + return {}; + + std::ostringstream ss; + ss << "Fallback affected " << pct << "% of sampled surface area (" + << top_surface_image_contoning_format_area_value(total_area.fallback_area_mm2, 1, 2, 3) + << " mm2 of " + << top_surface_image_contoning_format_area_value(total_area.total_area_mm2, 1, 2, 3) + << " mm2)."; + return ss.str(); +} + struct TopSurfaceImageContoningComponentArea { unsigned int component_id = 0; ExPolygons area; @@ -9135,7 +9347,7 @@ static std::vector top_surface_image_region_plans( plan.contoning_flat_surface_infill_mode = std::clamp(zone->effective_top_surface_contoning_flat_surface_infill_mode(), int(TextureMappingZone::ContoningFlatSurfaceInfillRectilinear), - int(TextureMappingZone::ContoningFlatSurfaceInfillAdaptiveLines)); + int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinVariableOverlap)); plan.contoning_layer_phase_enabled = zone->effective_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 = @@ -9279,32 +9491,47 @@ static std::vector top_surface_image_region_plans( const bool upper_nearest_sample_fallback = contoning_solver.nearest_measured_sample_fallback_used(false); const bool lower_nearest_sample_fallback = contoning_solver.nearest_measured_sample_fallback_used(true); if ((upper_nearest_sample_fallback || lower_nearest_sample_fallback) && object != nullptr) { - const char *surface_name = - upper_nearest_sample_fallback && lower_nearest_sample_fallback ? - "upper and lower surfaces" : - (upper_nearest_sample_fallback ? "upper surfaces" : "lower surfaces"); - std::vector fallback_issues; - if (upper_nearest_sample_fallback) { - std::vector upper_issues = - contoning_solver.nearest_measured_sample_fallback_issues(false); - fallback_issues.insert(fallback_issues.end(), upper_issues.begin(), upper_issues.end()); + const std::optional fallback_area = + top_surface_image_contoning_nearest_measured_sample_total_fallback_area(contoning_solver, + upper_nearest_sample_fallback, + lower_nearest_sample_fallback); + const double fallback_pct = + fallback_area ? top_surface_image_contoning_fallback_percentage_value(*fallback_area) : + std::numeric_limits::quiet_NaN(); + if (!std::isfinite(fallback_pct) || fallback_pct >= 0.01) { + const char *surface_name = + upper_nearest_sample_fallback && lower_nearest_sample_fallback ? + "upper and lower surfaces" : + (upper_nearest_sample_fallback ? "upper surfaces" : "lower surfaces"); + std::vector fallback_issues; + if (upper_nearest_sample_fallback) { + std::vector upper_issues = + contoning_solver.nearest_measured_sample_fallback_issues(false); + fallback_issues.insert(fallback_issues.end(), upper_issues.begin(), upper_issues.end()); + } + if (lower_nearest_sample_fallback) { + std::vector lower_issues = + contoning_solver.nearest_measured_sample_fallback_issues(true); + fallback_issues.insert(fallback_issues.end(), lower_issues.begin(), lower_issues.end()); + } + std::string warning = Slic3r::format( + L("Top-surface color prediction fell back from calibrated nearest measured sample to %1% for %2% because the current layer heights or stack depth do not match the calibration sheet."), + contoning_solver.nearest_measured_sample_fallback_name(), + surface_name); + const std::string details = + top_surface_image_contoning_nearest_measured_sample_fallback_details(fallback_issues); + if (!details.empty()) + warning += " " + details; + if (fallback_area) { + const std::string area_details = + top_surface_image_contoning_nearest_measured_sample_fallback_area_details(*fallback_area); + if (!area_details.empty()) + warning += " " + area_details; + } + object->add_slicing_warning( + PrintStateBase::WarningLevel::NON_CRITICAL, + warning); } - if (lower_nearest_sample_fallback) { - std::vector lower_issues = - contoning_solver.nearest_measured_sample_fallback_issues(true); - fallback_issues.insert(fallback_issues.end(), lower_issues.begin(), lower_issues.end()); - } - std::string warning = Slic3r::format( - L("Top-surface color prediction fell back from calibrated nearest measured sample to %1% for %2% because the current layer heights or stack depth do not match the calibration sheet."), - contoning_solver.nearest_measured_sample_fallback_name(), - surface_name); - const std::string details = - top_surface_image_contoning_nearest_measured_sample_fallback_details(fallback_issues); - if (!details.empty()) - warning += " " + details; - object->add_slicing_warning( - PrintStateBase::WarningLevel::NON_CRITICAL, - warning); } } else { for (int depth = 0; depth < stack_depth; ++depth) { @@ -10867,6 +11094,7 @@ static bool top_surface_image_contoning_boundary_skin_mode(int mode) { return mode == int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinFixed) || mode == int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinVariable) || + mode == int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinVariableOverlap) || mode == int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinHybrid); } @@ -14599,7 +14827,12 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, surface_fill.params.texture_mapping_top_surface_contoning_flat_surface_infill_mode)) { const bool variable_width_boundary_skin = surface_fill.params.texture_mapping_top_surface_contoning_flat_surface_infill_mode == - int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinVariable); + int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinVariable) || + surface_fill.params.texture_mapping_top_surface_contoning_flat_surface_infill_mode == + int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinVariableOverlap); + const bool overlap_boundary_skin = + surface_fill.params.texture_mapping_top_surface_contoning_flat_surface_infill_mode == + int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinVariableOverlap); const bool hybrid_boundary_skin = surface_fill.params.texture_mapping_top_surface_contoning_flat_surface_infill_mode == int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinHybrid); @@ -14626,7 +14859,8 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, fallback_params.flow = fallback_flow; fallback_params.pattern = ipRectilinear; fallback_params.no_edge_overlap = true; - fallback_params.edge_overlap_width_factor = 0.0f; + fallback_params.edge_overlap_width_factor = + overlap_boundary_skin ? 1.0f : (variable_width_boundary_skin ? 0.5f : 0.0f); ExtrusionEntitiesPtr hybrid_interior_entities; if (!hybrid_boundary_skin && collection && !collection->empty()) { apply_top_surface_image_collection_metadata(*collection, surface_fill.params, std::nullopt, throw_if_canceled_ptr); diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp index 88d87a121cb..28b688483a2 100644 --- a/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -2358,6 +2358,7 @@ static std::vector perimeter_texture_sample_polygon const ExPolygon &source, const std::vector &erode_ladder, float erode_step_mm, + bool disable_smoothing, const PerimeterTextureMaskIndex *top_visible_recolor_mask = nullptr, const PerimeterTextureTopVisibleRecolorThresholds *top_visible_recolor_thresholds = nullptr) { @@ -2450,7 +2451,8 @@ static std::vector perimeter_texture_sample_polygon samples[idx].inset_mm = smoothed[idx]; }; - smooth_insets(std::max(0.30f, 1.15f * context.base_outer_width_mm)); + if (!disable_smoothing) + smooth_insets(std::max(0.30f, 1.15f * context.base_outer_width_mm)); for (int pass = 0; pass < 4; ++pass) { for (size_t idx = 0; idx < samples.size(); ++idx) { @@ -2467,7 +2469,8 @@ static std::vector perimeter_texture_sample_polygon } } - smooth_insets(std::max(0.20f, 0.45f * context.base_outer_width_mm)); + if (!disable_smoothing) + smooth_insets(std::max(0.20f, 0.45f * context.base_outer_width_mm)); return samples; } @@ -2496,6 +2499,7 @@ static Polygon perimeter_texture_moved_polygon_from_samples(const std::vectoris_string(); @@ -3220,7 +3230,7 @@ void TextureMappingManager::load_entries(const std::string &serialized, flat_surface_infill_mode_it->get() : TextureMappingZone::DefaultTopSurfaceContoningFlatSurfaceInfillMode, int(TextureMappingZone::ContoningFlatSurfaceInfillDefault), - int(TextureMappingZone::ContoningFlatSurfaceInfillAdaptiveLines)); + int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinVariableOverlap)); zone.top_surface_contoning_layer_phase_enabled = texture.value("top_surface_contoning_layer_phase_enabled", TextureMappingZone::DefaultTopSurfaceContoningLayerPhaseEnabled); diff --git a/src/libslic3r/TextureMapping.hpp b/src/libslic3r/TextureMapping.hpp index 4e61a615c99..671252b8c0f 100644 --- a/src/libslic3r/TextureMapping.hpp +++ b/src/libslic3r/TextureMapping.hpp @@ -97,7 +97,8 @@ struct TextureMappingZone ContoningFlatSurfaceInfillBoundarySkinHybrid = 6, ContoningFlatSurfaceInfillRectilinearWithBoundary = 7, ContoningFlatSurfaceInfillRectilinearWithRepair = 8, - ContoningFlatSurfaceInfillAdaptiveLines = 9 + ContoningFlatSurfaceInfillAdaptiveLines = 9, + ContoningFlatSurfaceInfillBoundarySkinVariableOverlap = 10 }; enum TopSurfaceContoningColorPredictionMode : uint8_t { @@ -190,6 +191,7 @@ struct TextureMappingZone static constexpr int DefaultModulationMode = DefaultImageTextureModulationMode; static constexpr bool DefaultModulationModeManuallyChanged = false; static constexpr bool DefaultUseModulatedOverhangGeometryForSupport = false; + static constexpr bool DefaultDisableV2PerimeterPathModulationSmoothing = false; static constexpr bool DefaultRecolorSmallPerimeterLoops = false; static constexpr bool DefaultRecolorTopVisiblePerimeterSections = false; static constexpr int DefaultTopVisiblePerimeterRecolorAggressiveness = int(TopVisibleRecolorAggressive); @@ -305,11 +307,12 @@ struct TextureMappingZone { const int clamped_mode = std::clamp(mode, int(ContoningFlatSurfaceInfillDefault), - int(ContoningFlatSurfaceInfillAdaptiveLines)); + int(ContoningFlatSurfaceInfillBoundarySkinVariableOverlap)); if (ShowExperimentalTopSurfaceContoningOptions) return clamped_mode; return clamped_mode == int(ContoningFlatSurfaceInfillRectilinear) || clamped_mode == int(ContoningFlatSurfaceInfillBoundarySkinVariable) || + clamped_mode == int(ContoningFlatSurfaceInfillBoundarySkinVariableOverlap) || clamped_mode == int(ContoningFlatSurfaceInfillAdaptiveLines) ? clamped_mode : DefaultTopSurfaceContoningFlatSurfaceInfillMode; @@ -485,6 +488,7 @@ struct TextureMappingZone bool nonlinear_offset_adjustment = DefaultNonlinearOffsetAdjustment; int modulation_mode = DefaultModulationMode; bool use_modulated_overhang_geometry_for_support = DefaultUseModulatedOverhangGeometryForSupport; + bool disable_v2_perimeter_path_modulation_smoothing = DefaultDisableV2PerimeterPathModulationSmoothing; bool modulation_mode_manually_changed = DefaultModulationModeManuallyChanged; bool recolor_small_perimeter_loops = DefaultRecolorSmallPerimeterLoops; bool recolor_top_visible_perimeter_sections = DefaultRecolorTopVisiblePerimeterSections; @@ -578,15 +582,13 @@ struct TextureMappingZone (is_image_texture() || is_surface_gradient()) && (top_surface_image_printing_method == int(TopSurfaceImageSameAngle45Width) || top_surface_image_printing_method == int(TopSurfaceImageSameLayer45Partition) || - (top_surface_image_printing_method == int(TopSurfaceImageContoning) && - uses_perimeter_path_modulation_v2())); + top_surface_image_printing_method == int(TopSurfaceImageContoning)); } bool top_surface_contoning_active() const { return top_surface_image_printing_enabled && (is_image_texture() || is_surface_gradient()) && - top_surface_image_printing_method == int(TopSurfaceImageContoning) && - uses_perimeter_path_modulation_v2(); + top_surface_image_printing_method == int(TopSurfaceImageContoning); } bool top_surface_image_fixed_coloring_filaments_active() const @@ -598,6 +600,7 @@ struct TextureMappingZone { return is_image_texture() && top_surface_contoning_active() && + uses_perimeter_path_modulation_v2() && top_surface_contoning_colors_upper_surfaces() && !effective_top_surface_contoning_only_color_surface_infill() && !effective_top_surface_contoning_replace_top_perimeters_with_infill() && @@ -751,8 +754,6 @@ struct TextureMappingZone { if (top_surface_image_printing_enabled && top_surface_image_printing_method == int(TopSurfaceImageContoning)) { - modulation_mode = int(ModulationPerimeterPathV2); - modulation_mode_manually_changed = true; top_surface_image_fixed_coloring_filaments = true; } if (!ShowExperimentalTopSurfaceContoningOptions) { @@ -814,6 +815,7 @@ struct TextureMappingZone seam_hiding = DefaultSeamHiding; nonlinear_offset_adjustment = DefaultNonlinearOffsetAdjustment; use_modulated_overhang_geometry_for_support = DefaultUseModulatedOverhangGeometryForSupport; + disable_v2_perimeter_path_modulation_smoothing = DefaultDisableV2PerimeterPathModulationSmoothing; modulation_mode = default_modulation_mode_for_surface_pattern(surface_pattern); modulation_mode_manually_changed = DefaultModulationModeManuallyChanged; recolor_small_perimeter_loops = DefaultRecolorSmallPerimeterLoops; diff --git a/src/libslic3r/TextureMappingContoning.cpp b/src/libslic3r/TextureMappingContoning.cpp index 96dfff39c21..be7251ff805 100644 --- a/src/libslic3r/TextureMappingContoning.cpp +++ b/src/libslic3r/TextureMappingContoning.cpp @@ -548,6 +548,15 @@ TextureMappingContoningSolver::nearest_measured_sample_fallback_issues(bool lowe return out; } +TextureMappingContoningNearestMeasuredSampleFallbackArea +TextureMappingContoningSolver::nearest_measured_sample_fallback_area(bool lower_surface) const +{ + if (!m_nearest_measured_sample_fallback_area_mutex || !m_nearest_measured_sample_fallback_areas) + return {}; + std::lock_guard lock(*m_nearest_measured_sample_fallback_area_mutex); + return (*m_nearest_measured_sample_fallback_areas)[lower_surface ? 1 : 0]; +} + std::string TextureMappingContoningSolver::nearest_measured_sample_fallback_name() const { if (m_nearest_measured_sample_fallback_model.valid()) { @@ -559,6 +568,39 @@ std::string TextureMappingContoningSolver::nearest_measured_sample_fallback_name return "default uncalibrated"; } +bool TextureMappingContoningSolver::nearest_measured_sample_stack_compatible( + int stack_layers, + int visible_depth, + const std::vector &surface_to_deep_layer_heights_mm, + const std::vector &surface_to_deep_layer_ids, + bool lower_surface) const +{ + return nearest_measured_sample_compatible(stack_layers, + visible_depth, + surface_to_deep_layer_heights_mm, + surface_to_deep_layer_ids, + lower_surface, + false); +} + +void TextureMappingContoningSolver::record_nearest_measured_sample_fallback_area( + bool lower_surface, + double fallback_area_mm2, + double total_area_mm2) const +{ + if (!m_nearest_measured_sample_fallback_area_mutex || !m_nearest_measured_sample_fallback_areas) + return; + if (!std::isfinite(total_area_mm2) || total_area_mm2 <= 0.) + return; + if (!std::isfinite(fallback_area_mm2)) + fallback_area_mm2 = 0.; + std::lock_guard lock(*m_nearest_measured_sample_fallback_area_mutex); + TextureMappingContoningNearestMeasuredSampleFallbackArea &area = + (*m_nearest_measured_sample_fallback_areas)[lower_surface ? 1 : 0]; + area.fallback_area_mm2 += std::clamp(fallback_area_mm2, 0., total_area_mm2); + area.total_area_mm2 += total_area_mm2; +} + const std::vector& TextureMappingContoningSolver::candidates_for_depth(int stack_layers) const { diff --git a/src/libslic3r/TextureMappingContoning.hpp b/src/libslic3r/TextureMappingContoning.hpp index 0b8ea8a066f..15ead60b6b2 100644 --- a/src/libslic3r/TextureMappingContoning.hpp +++ b/src/libslic3r/TextureMappingContoning.hpp @@ -38,6 +38,11 @@ struct TextureMappingContoningNearestMeasuredSampleFallbackIssue { float expected_layer_height_mm { 0.f }; }; +struct TextureMappingContoningNearestMeasuredSampleFallbackArea { + double fallback_area_mm2 { 0. }; + double total_area_mm2 { 0. }; +}; + class TextureMappingContoningSolver { public: @@ -56,7 +61,16 @@ public: bool nearest_measured_sample_fallback_used() const; bool nearest_measured_sample_fallback_used(bool lower_surface) const; std::vector nearest_measured_sample_fallback_issues(bool lower_surface) const; + TextureMappingContoningNearestMeasuredSampleFallbackArea nearest_measured_sample_fallback_area(bool lower_surface) const; std::string nearest_measured_sample_fallback_name() const; + bool nearest_measured_sample_stack_compatible(int stack_layers, + int visible_depth, + const std::vector &surface_to_deep_layer_heights_mm, + const std::vector &surface_to_deep_layer_ids, + bool lower_surface) const; + void record_nearest_measured_sample_fallback_area(bool lower_surface, + double fallback_area_mm2, + double total_area_mm2) const; TextureMappingContoningStack solve(const std::array &target_rgb, int stack_layers, @@ -153,6 +167,8 @@ private: mutable std::shared_ptr> m_nearest_measured_sample_lower_fallback_used { std::make_shared>(false) }; mutable std::shared_ptr m_nearest_measured_sample_fallback_issue_mutex { std::make_shared() }; mutable std::shared_ptr> m_nearest_measured_sample_fallback_issues { std::make_shared>() }; + mutable std::shared_ptr m_nearest_measured_sample_fallback_area_mutex { std::make_shared() }; + mutable std::shared_ptr> m_nearest_measured_sample_fallback_areas { std::make_shared>() }; }; std::vector texture_mapping_contoning_components_bottom_to_top(const TextureMappingZone &zone, diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp index eb3d7afa0b3..87ec7cafd6e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp @@ -8462,14 +8462,6 @@ static std::vector auto_enable_raw_top_surface_projection_zones(co zone->top_surface_contoning_color_lower_surfaces = true; changed = true; } - if (zone->modulation_mode != int(TextureMappingZone::ModulationPerimeterPathV2)) { - zone->modulation_mode = int(TextureMappingZone::ModulationPerimeterPathV2); - changed = true; - } - if (!zone->modulation_mode_manually_changed) { - zone->modulation_mode_manually_changed = true; - changed = true; - } } if (changed) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 76b29e1a1b7..528d75a6bfd 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2503,6 +2503,7 @@ public: bool nonlinear_offset_adjustment, int modulation_mode, bool use_modulated_overhang_geometry_for_support, + bool disable_v2_perimeter_path_modulation_smoothing, bool modulation_mode_manually_changed, bool recolor_small_perimeter_loops, bool recolor_top_visible_perimeter_sections, @@ -2882,7 +2883,7 @@ public: auto *modulation_mode_row = new wxBoxSizer(wxHORIZONTAL); modulation_mode_row->Add(new wxStaticText(print_settings_page, wxID_ANY, _L("Modulation mode:")), 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, gap); m_modulation_mode_choice = new wxChoice(print_settings_page, wxID_ANY); - update_modulation_mode_choices(false, modulation_mode); + update_modulation_mode_choices(modulation_mode); modulation_mode_row->Add(m_modulation_mode_choice, 1, wxALIGN_CENTER_VERTICAL); print_settings_root->Add(modulation_mode_row, 0, wxEXPAND | wxALL, gap); m_modulation_mode_choice->Bind(wxEVT_CHOICE, [this](wxCommandEvent &) { @@ -2984,6 +2985,14 @@ public: new wxCheckBox(experimental_page, wxID_ANY, _L("Use modulated overhang geometry in support generation")); m_use_modulated_overhang_geometry_for_support_checkbox->SetValue(use_modulated_overhang_geometry_for_support); experimental_box->Add(m_use_modulated_overhang_geometry_for_support_checkbox, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, gap); + m_disable_v2_perimeter_path_modulation_smoothing_checkbox = + new wxCheckBox(experimental_page, wxID_ANY, _L("Disable v2 perimeter path modulation smoothing")); + m_disable_v2_perimeter_path_modulation_smoothing_checkbox->SetValue( + modulation_mode == int(TextureMappingZone::ModulationPerimeterPathV2) && + disable_v2_perimeter_path_modulation_smoothing); + m_disable_v2_perimeter_path_modulation_smoothing_checkbox->SetToolTip( + _L("Leaves v2 perimeter path modulation insets unsmoothed so fine texture changes can produce bumpier path geometry.")); + experimental_box->Add(m_disable_v2_perimeter_path_modulation_smoothing_checkbox, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, gap); m_top_visible_perimeter_recolor_point_sampling_checkbox = new wxCheckBox(experimental_page, wxID_ANY, _L("Point-sample visible layer-line recolor")); m_top_visible_perimeter_recolor_point_sampling_checkbox->SetValue(top_visible_perimeter_recolor_point_sampling); @@ -3295,6 +3304,9 @@ public: if (!TextureMappingZone::ShowExperimentalTopSurfaceContoningOptions) add_contoning_flat_infill_choice(_L("Boundary Skin (variable width)"), int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinVariable)); + if (!TextureMappingZone::ShowExperimentalTopSurfaceContoningOptions) + add_contoning_flat_infill_choice(_L("Boundary Skin (variable width, more overlap)"), + int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinVariableOverlap)); if (TextureMappingZone::ShowExperimentalTopSurfaceContoningOptions) { add_contoning_flat_infill_choice(_L("Concentric"), int(TextureMappingZone::ContoningFlatSurfaceInfillConcentric)); @@ -3302,6 +3314,8 @@ public: int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinFixed)); add_contoning_flat_infill_choice(_L("Boundary Skin (variable width)"), int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinVariable)); + add_contoning_flat_infill_choice(_L("Boundary Skin (variable width, more overlap)"), + int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinVariableOverlap)); add_contoning_flat_infill_choice(_L("Spiral"), int(TextureMappingZone::ContoningFlatSurfaceInfillSpiral)); add_contoning_flat_infill_choice(_L("Boundary Skin Hybrid"), @@ -3901,6 +3915,12 @@ public: return m_use_modulated_overhang_geometry_for_support_checkbox != nullptr && m_use_modulated_overhang_geometry_for_support_checkbox->GetValue(); } + bool disable_v2_perimeter_path_modulation_smoothing() const + { + return modulation_mode() == int(TextureMappingZone::ModulationPerimeterPathV2) && + m_disable_v2_perimeter_path_modulation_smoothing_checkbox != nullptr && + m_disable_v2_perimeter_path_modulation_smoothing_checkbox->GetValue(); + } int modulation_mode() const { if (m_modulation_mode_choice == nullptr) @@ -5435,6 +5455,8 @@ private: m_recolor_top_visible_perimeter_sections_checkbox->Enable(perimeter_path_mode); if (m_use_modulated_overhang_geometry_for_support_checkbox != nullptr) m_use_modulated_overhang_geometry_for_support_checkbox->Enable(perimeter_path_v2_mode); + if (m_disable_v2_perimeter_path_modulation_smoothing_checkbox != nullptr) + m_disable_v2_perimeter_path_modulation_smoothing_checkbox->Enable(perimeter_path_v2_mode); if (m_top_visible_perimeter_recolor_point_sampling_checkbox != nullptr) m_top_visible_perimeter_recolor_point_sampling_checkbox->Enable(perimeter_path_v2_mode); const bool top_visible_enabled = @@ -5458,20 +5480,16 @@ private: } } - void update_modulation_mode_choices(bool force_perimeter_path_v2, int preferred_mode) + void update_modulation_mode_choices(int preferred_mode) { if (m_modulation_mode_choice == nullptr) return; - const int mode = force_perimeter_path_v2 ? - int(TextureMappingZone::ModulationPerimeterPathV2) : - std::clamp(preferred_mode, - int(TextureMappingZone::ModulationLineWidth), - int(TextureMappingZone::ModulationPerimeterPathV2)); + const int mode = std::clamp(preferred_mode, + int(TextureMappingZone::ModulationLineWidth), + int(TextureMappingZone::ModulationPerimeterPathV2)); m_modulation_mode_choice->Clear(); m_modulation_mode_choice_values.clear(); - auto append_mode = [this, force_perimeter_path_v2](int value, const wxString &label) { - if (force_perimeter_path_v2 && value != int(TextureMappingZone::ModulationPerimeterPathV2)) - return; + auto append_mode = [this](int value, const wxString &label) { m_modulation_mode_choice->Append(label); m_modulation_mode_choice_values.emplace_back(value); }; @@ -5530,6 +5548,9 @@ private: TextureMappingZone::effective_top_surface_contoning_flat_surface_infill_mode( top_surface_contoning_flat_surface_infill_mode()) == int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinVariable) || + TextureMappingZone::effective_top_surface_contoning_flat_surface_infill_mode( + top_surface_contoning_flat_surface_infill_mode()) == + int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinVariableOverlap) || TextureMappingZone::effective_top_surface_contoning_flat_surface_infill_mode( top_surface_contoning_flat_surface_infill_mode()) == int(TextureMappingZone::ContoningFlatSurfaceInfillAdaptiveLines); @@ -5579,10 +5600,7 @@ private: const bool contoning = enabled && contoning_selected; if (m_modulation_mode_choice != nullptr) { const int preferred_mode = modulation_mode(); - update_modulation_mode_choices(contoning, preferred_mode); - } - if (contoning) { - m_modulation_mode_manually_changed = true; + update_modulation_mode_choices(preferred_mode); update_modulation_mode_options_visibility(false); } const bool same_layer = @@ -5758,6 +5776,7 @@ private: wxCheckBox *m_nonlinear_offset_adjustment_checkbox {nullptr}; wxSpinCtrl *m_filament_overhang_contrast_spin {nullptr}; wxCheckBox *m_use_modulated_overhang_geometry_for_support_checkbox {nullptr}; + wxCheckBox *m_disable_v2_perimeter_path_modulation_smoothing_checkbox {nullptr}; wxChoice *m_modulation_mode_choice {nullptr}; std::vector m_modulation_mode_choice_values; bool m_modulation_mode_manually_changed {false}; @@ -10862,6 +10881,7 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager) updated.nonlinear_offset_adjustment, updated.modulation_mode, updated.use_modulated_overhang_geometry_for_support, + updated.disable_v2_perimeter_path_modulation_smoothing, updated.modulation_mode_manually_changed, updated.recolor_small_perimeter_loops, updated.recolor_top_visible_perimeter_sections, @@ -10958,6 +10978,9 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager) updated.use_modulated_overhang_geometry_for_support = dlg.use_modulated_overhang_geometry_for_support(); updated.modulation_mode_manually_changed = dlg.modulation_mode_manually_changed(); updated.apply_default_modulation_mode(); + updated.disable_v2_perimeter_path_modulation_smoothing = + updated.uses_perimeter_path_modulation_v2() && + dlg.disable_v2_perimeter_path_modulation_smoothing(); updated.recolor_small_perimeter_loops = dlg.recolor_small_perimeter_loops(); updated.recolor_top_visible_perimeter_sections = dlg.recolor_top_visible_perimeter_sections(); updated.top_visible_perimeter_recolor_aggressiveness = dlg.top_visible_perimeter_recolor_aggressiveness(); @@ -11037,11 +11060,6 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager) dlg.side_surface_color_calibration_name(); updated.side_surface_color_calibration_json = dlg.side_surface_color_calibration_json(); - if (updated.top_surface_image_printing_enabled && - updated.top_surface_image_printing_method == int(TextureMappingZone::TopSurfaceImageContoning)) { - updated.modulation_mode = int(TextureMappingZone::ModulationPerimeterPathV2); - updated.modulation_mode_manually_changed = true; - } if (updated.dithering_enabled) updated.compact_offset_mode = true; updated.minimum_visibility_offset_enabled = dlg.minimum_visibility_offset_enabled(); @@ -17392,16 +17410,21 @@ void Plater::priv::clear_warnings() } bool Plater::priv::warnings_dialog() { - if (current_warnings.empty()) + std::vector*> critical_warnings; + for (auto const& it : current_warnings) + if (it.first.level == PrintStateBase::WarningLevel::CRITICAL) + critical_warnings.emplace_back(&it); + + if (critical_warnings.empty()) return true; std::string text = _u8L("There are warnings after slicing models:") + "\n"; - for (auto const& it : current_warnings) { - size_t next_n = it.first.message.find_first_of('\n', 0); + for (auto const* it : critical_warnings) { + size_t next_n = it->first.message.find_first_of('\n', 0); text += "\n"; if (next_n != std::string::npos) - text += it.first.message.substr(0, next_n); + text += it->first.message.substr(0, next_n); else - text += it.first.message; + text += it->first.message; } //text += "\n\nDo you still wish to export?"; MessageDialog msg_window(this->q, from_u8(text), _L("warnings"), wxOK);