From 1aae13641a2987ecbae76ebd52398b0e403aeb16 Mon Sep 17 00:00:00 2001 From: sentientstardust Date: Thu, 28 May 2026 09:23:27 +0100 Subject: [PATCH] Convert gradients to RGB for areas that don't support raw offset --- src/libslic3r/Fill/Fill.cpp | 36 +++-- src/libslic3r/TextureMapping.hpp | 7 +- src/libslic3r/TextureMappingOffset.cpp | 199 ++++++++++++++++++------- src/libslic3r/TextureMappingOffset.hpp | 12 ++ 4 files changed, 185 insertions(+), 69 deletions(-) diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index 96c2cafa3d1..ea4eb90b955 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -1939,10 +1939,10 @@ static std::optional top_surface_image_conto return; } const std::optional> rgb = - sample_weight_field_rgb(context.weight_field, - sample_x_mm, - sample_y_mm, - context.high_resolution_texture_sampling); + texture_mapping_offset_target_rgb_at_point(context, + sample_x_mm, + sample_y_mm, + std::numeric_limits::quiet_NaN()); if (!rgb) return; out.rgb[0] += (*rgb)[0]; @@ -3064,7 +3064,9 @@ static std::vector top_surface_image_region_plans( std::vector filament_colours = print_config.filament_colour.values; filament_colours.resize(num_physical, "#FFFFFF"); std::vector components = - TextureMappingManager::effective_texture_component_ids(*zone, num_physical, filament_colours); + zone->is_image_texture() ? + TextureMappingManager::effective_texture_component_ids(*zone, num_physical, filament_colours) : + TextureMappingManager::selected_component_ids(*zone, num_physical); components.erase(std::remove_if(components.begin(), components.end(), [num_physical](unsigned int id) { return id == 0 || id > num_physical; }), components.end()); @@ -4497,10 +4499,10 @@ static ExtrusionPaths top_surface_image_split_path(const ExtrusionPath &path, continue; const Point qm = lerp(p0, p1, 0.5 * (t0 + t1)); const std::vector weights = - sample_weight_field_components(context.weight_field, - unscale(qm.x()), - unscale(qm.y()), - context.high_resolution_texture_sampling); + texture_mapping_offset_component_weights_at_point(context, + unscale(qm.x()), + unscale(qm.y()), + std::numeric_limits::quiet_NaN()); const float coverage = context.active_component_idx < weights.size() ? std::clamp(weights[context.active_component_idx], 0.f, 1.f) : 0.f; @@ -4582,10 +4584,10 @@ static std::vector top_surface_image_same_layer_fractions(const std::opti if (!context) return fractions; std::vector weights = - sample_weight_field_components(context->weight_field, - x_mm, - y_mm, - context->high_resolution_texture_sampling); + texture_mapping_offset_component_weights_at_point(*context, + x_mm, + y_mm, + std::numeric_limits::quiet_NaN()); if (weights.size() != size_t(component_count)) return fractions; fractions.assign(size_t(component_count), 0.f); @@ -6298,9 +6300,11 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, std::vector filament_colours = print_config.filament_colour.values; filament_colours.resize(print_config.filament_colour.values.size(), "#FFFFFF"); std::vector components = - TextureMappingManager::effective_texture_component_ids(*zone, - print_config.filament_colour.values.size(), - filament_colours); + zone->is_image_texture() ? + TextureMappingManager::effective_texture_component_ids(*zone, + print_config.filament_colour.values.size(), + filament_colours) : + TextureMappingManager::selected_component_ids(*zone, print_config.filament_colour.values.size()); const std::optional> background = top_surface_image_equal_blend_background(print_config, components); top_surface_image_context = diff --git a/src/libslic3r/TextureMapping.hpp b/src/libslic3r/TextureMapping.hpp index 97d022df5e7..0fb0a053955 100644 --- a/src/libslic3r/TextureMapping.hpp +++ b/src/libslic3r/TextureMapping.hpp @@ -395,7 +395,7 @@ struct TextureMappingZone bool top_surface_image_printing_active() const { return top_surface_image_printing_enabled && - is_image_texture() && + (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) && @@ -404,7 +404,7 @@ struct TextureMappingZone bool top_surface_contoning_active() const { return top_surface_image_printing_enabled && - is_image_texture() && + (is_image_texture() || is_surface_gradient()) && top_surface_image_printing_method == int(TopSurfaceImageContoning) && uses_perimeter_path_modulation_v2(); } @@ -416,7 +416,8 @@ struct TextureMappingZone } bool top_surface_contoning_perimeters_active() const { - return top_surface_contoning_active() && + return is_image_texture() && + top_surface_contoning_active() && !effective_top_surface_contoning_only_color_surface_infill() && !effective_top_surface_contoning_replace_top_perimeters_with_infill() && !effective_top_surface_contoning_recolor_surrounding_perimeters(); diff --git a/src/libslic3r/TextureMappingOffset.cpp b/src/libslic3r/TextureMappingOffset.cpp index 947019694ea..bd075453821 100644 --- a/src/libslic3r/TextureMappingOffset.cpp +++ b/src/libslic3r/TextureMappingOffset.cpp @@ -2823,6 +2823,133 @@ std::optional sample_weight_field_normal_z(const TextureMappingOffsetWeig return std::nullopt; } +static float texture_mapping_offset_context_sample_z_mm(const TextureMappingOffsetContext &context, float z_mm) +{ + if (std::isfinite(z_mm)) + return z_mm; + if (std::isfinite(context.sample_z_mm)) + return context.sample_z_mm; + if (context.layer != nullptr && std::isfinite(float(context.layer->print_z))) + return float(context.layer->print_z); + return 0.f; +} + +static float texture_mapping_offset_linear_gradient_t_at_point(const TextureMappingOffsetContext &context, + float x_mm, + float y_mm, + float z_mm) +{ + const Vec3f sample(x_mm, y_mm, texture_mapping_offset_context_sample_z_mm(context, z_mm)); + if (context.linear_gradient_radial_mode) { + const float radius = std::max(0.01f, context.linear_gradient_radius_mm); + return clamp01f((sample - context.linear_gradient_start_mm).norm() / radius); + } + + const Vec3f gradient_vec = context.linear_gradient_end_mm - context.linear_gradient_start_mm; + const float denom = gradient_vec.squaredNorm(); + return denom > 1e-8f ? clamp01f((sample - context.linear_gradient_start_mm).dot(gradient_vec) / denom) : 0.f; +} + +static std::vector texture_mapping_offset_gradient_component_weights_at_point(const TextureMappingOffsetContext &context, + float x_mm, + float y_mm, + float z_mm, + double direction_x, + double direction_y) +{ + std::vector weights(context.component_ids.size(), 0.f); + if (weights.empty()) + return weights; + + if (context.linear_gradient_mode) { + const float t = texture_mapping_offset_linear_gradient_t_at_point(context, x_mm, y_mm, z_mm); + return TextureMappingManager::linear_gradient_compact_weights(t, context.linear_gradient_stops, context.component_ids); + } + + if (context.object_center_mode || !std::isfinite(direction_x) || !std::isfinite(direction_y)) { + direction_x = double(x_mm) - unscale(context.object_center.x()); + direction_y = double(y_mm) - unscale(context.object_center.y()); + } + const double direction_len = std::hypot(direction_x, direction_y); + if (!std::isfinite(direction_len) || direction_len <= EPSILON) { + direction_x = 1.0; + direction_y = 0.0; + } else { + direction_x /= direction_len; + direction_y /= direction_len; + } + + const float theta_deg = + normalize_texture_mapping_offset_angle_deg(float(Geometry::rad2deg(std::atan2(direction_y, direction_x)))); + const float sample_theta_deg = context.signed_fade_factor < 0.f ? + normalize_texture_mapping_offset_angle_deg(theta_deg + 180.f) : + theta_deg; + + for (size_t i = 0; i < weights.size(); ++i) { + weights[i] = component_angular_influence(context.component_ids[i], + sample_theta_deg, + context.component_ids, + context.rotated_angles); + } + return weights; +} + +std::vector texture_mapping_offset_component_weights_at_point(const TextureMappingOffsetContext &context, + float x_mm, + float y_mm, + float z_mm) +{ + if (context.vertex_color_match_mode) + return sample_weight_field_components(context.weight_field, + x_mm, + y_mm, + context.high_resolution_texture_sampling); + + std::vector weights = + texture_mapping_offset_gradient_component_weights_at_point(context, + x_mm, + y_mm, + z_mm, + std::numeric_limits::quiet_NaN(), + std::numeric_limits::quiet_NaN()); + if (!context.linear_gradient_mode && context.fade_factor < 1.f - EPSILON) + for (float &weight : weights) + weight = clamp01f(1.f + (weight - 1.f) * context.fade_factor); + return weights; +} + +std::optional> texture_mapping_offset_target_rgb_at_point(const TextureMappingOffsetContext &context, + float x_mm, + float y_mm, + float z_mm) +{ + if (context.vertex_color_match_mode) + return sample_weight_field_rgb(context.weight_field, + x_mm, + y_mm, + context.high_resolution_texture_sampling); + + if (context.component_colors.empty() || context.component_colors.size() != context.component_ids.size()) + return std::nullopt; + + std::vector weights = texture_mapping_offset_component_weights_at_point(context, x_mm, y_mm, z_mm); + if (weights.empty()) + return std::nullopt; + bool has_weight = false; + for (const float weight : weights) { + if (std::isfinite(weight) && weight > EPSILON) { + has_weight = true; + break; + } + } + if (!has_weight) + weights.assign(context.component_colors.size(), 1.f); + + const std::array rgb = + mix_color_solver_components(context.component_colors, weights, ColorSolverMixModel::PigmentPainter); + return std::array{ clamp01f(rgb[0]), clamp01f(rgb[1]), clamp01f(rgb[2]) }; +} + std::vector decode_texture_mapping_offset_component_ids(const TextureMappingZone &zone, size_t num_physical) { if (zone.is_linear_gradient()) @@ -3203,6 +3330,7 @@ std::optional build_texture_mapping_offset_context_ context.active_component_id = active_component_id; context.active_component_idx = active_component_idx; context.component_ids = std::move(component_ids); + context.component_colors = std::move(component_colors); if (linear_gradient_mode) context.linear_gradient_stops = TextureMappingManager::normalized_linear_gradient_stops(zone, num_physical); context.component_distances_mm = std::move(distances_mm); @@ -3226,6 +3354,7 @@ std::optional build_texture_mapping_offset_context_ transmission_distance_width_factor(td_calibration_context, active_component_idx, previous_component_idx); context.base_outer_width_mm = base_outer_width_mm; context.layer_height_mm = layer_height_mm; + context.sample_z_mm = sample_z_mm; context.layer = &layer; return context; } @@ -3295,61 +3424,31 @@ float texture_mapping_offset_surface_inset_mm(const TextureMappingOffsetContext } inset_strength = std::clamp(1.f - desired_strength, 0.f, 1.f); } else if (context.linear_gradient_mode) { - float t = 0.f; - float z_mm = context.layer != nullptr ? float(context.layer->print_z) : 0.f; - if (std::isfinite(surface_v_mm)) - z_mm = surface_v_mm; - if (!std::isfinite(z_mm)) - z_mm = 0.f; - const Vec3f sample(unscale(point.x()), unscale(point.y()), z_mm); - if (context.linear_gradient_radial_mode) { - const float radius = std::max(0.01f, context.linear_gradient_radius_mm); - t = clamp01f((sample - context.linear_gradient_start_mm).norm() / radius); - } else { - const Vec3f gradient_vec = context.linear_gradient_end_mm - context.linear_gradient_start_mm; - const float denom = gradient_vec.squaredNorm(); - if (denom > 1e-8f) - t = clamp01f((sample - context.linear_gradient_start_mm).dot(gradient_vec) / denom); - } - if (context.linear_gradient_radial_mode || (context.linear_gradient_end_mm - context.linear_gradient_start_mm).squaredNorm() > 1e-8f) { - const std::vector weights = - TextureMappingManager::linear_gradient_compact_weights(t, context.linear_gradient_stops, context.component_ids); - const float desired_strength = context.active_component_idx < weights.size() ? - clamp01f(weights[context.active_component_idx]) : - 0.f; - inset_strength = std::clamp(1.f - desired_strength, 0.f, 1.f); - } else { - inset_strength = 0.f; - } + const std::vector weights = + texture_mapping_offset_gradient_component_weights_at_point(context, + unscale(point.x()), + unscale(point.y()), + surface_v_mm, + outward_x, + outward_y); + const float desired_strength = context.active_component_idx < weights.size() ? + clamp01f(weights[context.active_component_idx]) : + 0.f; + inset_strength = std::clamp(1.f - desired_strength, 0.f, 1.f); } else { - double theta_direction_x = outward_x; - double theta_direction_y = outward_y; - if (context.object_center_mode) { - const double radial_x = double(point.x()) - double(context.object_center.x()); - const double radial_y = double(point.y()) - double(context.object_center.y()); - const double radial_len = std::hypot(radial_x, radial_y); - if (radial_len > EPSILON) { - theta_direction_x = radial_x / radial_len; - theta_direction_y = radial_y / radial_len; - } - } - - const float theta_deg = - normalize_texture_mapping_offset_angle_deg(float(Geometry::rad2deg(std::atan2(theta_direction_y, theta_direction_x)))); - const float sample_theta_deg = context.signed_fade_factor < 0.f ? - normalize_texture_mapping_offset_angle_deg(theta_deg + 180.f) : - theta_deg; + const std::vector weights = + texture_mapping_offset_gradient_component_weights_at_point(context, + unscale(point.x()), + unscale(point.y()), + surface_v_mm, + outward_x, + outward_y); float raw_inset_mm = 0.f; - const size_t component_count = std::min(context.component_ids.size(), context.component_distances_mm.size()); + const size_t component_count = std::min(weights.size(), context.component_distances_mm.size()); for (size_t i = 0; i < component_count; ++i) { if (i == context.active_component_idx) continue; - const float influence = - component_angular_influence(context.component_ids[i], - sample_theta_deg, - context.component_ids, - context.rotated_angles); - raw_inset_mm += context.component_distances_mm[i] * influence; + raw_inset_mm += context.component_distances_mm[i] * weights[i]; } inset_strength = std::clamp(raw_inset_mm / std::max(context.inset_strength_reference_mm, float(EPSILON)), 0.f, 1.f); } diff --git a/src/libslic3r/TextureMappingOffset.hpp b/src/libslic3r/TextureMappingOffset.hpp index b4fe79ea614..14ceab9d9a4 100644 --- a/src/libslic3r/TextureMappingOffset.hpp +++ b/src/libslic3r/TextureMappingOffset.hpp @@ -72,6 +72,7 @@ struct TextureMappingOffsetContext { unsigned int active_component_id { 0 }; size_t active_component_idx { size_t(-1) }; std::vector component_ids; + std::vector> component_colors; std::vector component_distances_mm; std::vector rotated_angles; TextureMappingOffsetWeightField weight_field; @@ -87,6 +88,7 @@ struct TextureMappingOffsetContext { float active_component_td_width_factor { 1.f }; float base_outer_width_mm { 0.f }; float layer_height_mm { 0.f }; + float sample_z_mm { std::numeric_limits::quiet_NaN() }; const Layer *layer { nullptr }; }; @@ -124,6 +126,16 @@ std::optional sample_weight_field_normal_z(const TextureMappingOffsetWeig float y_mm, bool high_resolution_texture_sampling); +std::vector texture_mapping_offset_component_weights_at_point(const TextureMappingOffsetContext &context, + float x_mm, + float y_mm, + float z_mm); + +std::optional> texture_mapping_offset_target_rgb_at_point(const TextureMappingOffsetContext &context, + float x_mm, + float y_mm, + float z_mm); + float texture_mapping_offset_surface_inset_mm(const TextureMappingOffsetContext &context, const Point &point, double inward_x,