diff --git a/src/libslic3r/Layer.cpp b/src/libslic3r/Layer.cpp index 16a9efe8120..ac17681c90c 100644 --- a/src/libslic3r/Layer.cpp +++ b/src/libslic3r/Layer.cpp @@ -75,14 +75,47 @@ static inline bool layer_needs_raw_backup(const Layer *layer) void Layer::backup_untyped_slices() { if (layer_needs_raw_backup(this)) { - for (LayerRegion *layerm : m_regions) + for (LayerRegion *layerm : m_regions) { layerm->raw_slices = to_expolygons(layerm->slices.surfaces); + layerm->unmodulated_raw_slices = layerm->raw_slices; + layerm->perimeter_path_modulation_v2_applied = false; + layerm->perimeter_path_modulation_v2_fallback_slices.clear(); + layerm->perimeter_path_modulation_v2_has_fallback_slices = false; + layerm->perimeter_path_modulation_v2_fallback_is_modulated = false; + } } else { assert(m_regions.size() == 1); m_regions.front()->raw_slices.clear(); + m_regions.front()->unmodulated_raw_slices.clear(); + m_regions.front()->perimeter_path_modulation_v2_applied = false; + m_regions.front()->perimeter_path_modulation_v2_fallback_slices.clear(); + m_regions.front()->perimeter_path_modulation_v2_has_fallback_slices = false; + m_regions.front()->perimeter_path_modulation_v2_fallback_is_modulated = false; } } +void Layer::commit_perimeter_path_modulation_v2_fallbacks() +{ + bool changed = false; + for (LayerRegion *layerm : m_regions) { + if (layerm == nullptr || !layerm->perimeter_path_modulation_v2_has_fallback_slices) + continue; + layerm->slices = std::move(layerm->perimeter_path_modulation_v2_fallback_slices); + layerm->raw_slices = to_expolygons(layerm->slices.surfaces); + layerm->perimeter_path_modulation_v2_applied = layerm->perimeter_path_modulation_v2_fallback_is_modulated; + layerm->perimeter_path_modulation_v2_has_fallback_slices = false; + layerm->perimeter_path_modulation_v2_fallback_is_modulated = false; + changed = true; + } + if (!changed) + return; + this->make_slices(); + this->lslices_bboxes.clear(); + this->lslices_bboxes.reserve(this->lslices.size()); + for (const ExPolygon &expoly : this->lslices) + this->lslices_bboxes.emplace_back(get_extents(expoly)); +} + void Layer::restore_untyped_slices() { if (layer_needs_raw_backup(this)) { @@ -182,8 +215,14 @@ void Layer::make_perimeters() // keep track of regions whose perimeters we have already generated std::vector done(m_regions.size(), false); + auto has_perimeter_input = [](const LayerRegion *layerm) { + return layerm != nullptr && + (!layerm->slices.empty() || + (layerm->perimeter_path_modulation_v2_applied && !layerm->unmodulated_raw_slices.empty())); + }; + for (LayerRegionPtrs::iterator layerm = m_regions.begin(); layerm != m_regions.end(); ++ layerm) - if ((*layerm)->slices.empty()) { + if (!has_perimeter_input(*layerm)) { (*layerm)->perimeters.clear(); (*layerm)->fills.clear(); (*layerm)->thin_fills.clear(); @@ -199,7 +238,7 @@ void Layer::make_perimeters() LayerRegionPtrs layerms; layerms.push_back(*layerm); for (LayerRegionPtrs::const_iterator it = layerm + 1; it != m_regions.end(); ++it) - if (! (*it)->slices.empty()) { + if (has_perimeter_input(*it)) { LayerRegion* other_layerm = *it; const PrintRegion &other_region = other_layerm->region(); if (is_perimeter_compatible(this_region, other_region)) diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index 4e9fdad059c..0445cbc6fe8 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -42,6 +42,11 @@ public: // Only backed up for multi-region layers or layers with elephant foot compensation. //FIXME Review whether not to simplify the code by keeping the raw_slices all the time. ExPolygons raw_slices; + ExPolygons unmodulated_raw_slices; + bool perimeter_path_modulation_v2_applied = false; + SurfaceCollection perimeter_path_modulation_v2_fallback_slices; + bool perimeter_path_modulation_v2_has_fallback_slices = false; + bool perimeter_path_modulation_v2_fallback_is_modulated = false; // collection of extrusion paths/loops filling gaps // These fills are generated by the perimeter generator. @@ -170,6 +175,8 @@ public: void restore_untyped_slices(); // To improve robustness of detect_surfaces_type() when reslicing (working with typed slices), see GH issue #7442. void restore_untyped_slices_no_extra_perimeters(); + void apply_perimeter_path_modulation_v2(); + void commit_perimeter_path_modulation_v2_fallbacks(); // Slices merged into islands, to be used by the elephant foot compensation to trim the individual surfaces with the shrunk merged slices. ExPolygons merged(float offset) const; template bool any_internal_region_slice_contains(const T &item) const { diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp index 97fba16e333..65b298bcb3b 100644 --- a/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -411,21 +411,33 @@ static std::optional perimeter_texture_make_reco float base_outer_width_mm) { const Layer *layer = layer_region.layer(); - if (layer == nullptr || layer->object() == nullptr || layer->object()->print() == nullptr) + if (layer == nullptr) + return std::nullopt; + + const PrintObject *print_object = layer->object(); + if (print_object == nullptr) + return std::nullopt; + + const Print *print = print_object->print(); + if (print == nullptr || print->canceled()) + return std::nullopt; + + const PrintConfig &print_config = print->config(); + const size_t num_physical = print_config.filament_colour.values.size(); + if (num_physical == 0) return std::nullopt; std::optional context = - build_texture_mapping_offset_context_for_layer(*layer->object(), *layer, zone, texture_zone_id, 0, base_outer_width_mm); - if (!context) + build_texture_mapping_offset_context_for_layer(*print_object, *layer, zone, texture_zone_id, 0, base_outer_width_mm); + if (!context || print->canceled() || context->component_ids.empty()) return std::nullopt; PerimeterTextureRecolorSampler sampler; sampler.image_texture = zone.is_image_texture(); - sampler.num_physical = layer->object()->print()->config().filament_colour.values.size(); + sampler.num_physical = num_physical; if (sampler.image_texture) { sampler.image_context = std::move(*context); sampler.image_component_colors.reserve(sampler.image_context->component_ids.size()); - const PrintConfig &print_config = layer->object()->print()->config(); for (const unsigned int id : sampler.image_context->component_ids) { ColorRGB decoded; if (id >= 1 && id <= print_config.filament_colour.values.size() && @@ -441,15 +453,19 @@ static std::optional perimeter_texture_make_reco sampler.component_ids.reserve(context->component_ids.size()); sampler.gradient_contexts.reserve(context->component_ids.size()); for (const unsigned int component_id : context->component_ids) { + if (print->canceled()) + return std::nullopt; if (component_id < 1 || component_id > sampler.num_physical) continue; std::optional component_context = - build_texture_mapping_offset_context_for_layer(*layer->object(), *layer, zone, texture_zone_id, component_id, base_outer_width_mm); + build_texture_mapping_offset_context_for_layer(*print_object, *layer, zone, texture_zone_id, component_id, base_outer_width_mm); if (!component_context) continue; sampler.component_ids.emplace_back(component_id); sampler.gradient_contexts.emplace_back(std::move(*component_context)); } + if (sampler.component_ids.empty() || sampler.component_ids.size() != sampler.gradient_contexts.size()) + return std::nullopt; return sampler; } @@ -971,6 +987,52 @@ static ExPolygons perimeter_texture_recolor_masks_union(const std::vector &top_visible_recolor_masks, + ExPolygons &top_visible_recolor_protection_mask, + PerimeterTextureTopVisibleRecolorThresholds &top_visible_recolor_thresholds) +{ + top_visible_recolor_masks.clear(); + top_visible_recolor_protection_mask.clear(); + top_visible_recolor_thresholds = + perimeter_texture_top_visible_recolor_thresholds(zone.top_visible_perimeter_recolor_aggressiveness); + const PrintRegionConfig ®ion_config = layer_region.region().config(); + const int wall_loops = std::max(1, region_config.wall_loops.value); + const Flow perimeter_flow = layer_region.flow(frPerimeter); + const float wall_depth_mm = + 0.5f * texture_external_width_mm + + float(std::max(0, wall_loops - 1)) * float(perimeter_flow.spacing()) + + 0.5f * float(perimeter_flow.width()) + 0.05f; + ExPolygons top_visible_recolor_wall_band; + ExPolygons top_visible_recolor_mask = + perimeter_texture_top_visible_wall_band_mask(layer_region, slices, wall_depth_mm, &top_visible_recolor_wall_band); + if (top_visible_recolor_mask.empty()) + return; + + top_visible_recolor_masks = + perimeter_texture_top_visible_region_recolor_masks(layer_region, + slices, + top_visible_recolor_wall_band, + top_visible_recolor_mask, + zone, + texture_zone_id, + texture_external_width_mm, + top_visible_recolor_thresholds); + if (!perimeter_texture_recolor_masks_have_color(top_visible_recolor_masks)) { + top_visible_recolor_masks.clear(); + return; + } + + ExPolygons recolor_union = perimeter_texture_recolor_masks_union(top_visible_recolor_masks); + top_visible_recolor_protection_mask = intersection_ex(recolor_union, top_visible_recolor_mask); + if (top_visible_recolor_protection_mask.empty()) + top_visible_recolor_masks.clear(); +} + static bool perimeter_texture_split_surfaces_by_recolor_masks(const SurfaceCollection &input, const SurfaceCollection &colored_input, const std::vector &masks, @@ -1365,6 +1427,106 @@ static const TextureMappingZone *perimeter_path_modulation_zone_for_region(const return zone; } +static const TextureMappingZone *perimeter_path_modulation_v2_zone_for_region(const Print &print, + const PrintRegionConfig ®ion_config, + unsigned int &texture_zone_id) +{ + const TextureMappingZone *zone = perimeter_path_modulation_zone_for_region(print, region_config, texture_zone_id); + return zone != nullptr && zone->uses_perimeter_path_modulation_v2() ? zone : nullptr; +} + +void Layer::apply_perimeter_path_modulation_v2() +{ + PrintObject *print_object = this->object(); + if (print_object == nullptr || print_object->print() == nullptr) + return; + + bool needs_geometry_update = false; + for (LayerRegion *layerm : m_regions) { + if (layerm == nullptr) + continue; + unsigned int texture_zone_id = 0; + if (layerm->perimeter_path_modulation_v2_applied || + perimeter_path_modulation_v2_zone_for_region(*print_object->print(), + layerm->region().config(), + texture_zone_id) != nullptr) { + needs_geometry_update = true; + break; + } + } + if (!needs_geometry_update) + return; + + for (LayerRegion *layerm : m_regions) { + if (layerm == nullptr) + continue; + if (layerm->unmodulated_raw_slices.empty() && !layerm->raw_slices.empty()) + layerm->unmodulated_raw_slices = layerm->raw_slices; + if (layerm->unmodulated_raw_slices.empty() && !layerm->slices.empty()) + layerm->unmodulated_raw_slices = to_expolygons(layerm->slices.surfaces); + if (!layerm->unmodulated_raw_slices.empty() || !layerm->slices.empty()) { + layerm->slices.set(layerm->unmodulated_raw_slices, stInternal); + layerm->raw_slices = layerm->unmodulated_raw_slices; + } else { + layerm->raw_slices.clear(); + } + layerm->perimeter_path_modulation_v2_applied = false; + layerm->perimeter_path_modulation_v2_fallback_slices.clear(); + layerm->perimeter_path_modulation_v2_has_fallback_slices = false; + layerm->perimeter_path_modulation_v2_fallback_is_modulated = false; + } + + for (LayerRegion *layerm : m_regions) { + if (layerm == nullptr || layerm->slices.empty()) + continue; + unsigned int texture_zone_id = 0; + const TextureMappingZone *zone = + perimeter_path_modulation_v2_zone_for_region(*print_object->print(), + layerm->region().config(), + texture_zone_id); + if (zone == nullptr) + continue; + ExPolygons top_visible_recolor_protection_mask; + std::vector top_visible_recolor_masks; + PerimeterTextureTopVisibleRecolorThresholds top_visible_recolor_thresholds; + const ExPolygons *top_visible_recolor_mask_ptr = nullptr; + const PerimeterTextureTopVisibleRecolorThresholds *top_visible_recolor_thresholds_ptr = nullptr; + if (zone->recolor_top_visible_perimeter_sections) { + const float texture_external_width_mm = + std::max(0.05f, float(print_object->print()->config().texture_mapping_outer_wall_gradient_max_line_width.value)); + perimeter_texture_top_visible_recolor_data(*layerm, + layerm->slices, + *zone, + texture_zone_id, + texture_external_width_mm, + top_visible_recolor_masks, + top_visible_recolor_protection_mask, + top_visible_recolor_thresholds); + if (!top_visible_recolor_protection_mask.empty()) { + top_visible_recolor_mask_ptr = &top_visible_recolor_protection_mask; + top_visible_recolor_thresholds_ptr = &top_visible_recolor_thresholds; + } + } + SurfaceCollection modulated_slices = + perimeter_path_modulated_surfaces(*layerm, + layerm->slices, + *zone, + texture_zone_id, + std::nullopt, + top_visible_recolor_mask_ptr, + top_visible_recolor_thresholds_ptr); + layerm->slices = std::move(modulated_slices); + layerm->raw_slices = to_expolygons(layerm->slices.surfaces); + layerm->perimeter_path_modulation_v2_applied = true; + } + + this->make_slices(); + this->lslices_bboxes.clear(); + this->lslices_bboxes.reserve(this->lslices.size()); + for (const ExPolygon &expoly : this->lslices) + this->lslices_bboxes.emplace_back(get_extents(expoly)); +} + Flow LayerRegion::flow(FlowRole role) const { return this->flow(role, m_layer->height); @@ -1435,8 +1597,6 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, const LayerRe SurfaceCollection modulated_slices; const SurfaceCollection *perimeter_slices = &slices; - ExPolygons top_visible_recolor_mask; - ExPolygons top_visible_recolor_wall_band; ExPolygons top_visible_recolor_protection_mask; std::vector top_visible_recolor_masks; const ExPolygons *top_visible_recolor_mask_ptr = nullptr; @@ -1444,55 +1604,57 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, const LayerRe const PerimeterTextureTopVisibleRecolorThresholds *top_visible_recolor_thresholds_ptr = nullptr; unsigned int perimeter_texture_zone_id = 0; bool use_perimeter_path_modulation = false; + bool use_legacy_perimeter_path_modulation = false; + bool use_perimeter_path_modulation_v2 = false; const TextureMappingZone *perimeter_path_zone = perimeter_path_modulation_zone_for_region(*this->layer()->object()->print(), region_config, perimeter_texture_zone_id); if (perimeter_path_zone != nullptr) { if (perimeter_path_zone->recolor_top_visible_perimeter_sections) { - top_visible_recolor_thresholds = - perimeter_texture_top_visible_recolor_thresholds(perimeter_path_zone->top_visible_perimeter_recolor_aggressiveness); - const int wall_loops = std::max(1, region_config.wall_loops.value); - const Flow perimeter_flow = this->flow(frPerimeter); - const float wall_depth_mm = - 0.5f * texture_external_width_mm + - float(std::max(0, wall_loops - 1)) * float(perimeter_flow.spacing()) + - 0.5f * float(perimeter_flow.width()) + 0.05f; - top_visible_recolor_mask = - perimeter_texture_top_visible_wall_band_mask(*this, slices, wall_depth_mm, &top_visible_recolor_wall_band); - if (!top_visible_recolor_mask.empty()) { - top_visible_recolor_masks = - perimeter_texture_top_visible_region_recolor_masks(*this, - slices, - top_visible_recolor_wall_band, - top_visible_recolor_mask, - *perimeter_path_zone, - perimeter_texture_zone_id, - texture_external_width_mm, - top_visible_recolor_thresholds); - if (perimeter_texture_recolor_masks_have_color(top_visible_recolor_masks)) { - ExPolygons recolor_union = perimeter_texture_recolor_masks_union(top_visible_recolor_masks); - top_visible_recolor_protection_mask = intersection_ex(recolor_union, top_visible_recolor_mask); - if (!top_visible_recolor_protection_mask.empty()) { - top_visible_recolor_mask_ptr = &top_visible_recolor_protection_mask; - top_visible_recolor_thresholds_ptr = &top_visible_recolor_thresholds; - } else { - top_visible_recolor_masks.clear(); - } - } else { - top_visible_recolor_masks.clear(); - } + perimeter_texture_top_visible_recolor_data(*this, + slices, + *perimeter_path_zone, + perimeter_texture_zone_id, + texture_external_width_mm, + top_visible_recolor_masks, + top_visible_recolor_protection_mask, + top_visible_recolor_thresholds); + if (!top_visible_recolor_protection_mask.empty()) { + top_visible_recolor_mask_ptr = &top_visible_recolor_protection_mask; + top_visible_recolor_thresholds_ptr = &top_visible_recolor_thresholds; } } - modulated_slices = perimeter_path_modulated_surfaces(*this, - slices, - *perimeter_path_zone, - perimeter_texture_zone_id, - std::nullopt, - top_visible_recolor_mask_ptr, - top_visible_recolor_thresholds_ptr); - perimeter_slices = &modulated_slices; + if (perimeter_path_zone->uses_legacy_perimeter_path_modulation()) { + modulated_slices = perimeter_path_modulated_surfaces(*this, + slices, + *perimeter_path_zone, + perimeter_texture_zone_id, + std::nullopt, + top_visible_recolor_mask_ptr, + top_visible_recolor_thresholds_ptr); + perimeter_slices = &modulated_slices; + use_legacy_perimeter_path_modulation = true; + } else if (perimeter_path_zone->uses_perimeter_path_modulation_v2()) { + use_perimeter_path_modulation_v2 = true; + } use_perimeter_path_modulation = true; } + SurfaceCollection v2_original_slices; + const SurfaceCollection *fallback_original_slices = &slices; + if (use_perimeter_path_modulation_v2 && !this->unmodulated_raw_slices.empty()) { + v2_original_slices.set(this->unmodulated_raw_slices, stInternal); + fallback_original_slices = &v2_original_slices; + } + + auto set_perimeter_path_modulation_v2_fallback_slices = + [this, use_perimeter_path_modulation_v2](const SurfaceCollection &fallback_slices, bool is_modulated) { + if (!use_perimeter_path_modulation_v2) + return; + this->perimeter_path_modulation_v2_fallback_slices = fallback_slices; + this->perimeter_path_modulation_v2_has_fallback_slices = true; + this->perimeter_path_modulation_v2_fallback_is_modulated = is_modulated; + }; + const bool force_classic_wall_generator = region_uses_overhang_texture_mapping(*this->layer()->object()->print(), region_config); auto texture_external_flow = [&](float external_width_mm) { Flow out = this->flow(frExternalPerimeter); @@ -1592,7 +1754,7 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, const LayerRe SurfaceCollection fill_surfaces_before; ExPolygons fill_no_overlap_before; - if (use_perimeter_path_modulation) { + if (use_legacy_perimeter_path_modulation || use_perimeter_path_modulation_v2) { fill_surfaces_before = *fill_surfaces; fill_no_overlap_before = *fill_no_overlap; } @@ -1601,14 +1763,16 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, const LayerRe use_perimeter_path_modulation ? std::optional(texture_external_width_mm) : std::optional(); process_slices_with_top_visible_recolor(perimeter_slices, &slices, initial_texture_external_width_mm); - if (use_perimeter_path_modulation && this->perimeters.entities.empty() && this->thin_fills.entities.empty()) { + if ((use_legacy_perimeter_path_modulation || use_perimeter_path_modulation_v2) && + this->perimeters.entities.empty() && + this->thin_fills.entities.empty()) { this->perimeters.clear(); this->thin_fills.clear(); *fill_surfaces = fill_surfaces_before; *fill_no_overlap = fill_no_overlap_before; fill_surfaces_before = *fill_surfaces; fill_no_overlap_before = *fill_no_overlap; - process_slices(&slices, std::optional(texture_external_width_mm)); + process_slices(fallback_original_slices, std::optional(texture_external_width_mm)); const bool original_texture_has_extrusions = !this->perimeters.entities.empty() || !this->thin_fills.entities.empty(); const std::optional reduced_external_width_mm = perimeter_texture_min_external_width(this->perimeters); @@ -1619,8 +1783,10 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, const LayerRe if (perimeter_texture_apply_recolor_small_perimeter_loops(*this, *perimeter_path_zone, perimeter_texture_zone_id, - texture_external_width_mm)) + texture_external_width_mm)) { + set_perimeter_path_modulation_v2_fallback_slices(*fallback_original_slices, false); return; + } } if (has_reduced_external_width) { this->perimeters.clear(); @@ -1629,14 +1795,18 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, const LayerRe *fill_no_overlap = fill_no_overlap_before; SurfaceCollection reduced_modulated_slices = perimeter_path_modulated_surfaces(*this, - slices, + *fallback_original_slices, *perimeter_path_zone, perimeter_texture_zone_id, reduced_external_width_mm, top_visible_recolor_mask_ptr, top_visible_recolor_thresholds_ptr); - process_slices_with_top_visible_recolor(&reduced_modulated_slices, &slices, reduced_external_width_mm); + process_slices_with_top_visible_recolor(&reduced_modulated_slices, fallback_original_slices, reduced_external_width_mm); + if (!this->perimeters.entities.empty() || !this->thin_fills.entities.empty()) + set_perimeter_path_modulation_v2_fallback_slices(reduced_modulated_slices, true); } + if (!has_reduced_external_width && (!this->perimeters.entities.empty() || !this->thin_fills.entities.empty())) + set_perimeter_path_modulation_v2_fallback_slices(*fallback_original_slices, false); if (this->perimeters.entities.empty() && this->thin_fills.entities.empty()) { this->perimeters.clear(); this->thin_fills.clear(); @@ -1644,7 +1814,8 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, const LayerRe *fill_no_overlap = fill_no_overlap_before; const std::optional fallback_texture_external_width_mm = original_texture_has_extrusions ? std::optional(texture_external_width_mm) : std::optional(); - process_slices(&slices, fallback_texture_external_width_mm); + process_slices(fallback_original_slices, fallback_texture_external_width_mm); + set_perimeter_path_modulation_v2_fallback_slices(*fallback_original_slices, false); } } } diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index fb049fec05d..e5e331af7f2 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -514,6 +514,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n || opt_key == "texture_mapping_definitions" || opt_key == "texture_mapping_global_settings") { osteps.emplace_back(posPerimeters); + osteps.emplace_back(posSupportMaterial); steps.emplace_back(psWipeTower); steps.emplace_back(psSkirtBrim); } else if ( @@ -4116,6 +4117,8 @@ const std::string PrintStatistics::TotalFilamentUsedWipeTowerValueMask = "; tota #define JSON_LAYER_REGION_CONFIG_HASH "config_hash" #define JSON_LAYER_REGION_SLICES "slices" #define JSON_LAYER_REGION_RAW_SLICES "raw_slices" +#define JSON_LAYER_REGION_UNMODULATED_RAW_SLICES "unmodulated_raw_slices" +#define JSON_LAYER_REGION_PERIMETER_PATH_MODULATION_V2_APPLIED "perimeter_path_modulation_v2_applied" //#define JSON_LAYER_REGION_ENTITIES "entities" #define JSON_LAYER_REGION_THIN_FILLS "thin_fills" #define JSON_LAYER_REGION_FILL_EXPOLYGONS "fill_expolygons" @@ -4326,7 +4329,7 @@ static bool convert_extrusion_to_json(json& entity_json, json& entity_paths_json } static void to_json(json& j, const LayerRegion& layer_region) { - json unsupported_bridge_edges_json = json::array(), slices_surfaces_json = json::array(), raw_slices_json = json::array(), thin_fills_json, thin_fill_entities_json = json::array(); + json unsupported_bridge_edges_json = json::array(), slices_surfaces_json = json::array(), raw_slices_json = json::array(), unmodulated_raw_slices_json = json::array(), thin_fills_json, thin_fill_entities_json = json::array(); json fill_expolygons_json = json::array(), fill_no_overlap_expolygons_json = json::array(), fill_surfaces_json = json::array(), perimeters_json, perimeter_entities_json = json::array(), fills_json, fill_entities_json = json::array(); j[JSON_LAYER_REGION_CONFIG_HASH] = layer_region.region().config_hash(); @@ -4344,6 +4347,13 @@ static void to_json(json& j, const LayerRegion& layer_region) { raw_slices_json.push_back(std::move(raw_polygon_json)); } j.push_back({JSON_LAYER_REGION_RAW_SLICES, std::move(raw_slices_json)}); + for (const ExPolygon& raw_slice_explogyon : layer_region.unmodulated_raw_slices) { + json raw_polygon_json = raw_slice_explogyon; + + unmodulated_raw_slices_json.push_back(std::move(raw_polygon_json)); + } + j.push_back({JSON_LAYER_REGION_UNMODULATED_RAW_SLICES, std::move(unmodulated_raw_slices_json)}); + j.push_back({JSON_LAYER_REGION_PERIMETER_PATH_MODULATION_V2_APPLIED, layer_region.perimeter_path_modulation_v2_applied}); //thin fills thin_fills_json[JSON_EXTRUSION_NO_SORT] = layer_region.thin_fills.no_sort; @@ -4631,6 +4641,20 @@ static void convert_layer_region_from_json(const json& j, LayerRegion& layer_reg polygon = j[JSON_LAYER_REGION_RAW_SLICES][raw_slices_index]; layer_region.raw_slices.push_back(std::move(polygon)); } + if (j.contains(JSON_LAYER_REGION_UNMODULATED_RAW_SLICES)) { + int unmodulated_raw_slices_count = j[JSON_LAYER_REGION_UNMODULATED_RAW_SLICES].size(); + for (int raw_slices_index = 0; raw_slices_index < unmodulated_raw_slices_count; raw_slices_index++) + { + ExPolygon polygon; + + polygon = j[JSON_LAYER_REGION_UNMODULATED_RAW_SLICES][raw_slices_index]; + layer_region.unmodulated_raw_slices.push_back(std::move(polygon)); + } + } else { + layer_region.unmodulated_raw_slices = layer_region.raw_slices; + } + layer_region.perimeter_path_modulation_v2_applied = + j.value(JSON_LAYER_REGION_PERIMETER_PATH_MODULATION_V2_APPLIED, false); //thin fills layer_region.thin_fills.no_sort = j[JSON_LAYER_REGION_THIN_FILLS][JSON_EXTRUSION_NO_SORT]; diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 56a12f04f66..c6190162ef9 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -509,6 +509,11 @@ void PrintObject::make_perimeters() m_typed_slices = false; } + for (auto layer_it = m_layers.rbegin(); layer_it != m_layers.rend(); ++layer_it) { + m_print->throw_if_canceled(); + (*layer_it)->apply_perimeter_path_modulation_v2(); + } + // compare each layer to the one below, and mark those slices needing // one additional inner perimeter, like the top of domed objects- @@ -593,6 +598,11 @@ void PrintObject::make_perimeters() m_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Generating perimeters in parallel - end"; + for (Layer *layer : m_layers) { + m_print->throw_if_canceled(); + layer->commit_perimeter_path_modulation_v2_fallbacks(); + } + this->set_done(posPerimeters); } @@ -832,45 +842,169 @@ void PrintObject::generate_support_material() if (this->set_started(posSupportMaterial)) { this->clear_support_layers(); - if(!has_support() && !m_print->get_no_check_flag()) { - // BBS: pop a warning if objects have significant amount of overhangs but support material is not enabled - // Note: we also need to pop warning if support is disabled and only raft is enabled - m_print->set_status(50, L("Checking support necessity")); - typedef std::chrono::high_resolution_clock clock_; - typedef std::chrono::duration > second_; - std::chrono::time_point t0{ clock_::now() }; + struct LayerSupportGeometryBackup { + Layer *layer { nullptr }; + ExPolygons lslices; + std::vector lslices_bboxes; + }; + struct LayerRegionSupportGeometryBackup { + LayerRegion *layerm { nullptr }; + SurfaceCollection slices; + ExPolygons raw_slices; + SurfaceCollection fill_surfaces; + ExPolygons fill_expolygons; + ExPolygons fill_no_overlap_expolygons; + bool perimeter_path_modulation_v2_applied { false }; + }; - SupportNecessaryType sntype = this->is_support_necessary(); + std::vector layer_backups; + std::vector region_backups; + const bool typed_slices_backup = m_typed_slices; + bool support_geometry_swapped = false; - double duration{ std::chrono::duration_cast(clock_::now() - t0).count() }; - BOOST_LOG_TRIVIAL(info) << std::fixed << std::setprecision(0) << "is_support_necessary takes " << duration << " secs."; + auto rebuild_layer_geometry = [](Layer *layer) { + layer->make_slices(); + layer->lslices_bboxes.clear(); + layer->lslices_bboxes.reserve(layer->lslices.size()); + for (const ExPolygon &expoly : layer->lslices) + layer->lslices_bboxes.emplace_back(get_extents(expoly)); + }; - if (sntype != NoNeedSupp) { - std::map reasons = { - {SharpTail,L("floating regions")}, - {Cantilever,L("floating cantilever")}, - {LargeOverhang,L("large overhangs")} }; - std::string warning_message = Slic3r::format(L("It seems object %s has %s. Please re-orient the object or enable support generation."), - this->model_object()->name, reasons[sntype]); - this->active_step_add_warning(PrintStateBase::WarningLevel::NON_CRITICAL, warning_message, PrintStateBase::SlicingNeedSupportOn); + auto region_uses_unmodulated_support_geometry = [this](const LayerRegion *layerm) { + if (layerm == nullptr) + return false; + const int filament_id = layerm->region().config().wall_filament.value; + if (filament_id <= 0) + return false; + const TextureMappingZone *zone = this->print()->texture_mapping_manager().zone_from_id(unsigned(filament_id)); + return zone != nullptr && + zone->enabled && + !zone->deleted && + zone->uses_perimeter_path_modulation_v2() && + !zone->use_modulated_overhang_geometry_for_support && + (zone->is_2d_gradient() || zone->is_image_texture()); + }; + + auto restore_support_geometry = [&]() { + if (!support_geometry_swapped) + return; + for (LayerRegionSupportGeometryBackup &backup : region_backups) { + backup.layerm->slices = std::move(backup.slices); + backup.layerm->raw_slices = std::move(backup.raw_slices); + backup.layerm->fill_surfaces = std::move(backup.fill_surfaces); + backup.layerm->fill_expolygons = std::move(backup.fill_expolygons); + backup.layerm->fill_no_overlap_expolygons = std::move(backup.fill_no_overlap_expolygons); + backup.layerm->perimeter_path_modulation_v2_applied = backup.perimeter_path_modulation_v2_applied; } + for (LayerSupportGeometryBackup &backup : layer_backups) { + backup.layer->lslices = std::move(backup.lslices); + backup.layer->lslices_bboxes = std::move(backup.lslices_bboxes); + } + m_typed_slices = typed_slices_backup; + support_geometry_swapped = false; + }; + + auto use_unmodulated_support_geometry = [&]() { + bool needed = false; + for (Layer *layer : m_layers) { + for (LayerRegion *layerm : layer->m_regions) { + if (region_uses_unmodulated_support_geometry(layerm)) { + needed = true; + break; + } + } + if (needed) + break; + } + if (!needed) + return; + + layer_backups.reserve(m_layers.size()); + for (Layer *layer : m_layers) { + layer_backups.push_back({ layer, layer->lslices, layer->lslices_bboxes }); + for (LayerRegion *layerm : layer->m_regions) { + region_backups.push_back({ + layerm, + layerm->slices, + layerm->raw_slices, + layerm->fill_surfaces, + layerm->fill_expolygons, + layerm->fill_no_overlap_expolygons, + layerm->perimeter_path_modulation_v2_applied + }); + } + } + support_geometry_swapped = true; + + for (Layer *layer : m_layers) { + bool layer_changed = false; + for (LayerRegion *layerm : layer->m_regions) { + if (!region_uses_unmodulated_support_geometry(layerm)) + continue; + ExPolygons support_slices = + !layerm->unmodulated_raw_slices.empty() ? + layerm->unmodulated_raw_slices : + (!layerm->raw_slices.empty() ? + layerm->raw_slices : + to_expolygons(layerm->slices.surfaces)); + layerm->slices.set(support_slices, stInternal); + layerm->raw_slices = std::move(support_slices); + layerm->perimeter_path_modulation_v2_applied = false; + layer_changed = true; + } + if (layer_changed) + rebuild_layer_geometry(layer); + } + this->detect_surfaces_type(); + }; + + try { + use_unmodulated_support_geometry(); + + if(!has_support() && !m_print->get_no_check_flag()) { + // BBS: pop a warning if objects have significant amount of overhangs but support material is not enabled + // Note: we also need to pop warning if support is disabled and only raft is enabled + m_print->set_status(50, L("Checking support necessity")); + typedef std::chrono::high_resolution_clock clock_; + typedef std::chrono::duration > second_; + std::chrono::time_point t0{ clock_::now() }; + + SupportNecessaryType sntype = this->is_support_necessary(); + + double duration{ std::chrono::duration_cast(clock_::now() - t0).count() }; + BOOST_LOG_TRIVIAL(info) << std::fixed << std::setprecision(0) << "is_support_necessary takes " << duration << " secs."; + + if (sntype != NoNeedSupp) { + std::map reasons = { + {SharpTail,L("floating regions")}, + {Cantilever,L("floating cantilever")}, + {LargeOverhang,L("large overhangs")} }; + std::string warning_message = Slic3r::format(L("It seems object %s has %s. Please re-orient the object or enable support generation."), + this->model_object()->name, reasons[sntype]); + this->active_step_add_warning(PrintStateBase::WarningLevel::NON_CRITICAL, warning_message, PrintStateBase::SlicingNeedSupportOn); + } #if 0 - // Printing without supports. Empty layer means some objects or object parts are levitating, - // therefore they cannot be printed without supports. - for (const Layer *layer : m_layers) - if (layer->empty()) - throw Slic3r::SlicingError("Levitating objects cannot be printed without supports."); + // Printing without supports. Empty layer means some objects or object parts are levitating, + // therefore they cannot be printed without supports. + for (const Layer *layer : m_layers) + if (layer->empty()) + throw Slic3r::SlicingError("Levitating objects cannot be printed without supports."); #endif - } + } - if ((this->has_support() && m_layers.size() > 1) || (this->has_raft() && !m_layers.empty())) { - m_print->set_status(50, L("Generating support")); + if ((this->has_support() && m_layers.size() > 1) || (this->has_raft() && !m_layers.empty())) { + m_print->set_status(50, L("Generating support")); - this->_generate_support_material(); - m_print->throw_if_canceled(); + this->_generate_support_material(); + m_print->throw_if_canceled(); + } + restore_support_geometry(); + this->set_done(posSupportMaterial); + } catch (...) { + restore_support_geometry(); + throw; } - this->set_done(posSupportMaterial); } } diff --git a/src/libslic3r/TextureMapping.cpp b/src/libslic3r/TextureMapping.cpp index ae65afa68c3..1e33484facd 100644 --- a/src/libslic3r/TextureMapping.cpp +++ b/src/libslic3r/TextureMapping.cpp @@ -526,19 +526,25 @@ static int mapping_mode_from_name(const std::string &name) static std::string modulation_mode_name(int mode) { - return clamp_int(mode, - int(TextureMappingZone::ModulationLineWidth), - int(TextureMappingZone::ModulationPerimeterPath)) == - int(TextureMappingZone::ModulationPerimeterPath) ? - std::string("perimeter_path") : - std::string("line_width"); + switch (clamp_int(mode, + int(TextureMappingZone::ModulationLineWidth), + int(TextureMappingZone::ModulationPerimeterPathV2))) { + case int(TextureMappingZone::ModulationPerimeterPath): + return std::string("perimeter_path"); + case int(TextureMappingZone::ModulationPerimeterPathV2): + return std::string("perimeter_path_v2"); + default: + return std::string("line_width"); + } } static int modulation_mode_from_name(const std::string &name) { - return name == "perimeter_path" ? - int(TextureMappingZone::ModulationPerimeterPath) : - int(TextureMappingZone::ModulationLineWidth); + if (name == "perimeter_path") + return int(TextureMappingZone::ModulationPerimeterPath); + if (name == "perimeter_path_v2") + return int(TextureMappingZone::ModulationPerimeterPathV2); + return int(TextureMappingZone::ModulationLineWidth); } static std::string top_visible_recolor_aggressiveness_name(int mode) @@ -859,6 +865,7 @@ bool TextureMappingZone::operator==(const TextureMappingZone &rhs) const seam_hiding == rhs.seam_hiding && 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 && recolor_small_perimeter_loops == rhs.recolor_small_perimeter_loops && recolor_top_visible_perimeter_sections == rhs.recolor_top_visible_perimeter_sections && top_visible_perimeter_recolor_aggressiveness == rhs.top_visible_perimeter_recolor_aggressiveness && @@ -1116,6 +1123,7 @@ std::string TextureMappingManager::serialize_entries() texture["hide_seams"] = zone.seam_hiding; 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["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; texture["top_visible_perimeter_recolor_aggressiveness"] = @@ -1264,6 +1272,9 @@ void TextureMappingManager::load_entries(const std::string &serialized, zone.modulation_mode = modulation_mode_from_name(texture.value("modulation_mode", modulation_mode_name(TextureMappingZone::DefaultModulationMode))); + zone.use_modulated_overhang_geometry_for_support = + texture.value("use_modulated_overhang_geometry_for_support", + TextureMappingZone::DefaultUseModulatedOverhangGeometryForSupport); zone.recolor_small_perimeter_loops = texture.value("recolor_small_perimeter_loops", TextureMappingZone::DefaultRecolorSmallPerimeterLoops); zone.recolor_top_visible_perimeter_sections = diff --git a/src/libslic3r/TextureMapping.hpp b/src/libslic3r/TextureMapping.hpp index 678f3e57013..dfa38f56bfb 100644 --- a/src/libslic3r/TextureMapping.hpp +++ b/src/libslic3r/TextureMapping.hpp @@ -48,7 +48,8 @@ struct TextureMappingZone enum ModulationMode : uint8_t { ModulationLineWidth = 0, - ModulationPerimeterPath = 1 + ModulationPerimeterPath = 1, + ModulationPerimeterPathV2 = 2 }; enum TopVisiblePerimeterRecolorAggressiveness : uint8_t { @@ -114,6 +115,7 @@ struct TextureMappingZone static constexpr bool DefaultSeamHiding = false; static constexpr bool DefaultNonlinearOffsetAdjustment = false; static constexpr int DefaultModulationMode = int(ModulationLineWidth); + static constexpr bool DefaultUseModulatedOverhangGeometryForSupport = false; static constexpr bool DefaultRecolorSmallPerimeterLoops = false; static constexpr bool DefaultRecolorTopVisiblePerimeterSections = false; static constexpr int DefaultTopVisiblePerimeterRecolorAggressiveness = int(TopVisibleRecolorAggressive); @@ -170,6 +172,7 @@ struct TextureMappingZone bool seam_hiding = DefaultSeamHiding; bool nonlinear_offset_adjustment = DefaultNonlinearOffsetAdjustment; int modulation_mode = DefaultModulationMode; + bool use_modulated_overhang_geometry_for_support = DefaultUseModulatedOverhangGeometryForSupport; bool recolor_small_perimeter_loops = DefaultRecolorSmallPerimeterLoops; bool recolor_top_visible_perimeter_sections = DefaultRecolorTopVisiblePerimeterSections; int top_visible_perimeter_recolor_aggressiveness = DefaultTopVisiblePerimeterRecolorAggressiveness; @@ -199,7 +202,13 @@ struct TextureMappingZone bool is_image_texture() const { return surface_pattern == int(ImageTexture); } bool is_2d_gradient() const { return surface_pattern == int(Gradient2D); } - bool uses_perimeter_path_modulation() const { return modulation_mode == int(ModulationPerimeterPath); } + bool uses_perimeter_path_modulation() const + { + return modulation_mode == int(ModulationPerimeterPath) || + modulation_mode == int(ModulationPerimeterPathV2); + } + bool uses_legacy_perimeter_path_modulation() const { return modulation_mode == int(ModulationPerimeterPath); } + bool uses_perimeter_path_modulation_v2() const { return modulation_mode == int(ModulationPerimeterPathV2); } void reset_offset_settings() { @@ -224,6 +233,7 @@ struct TextureMappingZone seam_hiding = DefaultSeamHiding; nonlinear_offset_adjustment = DefaultNonlinearOffsetAdjustment; modulation_mode = DefaultModulationMode; + use_modulated_overhang_geometry_for_support = DefaultUseModulatedOverhangGeometryForSupport; recolor_small_perimeter_loops = DefaultRecolorSmallPerimeterLoops; recolor_top_visible_perimeter_sections = DefaultRecolorTopVisiblePerimeterSections; top_visible_perimeter_recolor_aggressiveness = DefaultTopVisiblePerimeterRecolorAggressiveness; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 827f99c69b5..d4d2148a0c4 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1426,6 +1426,7 @@ public: bool seam_hiding, bool nonlinear_offset_adjustment, int modulation_mode, + bool use_modulated_overhang_geometry_for_support, bool recolor_small_perimeter_loops, bool recolor_top_visible_perimeter_sections, int top_visible_perimeter_recolor_aggressiveness, @@ -1699,11 +1700,12 @@ public: modulation_mode_row->Add(new wxStaticText(print_settings_page, wxID_ANY, _L("Modulation mode:")), 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, gap); wxArrayString modulation_mode_choices; modulation_mode_choices.Add(_L("Line width modulation")); - modulation_mode_choices.Add(_L("Perimeter path modulation")); + modulation_mode_choices.Add(_L("Perimeter path modulation (legacy v1)")); + modulation_mode_choices.Add(_L("Perimeter path modulation (v2)")); m_modulation_mode_choice = new wxChoice(print_settings_page, wxID_ANY, wxDefaultPosition, wxDefaultSize, modulation_mode_choices); m_modulation_mode_choice->SetSelection(std::clamp(modulation_mode, int(TextureMappingZone::ModulationLineWidth), - int(TextureMappingZone::ModulationPerimeterPath))); + int(TextureMappingZone::ModulationPerimeterPathV2))); 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 &) { update_modulation_mode_options_visibility(false); }); @@ -1757,6 +1759,10 @@ public: m_compact_offset_mode_checkbox->SetToolTip( _L("Normalizes sampled filament offsets so the strongest active color uses the full maximum line width.")); experimental_box->Add(m_compact_offset_mode_checkbox, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, gap); + m_use_modulated_overhang_geometry_for_support_checkbox = + 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_dithering_resolution_panel = new wxPanel(experimental_page, wxID_ANY); auto *dithering_resolution_row = new wxBoxSizer(wxHORIZONTAL); m_dithering_resolution_panel->SetSizer(dithering_resolution_row); @@ -2028,12 +2034,17 @@ public: bool reduce_outer_surface_texture() const { return false; } bool seam_hiding() const { return m_seam_hiding_checkbox && m_seam_hiding_checkbox->GetValue(); } bool nonlinear_offset_adjustment() const { return m_nonlinear_offset_adjustment_checkbox && m_nonlinear_offset_adjustment_checkbox->GetValue(); } + bool use_modulated_overhang_geometry_for_support() const + { + return m_use_modulated_overhang_geometry_for_support_checkbox != nullptr && + m_use_modulated_overhang_geometry_for_support_checkbox->GetValue(); + } int modulation_mode() const { return m_modulation_mode_choice ? std::clamp(m_modulation_mode_choice->GetSelection(), int(TextureMappingZone::ModulationLineWidth), - int(TextureMappingZone::ModulationPerimeterPath)) : + int(TextureMappingZone::ModulationPerimeterPathV2)) : TextureMappingZone::DefaultModulationMode; } bool compact_offset_mode() const { return dithering_enabled() || (m_compact_offset_mode_checkbox && m_compact_offset_mode_checkbox->GetValue()); } @@ -2383,7 +2394,9 @@ private: void update_modulation_mode_options_visibility(bool fit_dialog) { - const bool perimeter_path_mode = modulation_mode() == int(TextureMappingZone::ModulationPerimeterPath); + const bool perimeter_path_mode = modulation_mode() == int(TextureMappingZone::ModulationPerimeterPath) || + modulation_mode() == int(TextureMappingZone::ModulationPerimeterPathV2); + const bool perimeter_path_v2_mode = modulation_mode() == int(TextureMappingZone::ModulationPerimeterPathV2); const bool top_visible_checked = m_recolor_top_visible_perimeter_sections_checkbox != nullptr && m_recolor_top_visible_perimeter_sections_checkbox->GetValue(); @@ -2393,6 +2406,8 @@ private: m_recolor_small_perimeter_loops_checkbox->Enable(perimeter_path_mode && !top_visible_checked); if (m_recolor_top_visible_perimeter_sections_checkbox != nullptr) 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); const bool top_visible_enabled = perimeter_path_mode && top_visible_checked; @@ -2457,6 +2472,7 @@ private: wxCheckBox *m_reduce_outer_surface_texture_checkbox {nullptr}; wxCheckBox *m_seam_hiding_checkbox {nullptr}; wxCheckBox *m_nonlinear_offset_adjustment_checkbox {nullptr}; + wxCheckBox *m_use_modulated_overhang_geometry_for_support_checkbox {nullptr}; wxChoice *m_modulation_mode_choice {nullptr}; wxCheckBox *m_recolor_small_perimeter_loops_checkbox {nullptr}; wxCheckBox *m_recolor_top_visible_perimeter_sections_checkbox {nullptr}; @@ -6642,6 +6658,7 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager) updated.seam_hiding, updated.nonlinear_offset_adjustment, updated.modulation_mode, + updated.use_modulated_overhang_geometry_for_support, updated.recolor_small_perimeter_loops, updated.recolor_top_visible_perimeter_sections, updated.top_visible_perimeter_recolor_aggressiveness, @@ -6683,6 +6700,7 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager) updated.seam_hiding = dlg.seam_hiding(); updated.nonlinear_offset_adjustment = dlg.nonlinear_offset_adjustment(); updated.modulation_mode = dlg.modulation_mode(); + updated.use_modulated_overhang_geometry_for_support = dlg.use_modulated_overhang_geometry_for_support(); 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();