Allow using top-surface coloring with side-surface variable line width modulation mode. Add boundary skin mode with more overlap

- Improve warnings for nearest measured sample mode fallback
This commit is contained in:
sentientstardust
2026-06-14 09:23:13 +01:00
parent 4606acea73
commit e8444445e8
8 changed files with 406 additions and 73 deletions

View File

@@ -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<coord_t>(x0 + step, bbox.max.x());
const coord_t y1 = std::min<coord_t>(y0 + step, bbox.max.y());
if (x1 <= x0 || y1 <= y0)
return 0.;
return unscale<double>(x1 - x0) * unscale<double>(y1 - y0);
}
static void top_surface_image_contoning_record_nearest_measured_sample_fallback_area(
const std::vector<int> &label_grid,
int cols,
int rows,
const std::vector<TopSurfaceImageContoningVectorLabel> &labels,
const std::vector<std::optional<TopSurfaceImageContoningCellSample>> &cell_samples,
const TextureMappingContoningSolver &solver,
bool lower_surface,
const std::vector<float> &surface_to_deep_layer_heights_mm,
const std::vector<int> &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<unsigned char> 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<std::vector<TopSurfaceImageContoningVectorRegion>> fill_regions_by_depth;
std::vector<std::vector<TopSurfaceImageContoningVectorRegion>> 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<TopSurfaceImageContoningVectorRegion> 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<const TopSurfaceImageContoningStackPlan> 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<double>::quiet_NaN();
return std::clamp(100. * area.fallback_area_mm2 / area.total_area_mm2, 0., 100.);
}
static std::optional<TextureMappingContoningNearestMeasuredSampleFallbackArea>
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<TopSurfaceImageRegionPlan> 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<TopSurfaceImageRegionPlan> 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<TextureMappingContoningNearestMeasuredSampleFallbackIssue> fallback_issues;
if (upper_nearest_sample_fallback) {
std::vector<TextureMappingContoningNearestMeasuredSampleFallbackIssue> 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<TextureMappingContoningNearestMeasuredSampleFallbackArea> 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<double>::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<TextureMappingContoningNearestMeasuredSampleFallbackIssue> fallback_issues;
if (upper_nearest_sample_fallback) {
std::vector<TextureMappingContoningNearestMeasuredSampleFallbackIssue> 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<TextureMappingContoningNearestMeasuredSampleFallbackIssue> 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<TextureMappingContoningNearestMeasuredSampleFallbackIssue> 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);

View File

@@ -2358,6 +2358,7 @@ static std::vector<PerimeterPathBoundarySample> perimeter_texture_sample_polygon
const ExPolygon &source,
const std::vector<ExPolygons> &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<PerimeterPathBoundarySample> 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<PerimeterPathBoundarySample> 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::vector<Pe
static ExPolygons perimeter_texture_modulated_expolygon(const ExPolygon &source,
const TextureMappingOffsetContext &context,
bool disable_smoothing,
const ExPolygons *top_visible_recolor_mask,
const PerimeterTextureTopVisibleRecolorThresholds *top_visible_recolor_thresholds,
bool &ok)
@@ -2522,6 +2526,7 @@ static ExPolygons perimeter_texture_modulated_expolygon(const ExPolygon &source,
source,
erode_ladder,
erode_step_mm,
disable_smoothing,
protection_mask_ptr,
top_visible_recolor_thresholds);
if (contour_samples.size() < 3)
@@ -2542,6 +2547,7 @@ static ExPolygons perimeter_texture_modulated_expolygon(const ExPolygon &source,
source,
erode_ladder,
erode_step_mm,
disable_smoothing,
protection_mask_ptr,
top_visible_recolor_thresholds));
if (hole_samples.back().size() < 3)
@@ -2633,10 +2639,18 @@ static SurfaceCollection perimeter_path_modulated_surfaces(const LayerRegion
SurfaceCollection out;
out.surfaces.reserve(slices.surfaces.size());
const bool disable_smoothing =
zone.uses_perimeter_path_modulation_v2() &&
zone.disable_v2_perimeter_path_modulation_smoothing;
for (const Surface &surface : slices.surfaces) {
bool ok = false;
ExPolygons modulated =
perimeter_texture_modulated_expolygon(surface.expolygon, *context, top_visible_recolor_mask, top_visible_recolor_thresholds, ok);
perimeter_texture_modulated_expolygon(surface.expolygon,
*context,
disable_smoothing,
top_visible_recolor_mask,
top_visible_recolor_thresholds,
ok);
if (ok && !modulated.empty())
out.append(std::move(modulated), surface);
else

View File

@@ -853,7 +853,7 @@ static std::string top_surface_contoning_flat_surface_infill_mode_name(int mode)
{
switch (clamp_int(mode,
int(TextureMappingZone::ContoningFlatSurfaceInfillDefault),
int(TextureMappingZone::ContoningFlatSurfaceInfillAdaptiveLines))) {
int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinVariableOverlap))) {
case int(TextureMappingZone::ContoningFlatSurfaceInfillRectilinear):
return "rectilinear";
case int(TextureMappingZone::ContoningFlatSurfaceInfillRectilinearWithBoundary):
@@ -866,6 +866,8 @@ static std::string top_surface_contoning_flat_surface_infill_mode_name(int mode)
return "boundary_skin_fixed";
case int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinVariable):
return "boundary_skin_variable";
case int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinVariableOverlap):
return "boundary_skin_variable_overlap";
case int(TextureMappingZone::ContoningFlatSurfaceInfillSpiral):
return "spiral";
case int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinHybrid):
@@ -889,6 +891,8 @@ static int top_surface_contoning_flat_surface_infill_mode_from_name(const std::s
return int(TextureMappingZone::ContoningFlatSurfaceInfillConcentric);
if (name == "boundary_skin" || name == "boundary_skin_variable")
return int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinVariable);
if (name == "boundary_skin_variable_overlap" || name == "boundary_skin_overlap")
return int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinVariableOverlap);
if (name == "boundary_skin_fixed")
return int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinFixed);
if (name == "spiral")
@@ -2435,6 +2439,8 @@ bool TextureMappingZone::operator==(const TextureMappingZone &rhs) const
nonlinear_offset_adjustment == rhs.nonlinear_offset_adjustment &&
modulation_mode == rhs.modulation_mode &&
use_modulated_overhang_geometry_for_support == rhs.use_modulated_overhang_geometry_for_support &&
(!uses_perimeter_path_modulation_v2() ||
disable_v2_perimeter_path_modulation_smoothing == rhs.disable_v2_perimeter_path_modulation_smoothing) &&
modulation_mode_manually_changed == rhs.modulation_mode_manually_changed &&
recolor_small_perimeter_loops == rhs.recolor_small_perimeter_loops &&
recolor_top_visible_perimeter_sections == rhs.recolor_top_visible_perimeter_sections &&
@@ -2812,6 +2818,7 @@ std::string TextureMappingManager::serialize_entries()
texture["nonlinear_offset_adjustment"] = zone.nonlinear_offset_adjustment;
texture["modulation_mode"] = modulation_mode_name(zone.modulation_mode);
texture["use_modulated_overhang_geometry_for_support"] = zone.use_modulated_overhang_geometry_for_support;
texture["disable_v2_perimeter_path_modulation_smoothing"] = zone.disable_v2_perimeter_path_modulation_smoothing;
texture["modulation_mode_manually_changed"] = zone.modulation_mode_manually_changed;
texture["recolor_small_perimeter_loops"] = zone.recolor_small_perimeter_loops || zone.recolor_top_visible_perimeter_sections;
texture["recolor_top_visible_perimeter_sections"] = zone.recolor_top_visible_perimeter_sections;
@@ -3099,6 +3106,9 @@ void TextureMappingManager::load_entries(const std::string &serialized,
zone.use_modulated_overhang_geometry_for_support =
texture.value("use_modulated_overhang_geometry_for_support",
TextureMappingZone::DefaultUseModulatedOverhangGeometryForSupport);
zone.disable_v2_perimeter_path_modulation_smoothing =
texture.value("disable_v2_perimeter_path_modulation_smoothing",
TextureMappingZone::DefaultDisableV2PerimeterPathModulationSmoothing);
const auto modulation_mode_it = texture.find("modulation_mode");
const bool has_modulation_mode =
modulation_mode_it != texture.end() && modulation_mode_it->is_string();
@@ -3220,7 +3230,7 @@ void TextureMappingManager::load_entries(const std::string &serialized,
flat_surface_infill_mode_it->get<int>() :
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);

View File

@@ -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;

View File

@@ -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<std::mutex> 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<float> &surface_to_deep_layer_heights_mm,
const std::vector<int> &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<std::mutex> 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::Candidate>&
TextureMappingContoningSolver::candidates_for_depth(int stack_layers) const
{

View File

@@ -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<TextureMappingContoningNearestMeasuredSampleFallbackIssue> 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<float> &surface_to_deep_layer_heights_mm,
const std::vector<int> &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<float, 3> &target_rgb,
int stack_layers,
@@ -153,6 +167,8 @@ private:
mutable std::shared_ptr<std::atomic<bool>> m_nearest_measured_sample_lower_fallback_used { std::make_shared<std::atomic<bool>>(false) };
mutable std::shared_ptr<std::mutex> m_nearest_measured_sample_fallback_issue_mutex { std::make_shared<std::mutex>() };
mutable std::shared_ptr<std::vector<TextureMappingContoningNearestMeasuredSampleFallbackIssue>> m_nearest_measured_sample_fallback_issues { std::make_shared<std::vector<TextureMappingContoningNearestMeasuredSampleFallbackIssue>>() };
mutable std::shared_ptr<std::mutex> m_nearest_measured_sample_fallback_area_mutex { std::make_shared<std::mutex>() };
mutable std::shared_ptr<std::array<TextureMappingContoningNearestMeasuredSampleFallbackArea, 2>> m_nearest_measured_sample_fallback_areas { std::make_shared<std::array<TextureMappingContoningNearestMeasuredSampleFallbackArea, 2>>() };
};
std::vector<unsigned int> texture_mapping_contoning_components_bottom_to_top(const TextureMappingZone &zone,

View File

@@ -8462,14 +8462,6 @@ static std::vector<unsigned int> 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)

View File

@@ -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<int> 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<const std::pair<Slic3r::PrintStateBase::Warning, size_t>*> 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);