From ef4ba55320f2275406c774384b2c693e55427f53 Mon Sep 17 00:00:00 2001 From: sentientstardust Date: Tue, 26 May 2026 23:15:30 +0100 Subject: [PATCH] Allow using different infill types for top surface coloring - Add boundary skin, boundary skin hybrid, and spiral infill types - Combine perimeter area with infill before solving colors (when 'replace top perimeters with infill' is enabled) - Allow UI to update while slicing is being cancelled --- src/libslic3r/Fill/Fill.cpp | 928 ++++++++++++++++-- src/libslic3r/TextureMapping.cpp | 51 + src/libslic3r/TextureMapping.hpp | 21 + src/slic3r/GUI/BackgroundSlicingProcess.cpp | 20 + src/slic3r/GUI/BackgroundSlicingProcess.hpp | 2 + src/slic3r/GUI/NotificationManager.cpp | 12 + src/slic3r/GUI/NotificationManager.hpp | 1 + src/slic3r/GUI/Plater.cpp | 57 +- .../GUI/SlicingProgressNotification.cpp | 9 +- .../GUI/SlicingProgressNotification.hpp | 1 + 10 files changed, 1034 insertions(+), 68 deletions(-) diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index b3b46950ecb..48cef16195e 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -17,9 +17,11 @@ #include "../TextureMapping.hpp" #include "../TextureMappingContoning.hpp" #include "../TextureMappingOffset.hpp" +#include "../VariableWidth.hpp" #include "AABBTreeLines.hpp" #include "ExtrusionEntity.hpp" +#include "Arachne/WallToolPaths.hpp" #include "FillBase.hpp" #include "FillRectilinear.hpp" #include "FillLightning.hpp" @@ -310,6 +312,7 @@ struct SurfaceFillParams bool texture_mapping_top_surface_contoning = false; int texture_mapping_top_surface_component_index = 0; int texture_mapping_top_surface_component_count = 0; + int texture_mapping_top_surface_contoning_flat_surface_infill_mode = TextureMappingZone::SlicerDefaultTopSurfaceContoningFlatSurfaceInfillMode; bool operator<(const SurfaceFillParams &rhs) const { #define RETURN_COMPARE_NON_EQUAL(KEY) if (this->KEY < rhs.KEY) return true; if (this->KEY > rhs.KEY) return false; @@ -354,6 +357,7 @@ struct SurfaceFillParams RETURN_COMPARE_NON_EQUAL(texture_mapping_top_surface_contoning); RETURN_COMPARE_NON_EQUAL(texture_mapping_top_surface_component_index); RETURN_COMPARE_NON_EQUAL(texture_mapping_top_surface_component_count); + RETURN_COMPARE_NON_EQUAL(texture_mapping_top_surface_contoning_flat_surface_infill_mode); return false; } @@ -392,7 +396,8 @@ struct SurfaceFillParams this->texture_mapping_top_surface_same_layer_partition == rhs.texture_mapping_top_surface_same_layer_partition && this->texture_mapping_top_surface_contoning == rhs.texture_mapping_top_surface_contoning && this->texture_mapping_top_surface_component_index == rhs.texture_mapping_top_surface_component_index && - this->texture_mapping_top_surface_component_count == rhs.texture_mapping_top_surface_component_count; + this->texture_mapping_top_surface_component_count == rhs.texture_mapping_top_surface_component_count && + this->texture_mapping_top_surface_contoning_flat_surface_infill_mode == rhs.texture_mapping_top_surface_contoning_flat_surface_infill_mode; } }; @@ -422,7 +427,9 @@ struct TopSurfaceImageStackSlice { }; struct TopSurfaceImageRegionPlan { + const Layer *target_layer = nullptr; const TextureMappingZone *zone = nullptr; + size_t region_id = size_t(-1); unsigned int zone_id = 0; std::vector components_bottom_to_top; std::vector slices; @@ -445,6 +452,7 @@ struct TopSurfaceImageRegionPlan { bool contoning_blue_noise_error_diffusion_enabled = false; bool contoning_supersampled_cells_enabled = false; bool contoning_surface_anchored_stacks_enabled = false; + int contoning_flat_surface_infill_mode = TextureMappingZone::SlicerDefaultTopSurfaceContoningFlatSurfaceInfillMode; }; enum class TopSurfaceImageSourceSurface { @@ -717,6 +725,7 @@ struct TopSurfaceImageContoningDepthRegionPlan { struct TopSurfaceImageContoningSourceContext { TextureMappingOffsetContext offset_context; std::vector stack_areas; + ExPolygons normal_filter_bypass_area; float threshold_deg { 0.f }; int stack_layers { 0 }; int pattern_filaments { 0 }; @@ -732,6 +741,10 @@ static long long top_surface_image_contoning_float_key(double value) struct TopSurfaceImageContoningStackPlanKey { const Layer *source_layer { nullptr }; size_t source_layer_id { 0 }; + const Layer *target_layer { nullptr }; + size_t target_layer_id { 0 }; + size_t region_id { size_t(-1) }; + int target_depth { -1 }; int source_surface { 0 }; long long sample_z { 0 }; unsigned int zone_id { 0 }; @@ -753,6 +766,10 @@ struct TopSurfaceImageContoningStackPlanKey { { return std::tie(source_layer, source_layer_id, + target_layer, + target_layer_id, + region_id, + target_depth, source_surface, sample_z, zone_id, @@ -771,6 +788,10 @@ struct TopSurfaceImageContoningStackPlanKey { blue_noise) < std::tie(rhs.source_layer, rhs.source_layer_id, + rhs.target_layer, + rhs.target_layer_id, + rhs.region_id, + rhs.target_depth, rhs.source_surface, rhs.sample_z, rhs.zone_id, @@ -1445,6 +1466,7 @@ static std::optional top_surface_image_conto const TextureMappingOffsetContext &context, const ExPolygons &area, const std::vector &source_stack_areas, + const ExPolygons &normal_filter_bypass_area, int stack_layers, int pattern_filaments, int depth, @@ -1470,8 +1492,10 @@ static std::optional top_surface_image_conto return; const float sample_x_mm = unscale(sample_point.x()); const float sample_y_mm = unscale(sample_point.y()); - if (!top_surface_image_contoning_sample_eligible(context, sample_x_mm, sample_y_mm, threshold_deg, source_surface)) - return; + if (normal_filter_bypass_area.empty() || !top_surface_image_expolygons_contain_point(normal_filter_bypass_area, sample_point)) { + if (!top_surface_image_contoning_sample_eligible(context, sample_x_mm, sample_y_mm, threshold_deg, source_surface)) + return; + } const std::optional> rgb = sample_weight_field_rgb(context.weight_field, sample_x_mm, @@ -1558,6 +1582,7 @@ static std::optional top_surface_image_co const PrintConfig &print_config, const TextureMappingContoningSolver &solver, TopSurfaceImageSourceSurface source_surface, + const std::vector *stack_area_extensions, const ThrowIfCanceled *throw_if_canceled) { if (!solver.valid()) @@ -1595,6 +1620,19 @@ static std::optional top_surface_image_co top_surface_image_contoning_pattern_filaments(out.stack_layers, plan.contoning_pattern_filaments); out.stack_areas = top_surface_image_contoning_stack_areas(source_layer, plan.zone_id, out.stack_layers, source_surface, throw_if_canceled); + if (stack_area_extensions != nullptr) { + const size_t count = std::min(out.stack_areas.size(), stack_area_extensions->size()); + for (size_t idx = 0; idx < count; ++idx) { + check_canceled(throw_if_canceled); + if ((*stack_area_extensions)[idx].empty()) + continue; + append(out.stack_areas[idx], (*stack_area_extensions)[idx]); + out.stack_areas[idx] = union_ex(out.stack_areas[idx]); + append(out.normal_filter_bypass_area, (*stack_area_extensions)[idx]); + } + if (!out.normal_filter_bypass_area.empty()) + out.normal_filter_bypass_area = union_ex(out.normal_filter_bypass_area); + } return out; } @@ -1627,6 +1665,7 @@ static std::vector top_surface_image_conto print_config, solver, source_surface, + nullptr, throw_if_canceled); if (!local_source_context) return regions; @@ -1677,6 +1716,7 @@ static std::vector top_surface_image_conto return top_surface_image_contoning_sample_cell(source->offset_context, area, source->stack_areas, + source->normal_filter_bypass_area, source->stack_layers, source->pattern_filaments, depth, @@ -1794,6 +1834,7 @@ static std::shared_ptr top_surface_imag const TextureMappingContoningSolver &solver, TopSurfaceImageSourceSurface source_surface, bool use_blue_noise_error_diffusion, + const std::vector *stack_area_extensions, const ThrowIfCanceled *throw_if_canceled) { std::shared_ptr out = std::make_shared(); @@ -1809,6 +1850,7 @@ static std::shared_ptr top_surface_imag print_config, solver, source_surface, + stack_area_extensions, throw_if_canceled); if (!source_context || source_context->stack_layers <= 0 || source_context->pattern_filaments <= 0) return out; @@ -1857,6 +1899,7 @@ static std::shared_ptr top_surface_imag return top_surface_image_contoning_sample_cell(source_context->offset_context, source_area, source_context->stack_areas, + source_context->normal_filter_bypass_area, source_context->stack_layers, source_context->pattern_filaments, -1, @@ -1962,6 +2005,7 @@ static TopSurfaceImageContoningStackPlanKey top_surface_image_contoning_stack_pl const TextureMappingZone &zone, const TextureMappingContoningSolver &solver, TopSurfaceImageSourceSurface source_surface, + int target_depth, bool use_blue_noise_error_diffusion) { const double sample_z = source_surface == TopSurfaceImageSourceSurface::Bottom ? @@ -1970,6 +2014,12 @@ static TopSurfaceImageContoningStackPlanKey top_surface_image_contoning_stack_pl TopSurfaceImageContoningStackPlanKey key; key.source_layer = &source_layer; key.source_layer_id = source_layer.id(); + if (plan.contoning_replace_top_perimeters_with_infill && plan.target_layer != nullptr) { + key.target_layer = plan.target_layer; + key.target_layer_id = plan.target_layer->id(); + key.region_id = plan.region_id; + key.target_depth = target_depth; + } key.source_surface = int(source_surface); key.sample_z = top_surface_image_contoning_float_key(sample_z); key.zone_id = plan.zone_id; @@ -1999,11 +2049,13 @@ static std::shared_ptr top_surface_imag const PrintConfig &print_config, const TextureMappingContoningSolver &solver, TopSurfaceImageSourceSurface source_surface, + int target_depth, bool use_blue_noise_error_diffusion, + const std::vector *stack_area_extensions, const ThrowIfCanceled *throw_if_canceled) { const TopSurfaceImageContoningStackPlanKey key = - top_surface_image_contoning_stack_plan_key(plan, source_layer, zone, solver, source_surface, use_blue_noise_error_diffusion); + top_surface_image_contoning_stack_plan_key(plan, source_layer, zone, solver, source_surface, target_depth, use_blue_noise_error_diffusion); auto builder = [&]() { return top_surface_image_contoning_build_stack_plan(plan, source_layer, @@ -2014,6 +2066,7 @@ static std::shared_ptr top_surface_imag solver, source_surface, use_blue_noise_error_diffusion, + stack_area_extensions, throw_if_canceled); }; return cache != nullptr ? cache->get_or_build(key, builder) : builder(); @@ -2088,6 +2141,30 @@ static Polygons top_surface_image_layer_perimeter_polygons(const Layer &layer, c return out; } +static ExPolygons top_surface_image_contoning_printable_area(ExPolygons area, float min_feature_mm) +{ + if (area.empty()) + return {}; + area = union_ex(area); + ExPolygons out; + const double min_area_mm2 = std::max(0.05, double(min_feature_mm) * double(min_feature_mm) * 0.08); + for (ExPolygon &expolygon : area) + if (top_surface_image_scaled_area_mm2(expolygon.area()) >= min_area_mm2) + out.emplace_back(std::move(expolygon)); + return out.empty() ? ExPolygons() : union_ex(out); +} + +static ExPolygons top_surface_image_contoning_replacement_wall_area(const ExPolygons &source_area, + const ExPolygons &wall_area, + coord_t grow_radius, + float min_feature_mm) +{ + if (source_area.empty() || wall_area.empty()) + return {}; + ExPolygons out = intersection_ex(offset_ex(source_area, float(grow_radius)), wall_area, ApplySafetyOffset::Yes); + return top_surface_image_contoning_printable_area(std::move(out), min_feature_mm); +} + static std::shared_ptr top_surface_image_contoning_build_depth_region_plan( const TopSurfaceImageRegionPlan &plan, const Layer &source_layer, @@ -2097,6 +2174,7 @@ static std::shared_ptr top_surfac const PrintConfig &print_config, const TextureMappingContoningSolver &solver, TopSurfaceImageSourceSurface source_surface, + const std::vector *stack_area_extensions, const ThrowIfCanceled *throw_if_canceled) { std::shared_ptr out = @@ -2121,6 +2199,7 @@ static std::shared_ptr top_surfac print_config, solver, source_surface, + stack_area_extensions, throw_if_canceled); if (!source_context || source_context->stack_layers <= 0 || source_context->pattern_filaments <= 0) return out; @@ -2143,6 +2222,25 @@ static std::shared_ptr top_surfac if (area.empty() && !include_perimeter_regions) return; + if (plan.contoning_replace_top_perimeters_with_infill) { + std::vector stack_regions = + top_surface_image_contoning_vector_regions(plan, + source_layer, + perimeter_area, + object, + zone, + print_config, + depth, + depth_solver, + source_surface, + source, + plan.contoning_blue_noise_error_diffusion_enabled, + throw_if_canceled); + out->fill_regions_by_depth[size_t(depth)] = stack_regions; + out->perimeter_regions_by_depth[size_t(depth)] = std::move(stack_regions); + return; + } + if (plan.contoning_blue_noise_error_diffusion_enabled) { if (!area.empty()) { out->fill_regions_by_depth[size_t(depth)] = @@ -2223,6 +2321,8 @@ static std::shared_ptr top_surfac const PrintConfig &print_config, const TextureMappingContoningSolver &solver, TopSurfaceImageSourceSurface source_surface, + int target_depth, + const std::vector *stack_area_extensions, const ThrowIfCanceled *throw_if_canceled) { TopSurfaceImageContoningStackPlanKey key = @@ -2231,6 +2331,7 @@ static std::shared_ptr top_surfac zone, solver, source_surface, + target_depth, plan.contoning_blue_noise_error_diffusion_enabled); auto builder = [&]() { return top_surface_image_contoning_build_depth_region_plan(plan, @@ -2241,6 +2342,7 @@ static std::shared_ptr top_surfac print_config, solver, source_surface, + stack_area_extensions, throw_if_canceled); }; return cache != nullptr ? cache->get_or_build_depth_regions(key, builder) : builder(); @@ -2248,8 +2350,10 @@ static std::shared_ptr top_surfac static void top_surface_image_append_contoning_slices(TopSurfaceImageRegionPlan &plan, const Layer &source_layer, - const ExPolygons &area, - const ExPolygons &perimeter_area, + const ExPolygons &fill_area, + const ExPolygons &vector_area, + const ExPolygons &perimeter_clip_area, + const ExPolygons &stack_extension_area, const PrintObject &object, const TextureMappingZone &zone, const PrintConfig &print_config, @@ -2259,13 +2363,26 @@ static void top_surface_image_append_contoning_slices(TopSurfaceImageRegionPlan TopSurfaceImageContoningStackPlanCache *stack_plan_cache, const ThrowIfCanceled *throw_if_canceled) { - if (perimeter_area.empty() || !solver.valid()) + if (vector_area.empty() || !solver.valid()) return; check_canceled(throw_if_canceled); const int pattern_filaments = top_surface_image_contoning_pattern_filaments(plan.contoning_stack_layers, plan.contoning_pattern_filaments); const bool include_perimeter_regions = plan.contoning_replace_top_perimeters_with_infill || plan.contoning_recolor_surrounding_perimeters; + const int stack_layers = + std::clamp(plan.contoning_stack_layers, + TextureMappingZone::MinTopSurfaceContoningStackLayers, + TextureMappingZone::MaxTopSurfaceContoningStackLayers); + std::vector stack_area_extensions; + const std::vector *stack_area_extensions_ptr = nullptr; + if (plan.contoning_replace_top_perimeters_with_infill && !stack_extension_area.empty() && depth >= 0) { + const int extension_layers = std::min(stack_layers, depth + 1); + stack_area_extensions.resize(size_t(extension_layers)); + for (ExPolygons &extension : stack_area_extensions) + extension = stack_extension_area; + stack_area_extensions_ptr = &stack_area_extensions; + } std::vector by_component(print_config.filament_colour.values.size() + 1); std::vector perimeter_by_component(include_perimeter_regions ? print_config.filament_colour.values.size() + 1 : 0); @@ -2281,10 +2398,13 @@ static void top_surface_image_append_contoning_slices(TopSurfaceImageRegionPlan region.bottom_to_top[size_t(int(region.bottom_to_top.size()) - 1 - pattern_depth)]; if (component_id == 0 || component_id >= by_component.size()) continue; - if (for_perimeter && include_perimeter_regions) - append(perimeter_by_component[component_id], region.area); - if (for_fill && !area.empty()) { - ExPolygons component_area = intersection_ex(region.area, area, ApplySafetyOffset::Yes); + if (for_perimeter && include_perimeter_regions && !perimeter_clip_area.empty()) { + ExPolygons component_perimeter_area = intersection_ex(region.area, perimeter_clip_area, ApplySafetyOffset::Yes); + if (!component_perimeter_area.empty()) + append(perimeter_by_component[component_id], std::move(component_perimeter_area)); + } + if (for_fill && !fill_area.empty()) { + ExPolygons component_area = intersection_ex(region.area, fill_area, ApplySafetyOffset::Yes); if (!component_area.empty()) append(by_component[component_id], std::move(component_area)); } @@ -2302,6 +2422,7 @@ static void top_surface_image_append_contoning_slices(TopSurfaceImageRegionPlan print_config, solver, source_surface, + stack_area_extensions_ptr, throw_if_canceled); } return source_context ? &*source_context : nullptr; @@ -2313,12 +2434,14 @@ static void top_surface_image_append_contoning_slices(TopSurfaceImageRegionPlan top_surface_image_contoning_depth_region_plan(stack_plan_cache, plan, source_layer, - perimeter_area, + vector_area, object, zone, print_config, solver, source_surface, + depth, + stack_area_extensions_ptr, throw_if_canceled); if (depth >= 0 && depth < int(depth_region_plan->fill_regions_by_depth.size())) append_regions(depth_region_plan->fill_regions_by_depth[size_t(depth)], true, false); @@ -2329,25 +2452,44 @@ static void top_surface_image_append_contoning_slices(TopSurfaceImageRegionPlan top_surface_image_contoning_stack_plan(stack_plan_cache, plan, source_layer, - perimeter_area, + vector_area, object, zone, print_config, solver, source_surface, + depth, false, + stack_area_extensions_ptr, throw_if_canceled); const std::vector stack_regions = - top_surface_image_contoning_vector_regions_from_stack_plan(plan, *stack_plan, perimeter_area, depth, throw_if_canceled); + top_surface_image_contoning_vector_regions_from_stack_plan(plan, *stack_plan, vector_area, depth, throw_if_canceled); append_regions(stack_regions, true, include_perimeter_regions); } } else if (plan.contoning_blue_noise_error_diffusion_enabled) { - if (!area.empty()) { + if (plan.contoning_replace_top_perimeters_with_infill) { + if (const TopSurfaceImageContoningSourceContext *ctx = get_source_context()) { + const std::vector stack_regions = + top_surface_image_contoning_vector_regions(plan, + source_layer, + vector_area, + object, + zone, + print_config, + depth, + solver, + source_surface, + ctx, + true, + throw_if_canceled); + append_regions(stack_regions, true, true); + } + } else if (!fill_area.empty()) { if (const TopSurfaceImageContoningSourceContext *ctx = get_source_context()) { const std::vector fill_regions = top_surface_image_contoning_vector_regions(plan, source_layer, - area, + fill_area, object, zone, print_config, @@ -2360,12 +2502,12 @@ static void top_surface_image_append_contoning_slices(TopSurfaceImageRegionPlan append_regions(fill_regions, true, false); } } - if (include_perimeter_regions) { + if (include_perimeter_regions && !plan.contoning_replace_top_perimeters_with_infill) { if (const TopSurfaceImageContoningSourceContext *ctx = get_source_context()) { const std::vector perimeter_regions = top_surface_image_contoning_vector_regions(plan, source_layer, - perimeter_area, + vector_area, object, zone, print_config, @@ -2383,7 +2525,7 @@ static void top_surface_image_append_contoning_slices(TopSurfaceImageRegionPlan const std::vector stack_regions = top_surface_image_contoning_vector_regions(plan, source_layer, - perimeter_area, + vector_area, object, zone, print_config, @@ -2481,7 +2623,9 @@ static std::vector top_surface_image_region_plans( continue; TopSurfaceImageRegionPlan plan; + plan.target_layer = &layer; plan.zone = zone; + plan.region_id = region_id; plan.zone_id = zone_id; plan.same_layer_partition = zone->top_surface_image_printing_method == int(TextureMappingZone::TopSurfaceImageSameLayer45Partition); @@ -2522,6 +2666,11 @@ static std::vector top_surface_image_region_plans( std::clamp(zone->top_surface_contoning_perimeter_mode, int(TextureMappingZone::ContoningPerimeterSegmentBlocks), int(TextureMappingZone::ContoningPerimeterSegmentInfill)); + plan.contoning_flat_surface_infill_mode = + std::clamp(TextureMappingZone::effective_top_surface_contoning_flat_surface_infill_mode( + zone->top_surface_contoning_flat_surface_infill_mode), + int(TextureMappingZone::ContoningFlatSurfaceInfillRectilinear), + int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinHybrid)); plan.contoning_layer_phase_enabled = zone->top_surface_contoning_layer_phase_enabled; plan.contoning_varied_infill_angles_enabled = zone->top_surface_contoning_varied_infill_angles_enabled; plan.contoning_blue_noise_error_diffusion_enabled = zone->top_surface_contoning_blue_noise_error_diffusion_enabled; @@ -2534,10 +2683,31 @@ static std::vector top_surface_image_region_plans( (plan.same_layer_partition ? plan.colored_top_layers : int(plan.components_bottom_to_top.size())); + ExPolygons current_region_wall_area; + coord_t replacement_wall_grow_radius = 0; + if (plan.contoning && plan.contoning_replace_top_perimeters_with_infill) { + Polygons current_region_wall_polygons; + layerm->perimeters.polygons_covered_by_width(current_region_wall_polygons, 0.f); + if (!current_region_wall_polygons.empty()) + current_region_wall_area = union_ex(current_region_wall_polygons); + const Flow perimeter_flow = layerm->flow(frPerimeter); + const float perimeter_spacing_mm = float(perimeter_flow.spacing()); + const int wall_loops = std::max(1, layerm->region().config().wall_loops.value); + const float wall_depth_mm = + 0.5f * plan.contoning_external_width_mm + + float(std::max(0, wall_loops - 1)) * perimeter_spacing_mm + + 0.5f * float(perimeter_flow.width()) + + 0.05f; + replacement_wall_grow_radius = + std::max(1, scale_(std::max(wall_depth_mm, plan.contoning_external_width_mm))); + } if (plan.contoning) { auto append_contoning_surface = [&](TopSurfaceImageSourceSurface source_surface) { std::vector contoning_depth_layers(size_t(stack_depth), nullptr); - std::vector contoning_depth_areas(static_cast(stack_depth)); + std::vector contoning_fill_areas(static_cast(stack_depth)); + std::vector contoning_vector_areas(static_cast(stack_depth)); + std::vector contoning_perimeter_clip_areas(static_cast(stack_depth)); + std::vector contoning_stack_extension_areas(static_cast(stack_depth)); for (int depth = 0; depth < stack_depth; ++depth) { check_canceled(throw_if_canceled); const Layer *source_layer = &layer; @@ -2548,29 +2718,47 @@ static std::vector top_surface_image_region_plans( if (source_layer == nullptr) break; - ExPolygons perimeter_area = top_surface_image_visible_surface_mask(*source_layer, zone_id, source_surface); - if (perimeter_area.empty()) + ExPolygons source_area = top_surface_image_visible_surface_mask(*source_layer, zone_id, source_surface); + if (source_area.empty()) continue; - ExPolygons area = perimeter_area; + ExPolygons fill_area = source_area; + ExPolygons vector_area = source_area; + ExPolygons perimeter_clip_area = source_area; + ExPolygons replacement_wall_area; + if (plan.contoning_replace_top_perimeters_with_infill && !current_region_wall_area.empty()) { + replacement_wall_area = + top_surface_image_contoning_replacement_wall_area(source_area, + current_region_wall_area, + replacement_wall_grow_radius, + plan.contoning_min_feature_mm); + perimeter_clip_area = replacement_wall_area; + if (!replacement_wall_area.empty()) { + append(vector_area, replacement_wall_area); + vector_area = union_ex(vector_area); + } + } if (!plan.contoning_replace_top_perimeters_with_infill && !current_layer_perimeters.empty()) - area = diff_ex(area, current_layer_perimeters, ApplySafetyOffset::Yes); - if (area.empty() && !plan.contoning_recolor_surrounding_perimeters) + fill_area = diff_ex(fill_area, current_layer_perimeters, ApplySafetyOffset::Yes); + if (fill_area.empty() && perimeter_clip_area.empty() && !plan.contoning_recolor_surrounding_perimeters) continue; contoning_depth_layers[size_t(depth)] = source_layer; - contoning_depth_areas[size_t(depth)] = std::move(area); + contoning_fill_areas[size_t(depth)] = std::move(fill_area); + contoning_vector_areas[size_t(depth)] = std::move(vector_area); + contoning_perimeter_clip_areas[size_t(depth)] = std::move(perimeter_clip_area); + contoning_stack_extension_areas[size_t(depth)] = std::move(replacement_wall_area); } for (int depth = 0; depth < stack_depth; ++depth) { check_canceled(throw_if_canceled); if (contoning_depth_layers[size_t(depth)] == nullptr) continue; - ExPolygons perimeter_area = - top_surface_image_visible_surface_mask(*contoning_depth_layers[size_t(depth)], zone_id, source_surface); - if (perimeter_area.empty()) + if (contoning_vector_areas[size_t(depth)].empty()) continue; top_surface_image_append_contoning_slices(plan, *contoning_depth_layers[size_t(depth)], - contoning_depth_areas[size_t(depth)], - perimeter_area, + contoning_fill_areas[size_t(depth)], + contoning_vector_areas[size_t(depth)], + contoning_perimeter_clip_areas[size_t(depth)], + contoning_stack_extension_areas[size_t(depth)], *object, *zone, print_config, @@ -2695,19 +2883,6 @@ static std::vector top_surface_image_contoning_per return masks; } -static ExPolygons top_surface_image_contoning_printable_area(ExPolygons area, float min_feature_mm) -{ - if (area.empty()) - return {}; - area = union_ex(area); - ExPolygons out; - const double min_area_mm2 = std::max(0.05, double(min_feature_mm) * double(min_feature_mm) * 0.08); - for (ExPolygon &expolygon : area) - if (top_surface_image_scaled_area_mm2(expolygon.area()) >= min_area_mm2) - out.emplace_back(std::move(expolygon)); - return out.empty() ? ExPolygons() : union_ex(out); -} - static void top_surface_image_move_collection_entities(ExtrusionEntitiesPtr &out, ExtrusionEntityCollection &collection) { for (ExtrusionEntity *entity : collection.entities) @@ -3064,9 +3239,11 @@ static void top_surface_image_append_colored_block_loops(LayerRegion &layerm, } static void top_surface_image_append_perimeter_infill_surface(std::vector &surface_fills, - LayerRegion &layerm, + const LayerRegion &layerm, size_t region_id, + const TopSurfaceImageRegionPlan &plan, const TopSurfaceImagePerimeterMask &mask, + int flat_mode, ExPolygons &&area) { if (area.empty() || mask.component_id == 0) @@ -3075,9 +3252,27 @@ static void top_surface_image_append_perimeter_infill_surface(std::vectorheight; surface.thickness_layers = 1; + const ExtrusionRole extrusion_role = surface.is_top() ? erTopSolidInfill : (surface.is_bottom() ? erBottomSurface : erSolidInfill); + for (SurfaceFill &fill : surface_fills) { + const SurfaceFillParams &existing = fill.params; + if (!existing.texture_mapping_top_surface_image || + !existing.texture_mapping_top_surface_contoning || + existing.texture_mapping_top_surface_contoning_flat_surface_infill_mode != flat_mode || + existing.texture_mapping_top_surface_zone_id != mask.zone_id || + existing.texture_mapping_top_surface_component_id != mask.component_id || + existing.texture_mapping_top_surface_stack_depth != mask.depth || + existing.extrusion_role != extrusion_role) + continue; + append_surface_fill_expolygons(fill, region_id, surface, std::move(area), layerm); + fill.expolygons = union_ex(fill.expolygons); + return; + } + SurfaceFillParams params; params.extruder = mask.component_id; params.pattern = ipRectilinear; + if (flat_mode == int(TextureMappingZone::ContoningFlatSurfaceInfillConcentric)) + params.pattern = ipConcentric; params.density = 100.f; params.angle = mask.angle_rad; params.fixed_angle = true; @@ -3089,18 +3284,55 @@ static void top_surface_image_append_perimeter_infill_surface(std::vectorheight); params.spacing = params.flow.spacing(); - params.extrusion_role = surface.is_top() ? erTopSolidInfill : (surface.is_bottom() ? erBottomSurface : erSolidInfill); + params.extrusion_role = extrusion_role; params.texture_mapping_top_surface_image = true; params.texture_mapping_top_surface_zone_id = mask.zone_id; params.texture_mapping_top_surface_component_id = mask.component_id; params.texture_mapping_top_surface_stack_depth = mask.depth; params.texture_mapping_top_surface_fixed_coloring = true; params.texture_mapping_top_surface_contoning = true; - params.texture_mapping_top_surface_component_index = 0; - params.texture_mapping_top_surface_component_count = 1; + params.texture_mapping_top_surface_min_width_mm = plan.min_width_mm; + params.texture_mapping_top_surface_max_width_mm = plan.max_width_mm; + const int pattern_filaments = top_surface_image_contoning_pattern_filaments(plan.contoning_stack_layers, + plan.contoning_pattern_filaments); + params.texture_mapping_top_surface_component_index = pattern_filaments > 0 ? mask.depth % pattern_filaments : 0; + params.texture_mapping_top_surface_component_count = std::max(1, pattern_filaments); + params.texture_mapping_top_surface_contoning_flat_surface_infill_mode = flat_mode; SurfaceFill &fill = surface_fill_for_params(surface_fills, params); append_surface_fill_expolygons(fill, region_id, surface, std::move(area), layerm); + fill.expolygons = union_ex(fill.expolygons); +} + +static void top_surface_image_append_contoning_replacement_surfaces(std::vector &surface_fills, + const LayerRegion &layerm, + size_t region_id, + const TopSurfaceImageRegionPlan &plan, + const ThrowIfCanceled *throw_if_canceled) +{ + if (!plan.contoning || !plan.contoning_replace_top_perimeters_with_infill) + return; + std::vector masks = top_surface_image_contoning_perimeter_masks(plan); + if (masks.empty()) + return; + ExPolygons taken; + for (const TopSurfaceImagePerimeterMask &mask : masks) { + check_canceled(throw_if_canceled); + ExPolygons area = mask.area; + if (!taken.empty()) + area = diff_ex(area, taken, ApplySafetyOffset::Yes); + area = top_surface_image_contoning_printable_area(std::move(area), plan.contoning_min_feature_mm); + if (area.empty()) + continue; + append(taken, area); + top_surface_image_append_perimeter_infill_surface(surface_fills, + layerm, + region_id, + plan, + mask, + plan.contoning_flat_surface_infill_mode, + std::move(area)); + } } static void top_surface_image_apply_contoning_perimeter_options(const Layer &layer, @@ -3127,32 +3359,21 @@ static void top_surface_image_apply_contoning_perimeter_options(const Layer &lay if (wall_area.empty()) continue; - ExPolygons affected_area; for (TopSurfaceImagePerimeterMask &mask : masks) { check_canceled(throw_if_canceled); mask.area = intersection_ex(mask.area, wall_area, ApplySafetyOffset::Yes); mask.area = top_surface_image_contoning_printable_area(std::move(mask.area), plan.contoning_min_feature_mm); + } + ExPolygons affected_area; + for (const TopSurfaceImagePerimeterMask &mask : masks) if (!mask.area.empty()) append(affected_area, mask.area); - } if (affected_area.empty()) continue; affected_area = union_ex(affected_area); if (plan.contoning_replace_top_perimeters_with_infill) { top_surface_image_trim_perimeters_by_mask(layerm->perimeters, affected_area); - ExPolygons taken; - for (const TopSurfaceImagePerimeterMask &mask : masks) { - check_canceled(throw_if_canceled); - ExPolygons area = mask.area; - if (!taken.empty()) - area = diff_ex(area, taken, ApplySafetyOffset::Yes); - area = top_surface_image_contoning_printable_area(std::move(area), plan.contoning_min_feature_mm); - if (area.empty()) - continue; - append(taken, area); - top_surface_image_append_perimeter_infill_surface(surface_fills, *layerm, region_id, mask, std::move(area)); - } continue; } @@ -3173,10 +3394,22 @@ static void top_surface_image_apply_contoning_perimeter_options(const Layer &lay continue; append(taken, area); if (plan.contoning_perimeter_mode == int(TextureMappingZone::ContoningPerimeterSegmentInfill)) { - top_surface_image_append_perimeter_infill_surface(surface_fills, *layerm, region_id, mask, std::move(area)); + top_surface_image_append_perimeter_infill_surface(surface_fills, + *layerm, + region_id, + plan, + mask, + int(TextureMappingZone::ContoningFlatSurfaceInfillRectilinear), + std::move(area)); } else { top_surface_image_append_colored_block_loops(*layerm, area, mask.component_id); - top_surface_image_append_perimeter_infill_surface(surface_fills, *layerm, region_id, mask, std::move(area)); + top_surface_image_append_perimeter_infill_surface(surface_fills, + *layerm, + region_id, + plan, + mask, + int(TextureMappingZone::ContoningFlatSurfaceInfillRectilinear), + std::move(area)); } } } @@ -3213,6 +3446,9 @@ static SurfaceFillParams top_surface_image_params_for_slice(const Layer &layer, float(print_config.nozzle_diameter.values.empty() ? 0.4 : print_config.nozzle_diameter.values.front()); const float height = float((surface.thickness == -1) ? layer.height : surface.thickness); if (slice.contoning) { + const int flat_mode = plan.contoning_flat_surface_infill_mode; + if (flat_mode == int(TextureMappingZone::ContoningFlatSurfaceInfillConcentric)) + params.pattern = ipConcentric; params.flow = base_params.flow; params.spacing = base_params.spacing; } else { @@ -3232,6 +3468,7 @@ static SurfaceFillParams top_surface_image_params_for_slice(const Layer &layer, params.texture_mapping_top_surface_contoning = slice.contoning; params.texture_mapping_top_surface_component_index = int(slice.component_index); params.texture_mapping_top_surface_component_count = int(slice.component_count); + params.texture_mapping_top_surface_contoning_flat_surface_infill_mode = plan.contoning_flat_surface_infill_mode; return params; } @@ -3472,6 +3709,340 @@ static void top_surface_image_same_layer_partition_fill(ExtrusionEntityCollectio } } +static bool top_surface_image_contoning_boundary_skin_mode(int mode) +{ + return mode == int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinFixed) || + mode == int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinVariable) || + mode == int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinHybrid); +} + +static bool top_surface_image_contoning_spiral_mode(int mode) +{ + return mode == int(TextureMappingZone::ContoningFlatSurfaceInfillSpiral); +} + +static Flow top_surface_image_contoning_boundary_skin_flow(const SurfaceFillParams ¶ms, bool variable) +{ + const float min_width = std::clamp(params.texture_mapping_top_surface_min_width_mm, + TextureMappingZone::MinTopSurfaceImageLineWidthMm, + TextureMappingZone::MaxTopSurfaceImageLineWidthMm); + const float max_width = std::clamp(params.texture_mapping_top_surface_max_width_mm, + min_width, + TextureMappingZone::MaxTopSurfaceImageLineWidthMm); + const float width = variable ? + min_width : + std::clamp(params.flow.width(), min_width, max_width); + return Flow(width, params.flow.height(), params.flow.nozzle_diameter()); +} + +static void top_surface_image_filter_short_boundary_skin_entities(ExtrusionEntityCollection &collection, float min_length_mm) +{ + ExtrusionEntitiesPtr kept; + kept.reserve(collection.entities.size()); + for (ExtrusionEntity *entity : collection.entities) { + if (entity != nullptr && unscale(entity->length()) >= double(min_length_mm)) + kept.emplace_back(entity); + else + delete entity; + } + collection.entities = std::move(kept); +} + +static ExPolygons top_surface_image_boundary_skin_leftover(const Surface &surface, + const ExtrusionEntityCollection &collection) +{ + if (collection.empty()) + return ExPolygons { surface.expolygon }; + Polygons covered_polygons; + collection.polygons_covered_by_width(covered_polygons, float(scale_(0.02))); + if (covered_polygons.empty()) + return ExPolygons { surface.expolygon }; + ExPolygons covered = intersection_ex(union_ex(covered_polygons), ExPolygons { surface.expolygon }, ApplySafetyOffset::Yes); + if (covered.empty()) + return ExPolygons { surface.expolygon }; + return diff_ex(ExPolygons { surface.expolygon }, covered, ApplySafetyOffset::Yes); +} + +static bool top_surface_image_contoning_connector_printable(const ExPolygon &area, + const Point &from, + const Point &to, + coord_t max_length) +{ + if (from == to) + return true; + if (max_length > 0) { + const double max_length_sq = double(max_length) * double(max_length); + if ((to - from).cast().squaredNorm() > max_length_sq) + return false; + } + return area.contains(Line(from, to)); +} + +static coord_t top_surface_image_spiral_spacing(const SurfaceFillParams ¶ms) +{ + const double spacing = params.spacing > EPSILON ? params.spacing : params.flow.spacing(); + return std::max(1, scaled(spacing)); +} + +static Polygons top_surface_image_spiral_loops(const Surface &surface, const SurfaceFillParams ¶ms) +{ + const coord_t spacing = top_surface_image_spiral_spacing(params); + const coord_t half_spacing = std::max(1, spacing / 2); + const coord_t centerline_inset = std::max(1, params.flow.scaled_width() / 2); + ExPolygons last = offset_ex(surface.expolygon, -float(centerline_inset)); + Polygons loops = to_polygons(last); + while (!last.empty()) { + last = offset2_ex(last, -(spacing + half_spacing), +half_spacing); + append(loops, to_polygons(last)); + } + loops = union_pt_chained_outside_in(loops); + for (Polygon &loop : loops) + if (loop.points.size() >= 3) + loop.densify(float(spacing)); + return loops; +} + +static int top_surface_image_spiral_loop_start_index(const Polygon &loop, + const Point &from, + const ExPolygon *clip, + coord_t max_connector) +{ + int best = -1; + double best_dist = std::numeric_limits::max(); + for (size_t i = 0; i < loop.points.size(); ++i) { + const Point &point = loop.points[i]; + if (clip != nullptr && + !top_surface_image_contoning_connector_printable(*clip, from, point, max_connector)) + continue; + const double dist = (point - from).cast().squaredNorm(); + if (dist < best_dist) { + best_dist = dist; + best = int(i); + } + } + return best; +} + +static Polyline top_surface_image_spiral_loop_arc(const Polygon &loop, int start_idx) +{ + Polyline out; + const int point_count = int(loop.points.size()); + if (point_count < 3) + return out; + int idx = ((start_idx % point_count) + point_count) % point_count; + const int end_idx = (idx + point_count - 1) % point_count; + out.points.reserve(size_t(point_count)); + for (;;) { + out.points.emplace_back(loop.points[size_t(idx)]); + if (idx == end_idx) + break; + idx = (idx + 1) % point_count; + } + remove_same_neighbor(out); + return out; +} + +static Polylines top_surface_image_spiral_polylines(const Surface &surface, + const Polygons &loops, + coord_t max_connector, + const ThrowIfCanceled *throw_if_canceled) +{ + Polylines out; + Polyline current; + Point last_pos(0, 0); + for (const Polygon &loop : loops) { + check_canceled(throw_if_canceled); + if (loop.points.size() < 3) + continue; + int start_idx = current.empty() ? + top_surface_image_spiral_loop_start_index(loop, last_pos, nullptr, 0) : + top_surface_image_spiral_loop_start_index(loop, current.last_point(), &surface.expolygon, max_connector); + if (start_idx < 0) { + if (current.is_valid()) + out.emplace_back(std::move(current)); + current = Polyline(); + start_idx = top_surface_image_spiral_loop_start_index(loop, last_pos, nullptr, 0); + } + if (start_idx < 0) + continue; + Polyline loop_polyline = top_surface_image_spiral_loop_arc(loop, start_idx); + remove_same_neighbor(loop_polyline); + if (!loop_polyline.is_valid()) + continue; + if (!current.empty() && + !top_surface_image_contoning_connector_printable(surface.expolygon, + current.last_point(), + loop_polyline.first_point(), + max_connector)) { + if (current.is_valid()) + out.emplace_back(std::move(current)); + current = Polyline(); + } + current.append(std::move(loop_polyline)); + last_pos = current.last_point(); + } + if (current.is_valid()) + out.emplace_back(std::move(current)); + return out; +} + +static std::unique_ptr top_surface_image_spiral_collection( + const Surface &surface, + const SurfaceFillParams ¶ms, + ExPolygons *leftover, + const ThrowIfCanceled *throw_if_canceled) +{ + check_canceled(throw_if_canceled); + std::unique_ptr collection(new ExtrusionEntityCollection()); + collection->no_sort = true; + const coord_t spacing = top_surface_image_spiral_spacing(params); + const coord_t max_connector = std::max(1, 2 * spacing); + Polygons loops = top_surface_image_spiral_loops(surface, params); + Polylines polylines = top_surface_image_spiral_polylines(surface, loops, max_connector, throw_if_canceled); + const float min_length_mm = + std::max(0.18f, std::min(0.75f, params.flow.width() * 0.55f)); + for (Polyline &polyline : polylines) { + check_canceled(throw_if_canceled); + remove_same_neighbor(polyline); + if (!polyline.is_valid() || unscale(polyline.length()) < double(min_length_mm)) + continue; + ExtrusionPath *path = new ExtrusionPath(params.extrusion_role, + params.flow.mm3_per_mm(), + params.flow.width(), + params.flow.height()); + path->polyline = std::move(polyline); + collection->entities.emplace_back(path); + } + if (leftover != nullptr) + *leftover = top_surface_image_boundary_skin_leftover(surface, *collection); + return collection; +} + +static Polyline top_surface_image_boundary_skin_polyline_from_thick(const ThickPolyline &source) +{ + Polyline out; + if (source.points.size() < 2) + return out; + out.points = source.points; + remove_same_neighbor(out); + return out; +} + +static std::unique_ptr top_surface_image_boundary_skin_collection( + const Surface &surface, + const SurfaceFillParams ¶ms, + const PrintConfig &print_config, + const PrintObjectConfig &object_config, + int layer_id, + bool variable, + bool hybrid, + ExPolygons *leftover, + const ThrowIfCanceled *throw_if_canceled) +{ + check_canceled(throw_if_canceled); + std::unique_ptr collection(new ExtrusionEntityCollection()); + collection->no_sort = true; + const float min_width = std::clamp(params.texture_mapping_top_surface_min_width_mm, + TextureMappingZone::MinTopSurfaceImageLineWidthMm, + TextureMappingZone::MaxTopSurfaceImageLineWidthMm); + const float max_width = std::clamp(params.texture_mapping_top_surface_max_width_mm, + min_width, + TextureMappingZone::MaxTopSurfaceImageLineWidthMm); + const Flow max_flow = Flow(max_width, params.flow.height(), params.flow.nozzle_diameter()); + const Flow min_flow = Flow(min_width, params.flow.height(), params.flow.nozzle_diameter()); + const Flow output_flow = top_surface_image_contoning_boundary_skin_flow(params, variable); + const coord_t preferred_spacing = std::max(1, variable ? max_flow.scaled_spacing() : output_flow.scaled_spacing()); + const coordf_t min_spacing = std::max(1.0, min_flow.scaled_spacing()); + const coordf_t max_spacing = std::max(min_spacing, max_flow.scaled_spacing()); + const coordf_t output_spacing = std::max(1.0, output_flow.scaled_spacing()); + + Polygons outline = to_polygons(surface.expolygon); + ExPolygons inner_leftover; + if (!outline.empty()) { + Arachne::WallToolPathsParams input_params = + Arachne::make_paths_params(layer_id, object_config, print_config); + input_params.min_bead_width = variable ? min_width : output_flow.width(); + if (!std::isfinite(input_params.min_feature_size) || input_params.min_feature_size <= 0.f) + input_params.min_feature_size = min_width * 0.5f; + else + input_params.min_feature_size = std::min(input_params.min_feature_size, min_width * 0.5f); + input_params.min_length_factor = + (!std::isfinite(input_params.min_length_factor) || input_params.min_length_factor <= 0.f) ? + 0.5f : + std::min(input_params.min_length_factor, 0.5f); + input_params.wall_distribution_count = std::max(1, input_params.wall_distribution_count); + input_params.is_top_or_bottom_layer = true; + + Arachne::WallToolPaths wall_tool_paths(outline, + preferred_spacing, + preferred_spacing, + 1, + 0, + params.flow.height(), + input_params); + std::vector loops = wall_tool_paths.getToolPaths(); + inner_leftover = intersection_ex(union_ex(wall_tool_paths.getInnerContour()), + ExPolygons { surface.expolygon }, + ApplySafetyOffset::Yes); + ThickPolylines thick_polylines; + Point last_pos(0, 0); + for (Arachne::VariableWidthLines &loop : loops) { + check_canceled(throw_if_canceled); + for (const Arachne::ExtrusionLine &wall : loop) { + if (wall.size() < 2) + continue; + ThickPolyline thick_polyline = Arachne::to_thick_polyline(wall); + if (thick_polyline.points.size() < 2 || thick_polyline.width.empty()) + continue; + if (wall.is_closed && + thick_polyline.points.front() == thick_polyline.points.back() && + thick_polyline.width.front() == thick_polyline.width.back()) { + thick_polyline.points.pop_back(); + if (!thick_polyline.points.empty()) { + const int nearest_idx = last_pos.nearest_point_index(thick_polyline.points); + std::rotate(thick_polyline.points.begin(), thick_polyline.points.begin() + nearest_idx, thick_polyline.points.end()); + std::rotate(thick_polyline.width.begin(), thick_polyline.width.begin() + 2 * nearest_idx, thick_polyline.width.end()); + thick_polyline.points.emplace_back(thick_polyline.points.front()); + } + } + for (coordf_t &width : thick_polyline.width) + width = variable ? std::clamp(width, min_spacing, max_spacing) : output_spacing; + if (thick_polyline.is_valid()) { + last_pos = thick_polyline.last_point(); + thick_polylines.emplace_back(std::move(thick_polyline)); + } + } + } + const float min_length_mm = + std::max(0.18f, std::min(0.75f, (variable ? min_width : output_flow.width()) * 0.55f)); + if (variable || !hybrid) { + variable_width(thick_polylines, + params.extrusion_role, + variable ? max_flow : output_flow, + collection->entities); + top_surface_image_filter_short_boundary_skin_entities(*collection, min_length_mm); + } else { + for (const ThickPolyline &thick_polyline : thick_polylines) { + check_canceled(throw_if_canceled); + Polyline polyline = top_surface_image_boundary_skin_polyline_from_thick(thick_polyline); + remove_same_neighbor(polyline); + if (!polyline.is_valid() || unscale(polyline.length()) < double(min_length_mm)) + continue; + ExtrusionPath *path = new ExtrusionPath(params.extrusion_role, + output_flow.mm3_per_mm(), + output_flow.width(), + output_flow.height()); + path->polyline = std::move(polyline); + collection->entities.emplace_back(path); + } + } + } + + if (leftover != nullptr) + *leftover = collection->empty() ? ExPolygons { surface.expolygon } : std::move(inner_leftover); + return collection; +} + static void apply_top_surface_image_collection_metadata(ExtrusionEntityCollection &collection, const SurfaceFillParams ¶ms, const std::optional &context, @@ -3503,6 +4074,119 @@ static void apply_top_surface_image_collection_metadata(ExtrusionEntityCollectio collection.entities = std::move(replacement); } +static void apply_top_surface_image_entities_metadata(ExtrusionEntitiesPtr &entities, + const SurfaceFillParams ¶ms, + const std::optional &context, + const ThrowIfCanceled *throw_if_canceled) +{ + for (ExtrusionEntity *entity : entities) + if (ExtrusionEntityCollection *collection = dynamic_cast(entity)) + apply_top_surface_image_collection_metadata(*collection, params, context, throw_if_canceled); +} + +static ExtrusionPath *top_surface_image_last_extrusion_path(ExtrusionEntitiesPtr &entities) +{ + for (auto it = entities.rbegin(); it != entities.rend(); ++it) { + if (ExtrusionPath *path = dynamic_cast(*it)) + return path; + if (ExtrusionMultiPath *multipath = dynamic_cast(*it)) + if (!multipath->paths.empty()) + return &multipath->paths.back(); + if (ExtrusionEntityCollection *collection = dynamic_cast(*it)) { + if (collection->entities.empty()) + continue; + return top_surface_image_last_extrusion_path(collection->entities); + } + if (*it != nullptr) + return nullptr; + } + return nullptr; +} + +static double top_surface_image_boundary_skin_start_distance_sq(const Polyline &polyline, const Point &near) +{ + if (!polyline.is_valid()) + return std::numeric_limits::max(); + if (polyline.is_closed()) { + double best = std::numeric_limits::max(); + const size_t point_count = polyline.points.size() > 1 ? polyline.points.size() - 1 : polyline.points.size(); + for (size_t i = 0; i < point_count; ++i) + best = std::min(best, (polyline.points[i] - near).cast().squaredNorm()); + return best; + } + return std::min((polyline.first_point() - near).cast().squaredNorm(), + (polyline.last_point() - near).cast().squaredNorm()); +} + +static void top_surface_image_boundary_skin_orient_near(Polyline &polyline, const Point &near) +{ + if (!polyline.is_valid()) + return; + if (polyline.is_closed()) { + Points points = polyline.points; + points.pop_back(); + if (points.empty()) + return; + const int nearest_idx = near.nearest_point_index(points); + std::rotate(points.begin(), points.begin() + nearest_idx, points.end()); + points.emplace_back(points.front()); + polyline.points = std::move(points); + } else if ((polyline.last_point() - near).cast().squaredNorm() < + (polyline.first_point() - near).cast().squaredNorm()) { + polyline.reverse(); + } +} + +static std::unique_ptr top_surface_image_take_nearest_boundary_skin_path( + ExtrusionEntityCollection &collection, + const Point &near) +{ + size_t best_idx = collection.entities.size(); + double best_dist = std::numeric_limits::max(); + for (size_t i = 0; i < collection.entities.size(); ++i) { + const ExtrusionPath *path = dynamic_cast(collection.entities[i]); + if (path == nullptr || !path->polyline.is_valid()) + continue; + const double dist = top_surface_image_boundary_skin_start_distance_sq(path->polyline, near); + if (dist < best_dist) { + best_dist = dist; + best_idx = i; + } + } + if (best_idx == collection.entities.size()) + return nullptr; + ExtrusionPath *path = static_cast(collection.entities[best_idx]); + collection.entities.erase(collection.entities.begin() + best_idx); + top_surface_image_boundary_skin_orient_near(path->polyline, near); + return std::unique_ptr(path); +} + +static bool top_surface_image_try_join_boundary_skin_hybrid(ExtrusionEntitiesPtr &interior_entities, + ExtrusionEntityCollection &boundary_collection, + const ExPolygon &area, + coord_t max_connector) +{ + ExtrusionPath *interior_path = top_surface_image_last_extrusion_path(interior_entities); + if (interior_path == nullptr || !interior_path->polyline.is_valid()) + return false; + std::unique_ptr boundary_path = + top_surface_image_take_nearest_boundary_skin_path(boundary_collection, interior_path->last_point()); + if (!boundary_path) + return false; + if (std::abs(interior_path->width - boundary_path->width) > EPSILON || + std::abs(interior_path->height - boundary_path->height) > EPSILON || + std::abs(interior_path->mm3_per_mm - boundary_path->mm3_per_mm) > EPSILON || + !top_surface_image_contoning_connector_printable(area, + interior_path->last_point(), + boundary_path->first_point(), + max_connector)) { + boundary_collection.entities.insert(boundary_collection.entities.begin(), boundary_path.release()); + return false; + } + interior_path->polyline.append(std::move(boundary_path->polyline)); + return true; +} + // Detect narrow infill regions // Based on the anti-vibration algorithm from PrusaSlicer: @@ -4258,6 +4942,13 @@ std::vector group_fills(const Layer &layer, } } } + if (region_id < top_surface_plans.size() && + top_surface_plans[region_id].zone != nullptr) + top_surface_image_append_contoning_replacement_surfaces(surface_fills, + layerm, + region_id, + top_surface_plans[region_id], + throw_if_canceled); } { @@ -4642,6 +5333,115 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, apply_top_surface_image_collection_metadata(*collection, surface_fill.params, std::nullopt, throw_if_canceled_ptr); fill_entities.push_back(collection.release()); } + } else if (surface_fill.params.texture_mapping_top_surface_contoning && + top_surface_image_contoning_spiral_mode( + surface_fill.params.texture_mapping_top_surface_contoning_flat_surface_infill_mode)) { + ExPolygons leftover; + std::unique_ptr collection = + top_surface_image_spiral_collection(surface_fill.surface, + surface_fill.params, + &leftover, + throw_if_canceled_ptr); + if (collection && !collection->empty()) { + apply_top_surface_image_collection_metadata(*collection, surface_fill.params, std::nullopt, throw_if_canceled_ptr); + fill_entities.push_back(collection.release()); + } + if (!leftover.empty()) { + SurfaceFillParams fallback_surface_params = surface_fill.params; + fallback_surface_params.pattern = ipRectilinear; + fallback_surface_params.texture_mapping_top_surface_contoning_flat_surface_infill_mode = + int(TextureMappingZone::ContoningFlatSurfaceInfillRectilinear); + FillParams fallback_params = params; + fallback_params.pattern = ipRectilinear; + for (ExPolygon &leftover_expoly : leftover) { + check_canceled(throw_if_canceled_ptr); + Surface leftover_surface = surface_fill.surface; + leftover_surface.expolygon = std::move(leftover_expoly); + f->spacing = fallback_surface_params.spacing; + const size_t fill_entities_before = fill_entities.size(); + f->fill_surface_extrusion(&leftover_surface, fallback_params, fill_entities); + for (size_t i = fill_entities_before; i < fill_entities.size(); ++i) + if (ExtrusionEntityCollection *fallback_collection = dynamic_cast(fill_entities[i])) + apply_top_surface_image_collection_metadata(*fallback_collection, + fallback_surface_params, + std::nullopt, + throw_if_canceled_ptr); + } + } + } else if (surface_fill.params.texture_mapping_top_surface_contoning && + top_surface_image_contoning_boundary_skin_mode( + 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); + const bool hybrid_boundary_skin = + surface_fill.params.texture_mapping_top_surface_contoning_flat_surface_infill_mode == + int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinHybrid); + ExPolygons leftover; + std::unique_ptr collection = + top_surface_image_boundary_skin_collection(surface_fill.surface, + surface_fill.params, + this->object()->print()->config(), + this->object()->config(), + int(this->id()), + variable_width_boundary_skin, + hybrid_boundary_skin, + &leftover, + throw_if_canceled_ptr); + SurfaceFillParams fallback_surface_params = surface_fill.params; + fallback_surface_params.pattern = ipRectilinear; + fallback_surface_params.texture_mapping_top_surface_contoning_flat_surface_infill_mode = + int(TextureMappingZone::ContoningFlatSurfaceInfillRectilinear); + const Flow fallback_flow = top_surface_image_contoning_boundary_skin_flow(surface_fill.params, + variable_width_boundary_skin); + fallback_surface_params.flow = fallback_flow; + fallback_surface_params.spacing = fallback_flow.spacing(); + FillParams fallback_params = params; + fallback_params.flow = fallback_flow; + fallback_params.pattern = ipRectilinear; + 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); + fill_entities.push_back(collection.release()); + } + if (!leftover.empty()) { + for (ExPolygon &leftover_expoly : leftover) { + check_canceled(throw_if_canceled_ptr); + Surface leftover_surface = surface_fill.surface; + leftover_surface.expolygon = std::move(leftover_expoly); + f->spacing = fallback_surface_params.spacing; + ExtrusionEntitiesPtr *fallback_entities = hybrid_boundary_skin ? &hybrid_interior_entities : &fill_entities; + const size_t fill_entities_before = fallback_entities->size(); + f->fill_surface_extrusion(&leftover_surface, fallback_params, *fallback_entities); + if (!hybrid_boundary_skin) { + for (size_t i = fill_entities_before; i < fallback_entities->size(); ++i) + if (ExtrusionEntityCollection *fallback_collection = dynamic_cast((*fallback_entities)[i])) + apply_top_surface_image_collection_metadata(*fallback_collection, + fallback_surface_params, + std::nullopt, + throw_if_canceled_ptr); + } + } + } + if (hybrid_boundary_skin) { + if (collection && !collection->empty() && !hybrid_interior_entities.empty()) { + const coord_t max_connector = std::max(1, 2 * fallback_flow.scaled_spacing()); + top_surface_image_try_join_boundary_skin_hybrid(hybrid_interior_entities, + *collection, + surface_fill.surface.expolygon, + max_connector); + } + apply_top_surface_image_entities_metadata(hybrid_interior_entities, + fallback_surface_params, + std::nullopt, + throw_if_canceled_ptr); + fill_entities.insert(fill_entities.end(), hybrid_interior_entities.begin(), hybrid_interior_entities.end()); + hybrid_interior_entities.clear(); + } + if (hybrid_boundary_skin && collection && !collection->empty()) { + apply_top_surface_image_collection_metadata(*collection, surface_fill.params, std::nullopt, throw_if_canceled_ptr); + fill_entities.push_back(collection.release()); + } } else { const size_t fill_entities_before = fill_entities.size(); f->fill_surface_extrusion(&surface_fill.surface, diff --git a/src/libslic3r/TextureMapping.cpp b/src/libslic3r/TextureMapping.cpp index 49a48f16bce..a8595475fa9 100644 --- a/src/libslic3r/TextureMapping.cpp +++ b/src/libslic3r/TextureMapping.cpp @@ -684,6 +684,45 @@ static int top_surface_image_printing_method_from_name(const std::string &name) return int(TextureMappingZone::TopSurfaceImageSameAngle45Width); } +static std::string top_surface_contoning_flat_surface_infill_mode_name(int mode) +{ + switch (clamp_int(mode, + int(TextureMappingZone::ContoningFlatSurfaceInfillDefault), + int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinHybrid))) { + case int(TextureMappingZone::ContoningFlatSurfaceInfillRectilinear): + return "rectilinear"; + case int(TextureMappingZone::ContoningFlatSurfaceInfillConcentric): + return "concentric"; + case int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinFixed): + return "boundary_skin_fixed"; + case int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinVariable): + return "boundary_skin_variable"; + case int(TextureMappingZone::ContoningFlatSurfaceInfillSpiral): + return "spiral"; + case int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinHybrid): + return "boundary_skin_hybrid"; + default: + return "default"; + } +} + +static int top_surface_contoning_flat_surface_infill_mode_from_name(const std::string &name) +{ + if (name == "rectilinear") + return int(TextureMappingZone::ContoningFlatSurfaceInfillRectilinear); + if (name == "concentric") + return int(TextureMappingZone::ContoningFlatSurfaceInfillConcentric); + if (name == "boundary_skin" || name == "boundary_skin_variable") + return int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinVariable); + if (name == "boundary_skin_fixed") + return int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinFixed); + if (name == "spiral") + return int(TextureMappingZone::ContoningFlatSurfaceInfillSpiral); + if (name == "boundary_skin_hybrid") + return int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinHybrid); + return int(TextureMappingZone::ContoningFlatSurfaceInfillDefault); +} + static std::string top_visible_recolor_aggressiveness_name(int mode) { switch (clamp_int(mode, @@ -1057,6 +1096,7 @@ bool TextureMappingZone::operator==(const TextureMappingZone &rhs) const top_surface_contoning_replace_top_perimeters_with_infill == rhs.top_surface_contoning_replace_top_perimeters_with_infill && top_surface_contoning_recolor_surrounding_perimeters == rhs.top_surface_contoning_recolor_surrounding_perimeters && top_surface_contoning_perimeter_mode == rhs.top_surface_contoning_perimeter_mode && + top_surface_contoning_flat_surface_infill_mode == rhs.top_surface_contoning_flat_surface_infill_mode && top_surface_contoning_layer_phase_enabled == rhs.top_surface_contoning_layer_phase_enabled && top_surface_contoning_varied_infill_angles_enabled == rhs.top_surface_contoning_varied_infill_angles_enabled && top_surface_contoning_blue_noise_error_diffusion_enabled == rhs.top_surface_contoning_blue_noise_error_diffusion_enabled && @@ -1448,6 +1488,8 @@ std::string TextureMappingManager::serialize_entries() clamp_int(zone.top_surface_contoning_perimeter_mode, int(TextureMappingZone::ContoningPerimeterSegmentBlocks), int(TextureMappingZone::ContoningPerimeterSegmentInfill)); + texture["top_surface_contoning_flat_surface_infill_mode"] = + top_surface_contoning_flat_surface_infill_mode_name(zone.top_surface_contoning_flat_surface_infill_mode); texture["top_surface_contoning_layer_phase_enabled"] = zone.top_surface_contoning_layer_phase_enabled; texture["top_surface_contoning_varied_infill_angles_enabled"] = zone.top_surface_contoning_varied_infill_angles_enabled; @@ -1721,6 +1763,15 @@ void TextureMappingManager::load_entries(const std::string &serialized, TextureMappingZone::DefaultTopSurfaceContoningPerimeterMode), int(TextureMappingZone::ContoningPerimeterSegmentBlocks), int(TextureMappingZone::ContoningPerimeterSegmentInfill)); + auto flat_surface_infill_mode_it = texture.find("top_surface_contoning_flat_surface_infill_mode"); + zone.top_surface_contoning_flat_surface_infill_mode = + flat_surface_infill_mode_it != texture.end() && flat_surface_infill_mode_it->is_string() ? + top_surface_contoning_flat_surface_infill_mode_from_name(flat_surface_infill_mode_it->get()) : + clamp_int(flat_surface_infill_mode_it != texture.end() && flat_surface_infill_mode_it->is_number_integer() ? + flat_surface_infill_mode_it->get() : + TextureMappingZone::DefaultTopSurfaceContoningFlatSurfaceInfillMode, + int(TextureMappingZone::ContoningFlatSurfaceInfillDefault), + int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinHybrid)); if (zone.top_surface_contoning_replace_top_perimeters_with_infill) zone.top_surface_contoning_recolor_surrounding_perimeters = false; zone.top_surface_contoning_layer_phase_enabled = diff --git a/src/libslic3r/TextureMapping.hpp b/src/libslic3r/TextureMapping.hpp index 56714ed621a..bce13ca5dbb 100644 --- a/src/libslic3r/TextureMapping.hpp +++ b/src/libslic3r/TextureMapping.hpp @@ -78,6 +78,16 @@ struct TextureMappingZone ContoningPerimeterSegmentInfill = 2 }; + enum TopSurfaceContoningFlatSurfaceInfillMode : uint8_t { + ContoningFlatSurfaceInfillDefault = 0, + ContoningFlatSurfaceInfillRectilinear = 1, + ContoningFlatSurfaceInfillConcentric = 2, + ContoningFlatSurfaceInfillBoundarySkinFixed = 3, + ContoningFlatSurfaceInfillBoundarySkinVariable = 4, + ContoningFlatSurfaceInfillSpiral = 5, + ContoningFlatSurfaceInfillBoundarySkinHybrid = 6 + }; + enum FilamentColorMode : uint8_t { FilamentColorAny = 0, FilamentColorRGB = 1, @@ -179,6 +189,8 @@ struct TextureMappingZone static constexpr bool DefaultTopSurfaceContoningReplaceTopPerimetersWithInfill = false; static constexpr bool DefaultTopSurfaceContoningRecolorSurroundingPerimeters = false; static constexpr int DefaultTopSurfaceContoningPerimeterMode = int(ContoningPerimeterDividedLine); + static constexpr int DefaultTopSurfaceContoningFlatSurfaceInfillMode = int(ContoningFlatSurfaceInfillDefault); + static constexpr int SlicerDefaultTopSurfaceContoningFlatSurfaceInfillMode = int(ContoningFlatSurfaceInfillRectilinear); static constexpr bool DefaultTopSurfaceContoningLayerPhaseEnabled = false; static constexpr bool DefaultTopSurfaceContoningVariedInfillAnglesEnabled = false; static constexpr bool DefaultTopSurfaceContoningBlueNoiseErrorDiffusionEnabled = false; @@ -217,6 +229,13 @@ struct TextureMappingZone } } + static constexpr int effective_top_surface_contoning_flat_surface_infill_mode(int mode) + { + return mode == int(ContoningFlatSurfaceInfillDefault) ? + SlicerDefaultTopSurfaceContoningFlatSurfaceInfillMode : + mode; + } + struct LinearGradientAnchor { bool valid = false; size_t object_id = 0; @@ -287,6 +306,7 @@ struct TextureMappingZone bool top_surface_contoning_replace_top_perimeters_with_infill = DefaultTopSurfaceContoningReplaceTopPerimetersWithInfill; bool top_surface_contoning_recolor_surrounding_perimeters = DefaultTopSurfaceContoningRecolorSurroundingPerimeters; int top_surface_contoning_perimeter_mode = DefaultTopSurfaceContoningPerimeterMode; + int top_surface_contoning_flat_surface_infill_mode = DefaultTopSurfaceContoningFlatSurfaceInfillMode; bool top_surface_contoning_layer_phase_enabled = DefaultTopSurfaceContoningLayerPhaseEnabled; bool top_surface_contoning_varied_infill_angles_enabled = DefaultTopSurfaceContoningVariedInfillAnglesEnabled; bool top_surface_contoning_blue_noise_error_diffusion_enabled = DefaultTopSurfaceContoningBlueNoiseErrorDiffusionEnabled; @@ -411,6 +431,7 @@ struct TextureMappingZone top_surface_contoning_replace_top_perimeters_with_infill = DefaultTopSurfaceContoningReplaceTopPerimetersWithInfill; top_surface_contoning_recolor_surrounding_perimeters = DefaultTopSurfaceContoningRecolorSurroundingPerimeters; top_surface_contoning_perimeter_mode = DefaultTopSurfaceContoningPerimeterMode; + top_surface_contoning_flat_surface_infill_mode = DefaultTopSurfaceContoningFlatSurfaceInfillMode; top_surface_contoning_layer_phase_enabled = DefaultTopSurfaceContoningLayerPhaseEnabled; top_surface_contoning_varied_infill_angles_enabled = DefaultTopSurfaceContoningVariedInfillAnglesEnabled; top_surface_contoning_blue_noise_error_diffusion_enabled = DefaultTopSurfaceContoningBlueNoiseErrorDiffusionEnabled; diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index d26a3eb285c..6a08b028d75 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -355,6 +355,12 @@ void BackgroundSlicingProcess::thread_proc() else { //BBS: internal cancel m_internal_cancelled = true; + if (m_internal_cancel_event_requested) { + m_internal_cancel_event_requested = false; + SlicingProcessCompletedEvent evt(m_event_finished_id, 0, SlicingProcessCompletedEvent::Cancelled, exception); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": send internal SlicingProcessCompletedEvent to main, status %1%")%evt.status(); + wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, evt.Clone()); + } } m_print->restart(); lck.unlock(); @@ -556,6 +562,7 @@ bool BackgroundSlicingProcess::stop() if (m_state == STATE_STARTED || m_state == STATE_RUNNING) { // Cancel any task planned by the background thread on UI thread. cancel_ui_task(m_ui_task); + m_internal_cancel_event_requested = false; m_print->cancel(); // Wait until the background processing stops by being canceled. m_condition.wait(lck, [this](){ return m_state == STATE_CANCELED; }); @@ -586,6 +593,19 @@ bool BackgroundSlicingProcess::cancel() return m_state == STATE_FINISHED || m_state == STATE_CANCELED; } +bool BackgroundSlicingProcess::cancel_internal() +{ + BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< ", enter"< lck(m_mutex); + if (m_state == STATE_STARTED || m_state == STATE_RUNNING) { + cancel_ui_task(m_ui_task); + m_internal_cancel_event_requested = true; + m_print->cancel_internal(); + return true; + } + return false; +} + bool BackgroundSlicingProcess::reset() { bool stopped = this->stop(); diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.hpp b/src/slic3r/GUI/BackgroundSlicingProcess.hpp index c63c1350c89..aa2f05488e9 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.hpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.hpp @@ -132,6 +132,7 @@ public: // A stopped background processing may be restarted with start(). bool stop(); bool cancel(); + bool cancel_internal(); // Cancel the background processing and reset the print. Returns false if the background processing was not running. // Useful when the Model or configuration is being changed drastically. bool reset(); @@ -278,6 +279,7 @@ private: GUI::PartPlate* m_current_plate; PrinterTechnology m_printer_tech = ptUnknown; bool m_internal_cancelled = false; + bool m_internal_cancel_event_requested = false; PrintState m_step_state; bool set_step_started(BackgroundSlicingProcessStep step); diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index e1088413993..9965909674e 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -2402,6 +2402,18 @@ void NotificationManager::set_slicing_progress_percentage(const std::string& tex // Slicing progress notification was not found - init it thru plater so correct cancel callback function is appended wxGetApp().plater()->init_notification_manager(); } +void NotificationManager::set_slicing_progress_canceling(const std::string& text) +{ + for (std::unique_ptr& notification : m_pop_notifications) { + if (notification->get_type() == NotificationType::SlicingProgress) { + SlicingProgressNotification* spn = dynamic_cast(notification.get()); + spn->set_cancel_requested_text(text); + wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0); + return; + } + } + wxGetApp().plater()->init_notification_manager(); +} void NotificationManager::set_slicing_progress_canceled(const std::string& text) { for (std::unique_ptr& notification : m_pop_notifications) { diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index 4d318613902..6cecafbfbe2 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -284,6 +284,7 @@ public: void set_slicing_progress_began(); // percentage negative = canceled, <0-1) = progress, 1 = completed void set_slicing_progress_percentage(const std::string& text, float percentage); + void set_slicing_progress_canceling(const std::string& text); void set_slicing_progress_canceled(const std::string& text); // hides slicing progress notification imidietly void set_slicing_progress_hidden(); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 00c39285e05..8b8a5d7ed62 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1908,6 +1908,7 @@ public: bool top_surface_contoning_replace_top_perimeters_with_infill, bool top_surface_contoning_recolor_surrounding_perimeters, int top_surface_contoning_perimeter_mode, + int top_surface_contoning_flat_surface_infill_mode, bool top_surface_contoning_layer_phase_enabled, bool top_surface_contoning_varied_infill_angles_enabled, bool top_surface_contoning_blue_noise_error_diffusion_enabled, @@ -2532,6 +2533,27 @@ public: contoning_feature_row->Add(m_top_surface_contoning_min_feature_spin, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, gap / 2); contoning_feature_row->Add(new wxStaticText(m_top_surface_contoning_panel, wxID_ANY, _L("mm")), 0, wxALIGN_CENTER_VERTICAL); top_surface_contoning_root->Add(contoning_feature_row, 0, wxEXPAND); + auto *contoning_flat_infill_row = new wxBoxSizer(wxHORIZONTAL); + contoning_flat_infill_row->Add(new wxStaticText(m_top_surface_contoning_panel, wxID_ANY, _L("Flat surface infill mode")), + 0, + wxALIGN_CENTER_VERTICAL | wxRIGHT, + gap); + wxArrayString contoning_flat_infill_choices; + contoning_flat_infill_choices.Add(_L("Default (Rectilinear)")); + contoning_flat_infill_choices.Add(_L("Rectilinear")); + contoning_flat_infill_choices.Add(_L("Concentric")); + contoning_flat_infill_choices.Add(_L("Boundary Skin (fixed width)")); + contoning_flat_infill_choices.Add(_L("Boundary Skin (variable width)")); + contoning_flat_infill_choices.Add(_L("Spiral")); + contoning_flat_infill_choices.Add(_L("Boundary Skin Hybrid")); + m_top_surface_contoning_flat_surface_infill_choice = + new wxChoice(m_top_surface_contoning_panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, contoning_flat_infill_choices); + m_top_surface_contoning_flat_surface_infill_choice->SetSelection( + std::clamp(top_surface_contoning_flat_surface_infill_mode, + int(TextureMappingZone::ContoningFlatSurfaceInfillDefault), + int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinHybrid))); + contoning_flat_infill_row->Add(m_top_surface_contoning_flat_surface_infill_choice, 1, wxEXPAND); + top_surface_contoning_root->Add(contoning_flat_infill_row, 0, wxEXPAND | wxTOP, gap); top_surface_box->Add(m_top_surface_contoning_panel, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, gap); m_top_surface_image_fixed_coloring_filaments_checkbox = new wxCheckBox(top_surface_page, wxID_ANY, _L("Fixed top-surface coloring filaments")); @@ -3036,6 +3058,14 @@ public: int(TextureMappingZone::ContoningPerimeterSegmentInfill)) : TextureMappingZone::DefaultTopSurfaceContoningPerimeterMode; } + int top_surface_contoning_flat_surface_infill_mode() const + { + return m_top_surface_contoning_flat_surface_infill_choice != nullptr ? + std::clamp(m_top_surface_contoning_flat_surface_infill_choice->GetSelection(), + int(TextureMappingZone::ContoningFlatSurfaceInfillDefault), + int(TextureMappingZone::ContoningFlatSurfaceInfillBoundarySkinHybrid)) : + TextureMappingZone::DefaultTopSurfaceContoningFlatSurfaceInfillMode; + } bool top_surface_contoning_layer_phase_enabled() const { return m_top_surface_contoning_layer_phase_checkbox != nullptr && @@ -3507,6 +3537,8 @@ private: m_top_surface_contoning_pattern_filaments_spin->Enable(contoning); if (m_top_surface_contoning_min_feature_spin != nullptr) m_top_surface_contoning_min_feature_spin->Enable(contoning); + if (m_top_surface_contoning_flat_surface_infill_choice != nullptr) + m_top_surface_contoning_flat_surface_infill_choice->Enable(contoning); if (m_top_surface_contoning_checkboxes_panel != nullptr) m_top_surface_contoning_checkboxes_panel->Show(contoning); if (m_top_surface_contoning_color_lower_surfaces_checkbox != nullptr) { @@ -3612,6 +3644,7 @@ private: wxSpinCtrl *m_top_surface_contoning_stack_layers_spin {nullptr}; wxSpinCtrl *m_top_surface_contoning_pattern_filaments_spin {nullptr}; wxSpinCtrlDouble *m_top_surface_contoning_min_feature_spin {nullptr}; + wxChoice *m_top_surface_contoning_flat_surface_infill_choice {nullptr}; wxPanel *m_top_surface_contoning_checkboxes_panel {nullptr}; wxCheckBox *m_top_surface_contoning_color_lower_surfaces_checkbox {nullptr}; wxCheckBox *m_top_surface_contoning_only_color_surface_infill_checkbox {nullptr}; @@ -8628,6 +8661,7 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager) updated.top_surface_contoning_replace_top_perimeters_with_infill, updated.top_surface_contoning_recolor_surrounding_perimeters, updated.top_surface_contoning_perimeter_mode, + updated.top_surface_contoning_flat_surface_infill_mode, updated.top_surface_contoning_layer_phase_enabled, updated.top_surface_contoning_varied_infill_angles_enabled, updated.top_surface_contoning_blue_noise_error_diffusion_enabled, @@ -8693,6 +8727,8 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager) updated.top_surface_contoning_recolor_surrounding_perimeters = dlg.top_surface_contoning_recolor_surrounding_perimeters(); updated.top_surface_contoning_perimeter_mode = dlg.top_surface_contoning_perimeter_mode(); + updated.top_surface_contoning_flat_surface_infill_mode = + dlg.top_surface_contoning_flat_surface_infill_mode(); updated.top_surface_contoning_layer_phase_enabled = dlg.top_surface_contoning_layer_phase_enabled(); updated.top_surface_contoning_varied_infill_angles_enabled = dlg.top_surface_contoning_varied_infill_angles_enabled(); @@ -9525,6 +9561,7 @@ struct Plater::priv bool m_is_slicing {false}; bool auto_reslice_pending {false}; bool auto_reslice_after_cancel {false}; + bool background_process_update_after_cancel {false}; bool m_is_publishing {false}; int m_is_RightClickInLeftUI{-1}; int m_cur_slice_plate; @@ -12918,7 +12955,8 @@ void Plater::priv::schedule_auto_reslice_if_needed() if (background_process.running() || m_is_slicing) { // Remember to restart once the current slice stops and cancel it now. auto_reslice_after_cancel = true; - background_process.stop(); + if (background_process.cancel_internal()) + notification_manager->set_slicing_progress_canceling(_u8L("Cancelling...")); return; } @@ -13391,6 +13429,13 @@ void Plater::priv::export_gcode(fs::path output_path, bool output_path_on_remova } unsigned int Plater::priv::update_restart_background_process(bool force_update_scene, bool force_update_preview) { + if (this->background_process.running()) { + if (this->background_process.cancel_internal()) + notification_manager->set_slicing_progress_canceling(_u8L("Cancelling...")); + background_process_update_after_cancel = true; + return 0; + } + bool switch_print = true; //BBS: judge whether can switch print or not if ((partplate_list.get_plate_count() > 1) && !this->background_process.can_switch_print()) @@ -15000,6 +15045,7 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt) m_slice_all_only_has_gcode = false; } + const bool internal_cancelled = this->background_process.is_internal_cancelled(); // Stop the background task, wait until the thread goes into the "Idle" state. // At this point of time the thread should be either finished or canceled, // so the following call just confirms, that the produced data were consumed. @@ -15045,7 +15091,10 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt) } if (evt.cancelled()) { BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", cancel event, status: %1%") % evt.status(); - this->notification_manager->set_slicing_progress_canceled(_u8L("Slicing Canceled")); + if (internal_cancelled) + this->notification_manager->set_slicing_progress_hidden(); + else + this->notification_manager->set_slicing_progress_canceled(_u8L("Slicing Canceled")); is_finished = true; } @@ -15188,6 +15237,10 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt) auto_reslice_after_cancel = false; schedule_auto_reslice_if_needed(); } + if (background_process_update_after_cancel) { + background_process_update_after_cancel = false; + schedule_background_process(); + } BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(", exit."); } diff --git a/src/slic3r/GUI/SlicingProgressNotification.cpp b/src/slic3r/GUI/SlicingProgressNotification.cpp index 550e16a8b16..46a4c686be2 100644 --- a/src/slic3r/GUI/SlicingProgressNotification.cpp +++ b/src/slic3r/GUI/SlicingProgressNotification.cpp @@ -146,6 +146,12 @@ void NotificationManager::SlicingProgressNotification::set_status_text(const std } } +void NotificationManager::SlicingProgressNotification::set_cancel_requested_text(const std::string& text) +{ + m_cancel_requested = true; + set_status_text(text); +} + void NotificationManager::SlicingProgressNotification::set_print_info(const std::string& info) { if (m_sp_state != SlicingProgressState::SP_COMPLETED) { @@ -169,8 +175,7 @@ void NotificationManager::SlicingProgressNotification::on_cancel_button() if (!m_cancel_callback()) { set_progress_state(SlicingProgressState::SP_NO_SLICING); } else { - m_cancel_requested = true; - set_status_text(_u8L("Cancelling...")); + set_cancel_requested_text(_u8L("Cancelling...")); } } } diff --git a/src/slic3r/GUI/SlicingProgressNotification.hpp b/src/slic3r/GUI/SlicingProgressNotification.hpp index 974e9eb19d1..db1972be62d 100644 --- a/src/slic3r/GUI/SlicingProgressNotification.hpp +++ b/src/slic3r/GUI/SlicingProgressNotification.hpp @@ -32,6 +32,7 @@ public: SlicingProgressState get_progress_state() { return m_sp_state; } // sets text of notification - call after setting progress state void set_status_text(const std::string& text); + void set_cancel_requested_text(const std::string& text); // sets cancel button callback void set_cancel_callback(std::function callback) { m_cancel_callback = callback; } bool has_cancel_callback() const { return m_cancel_callback != nullptr; }