From 2c06c81159f7aadd6ac20c7a7583c8f4959a5601 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Tue, 8 Oct 2024 18:03:36 +0200 Subject: [PATCH] SPE-2486: Refactor function apply_mm_segmentation() to prepare support for fuzzy skin painting. --- src/libslic3r/PrintObjectSlice.cpp | 188 +++++++++++++++++------------ 1 file changed, 108 insertions(+), 80 deletions(-) diff --git a/src/libslic3r/PrintObjectSlice.cpp b/src/libslic3r/PrintObjectSlice.cpp index 754a2d1ef0..7f65f0783c 100644 --- a/src/libslic3r/PrintObjectSlice.cpp +++ b/src/libslic3r/PrintObjectSlice.cpp @@ -39,7 +39,6 @@ #include "libslic3r/TriangleMeshSlicer.hpp" #include "libslic3r/Utils.hpp" #include "libslic3r/libslic3r.h" -#include "tcbspan/span.hpp" namespace Slic3r { @@ -580,34 +579,36 @@ void PrintObject::slice() template void apply_mm_segmentation(PrintObject &print_object, ThrowOnCancel throw_on_cancel) { - // Returns MMU segmentation based on painting in MMU segmentation gizmo + // Returns MM segmentation based on painting in MM segmentation gizmo std::vector> segmentation = multi_material_segmentation_by_painting(print_object, throw_on_cancel); assert(segmentation.size() == print_object.layer_count()); tbb::parallel_for( tbb::blocked_range(0, segmentation.size(), std::max(segmentation.size() / 128, size_t(1))), [&print_object, &segmentation, throw_on_cancel](const tbb::blocked_range &range) { const auto &layer_ranges = print_object.shared_regions()->layer_ranges; - double z = print_object.get_layer(range.begin())->slice_z; + double z = print_object.get_layer(int(range.begin()))->slice_z; auto it_layer_range = layer_range_first(layer_ranges, z); const size_t num_extruders = print_object.print()->config().nozzle_diameter.size(); + struct ByExtruder { ExPolygons expolygons; BoundingBox bbox; }; + std::vector by_extruder; struct ByRegion { ExPolygons expolygons; bool needs_merge { false }; }; std::vector by_region; - for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) { + for (size_t layer_id = range.begin(); layer_id < range.end(); ++layer_id) { throw_on_cancel(); - Layer *layer = print_object.get_layer(layer_id); - it_layer_range = layer_range_next(layer_ranges, it_layer_range, layer->slice_z); + Layer &layer = *print_object.get_layer(int(layer_id)); + it_layer_range = layer_range_next(layer_ranges, it_layer_range, layer.slice_z); const PrintObjectRegions::LayerRangeRegions &layer_range = *it_layer_range; // Gather per extruder expolygons. by_extruder.assign(num_extruders, ByExtruder()); - by_region.assign(layer->region_count(), ByRegion()); + by_region.assign(layer.region_count(), ByRegion()); bool layer_split = false; for (size_t extruder_id = 0; extruder_id < num_extruders; ++ extruder_id) { ByExtruder ®ion = by_extruder[extruder_id]; @@ -617,87 +618,114 @@ void apply_mm_segmentation(PrintObject &print_object, ThrowOnCancel throw_on_can layer_split = true; } } - if (! layer_split) + + if (!layer_split) continue; + // Split LayerRegions by by_extruder regions. // layer_range.painted_regions are sorted by extruder ID and parent PrintObject region ID. - auto it_painted_region = layer_range.painted_regions.begin(); - for (int region_id = 0; region_id < int(layer->region_count()); ++ region_id) - if (LayerRegion &layerm = *layer->get_region(region_id); ! layerm.slices().empty()) { - assert(layerm.region().print_object_region_id() == region_id); - const BoundingBox bbox = get_extents(layerm.slices().surfaces); - assert(it_painted_region < layer_range.painted_regions.end()); - // Find the first it_painted_region which overrides this region. - for (; layer_range.volume_regions[it_painted_region->parent].region->print_object_region_id() < region_id; ++ it_painted_region) - assert(it_painted_region != layer_range.painted_regions.end()); - assert(it_painted_region != layer_range.painted_regions.end()); - assert(layer_range.volume_regions[it_painted_region->parent].region == &layerm.region()); - // 1-based extruder ID - bool self_trimmed = false; - int self_extruder_id = -1; - for (int extruder_id = 1; extruder_id <= int(by_extruder.size()); ++ extruder_id) - if (ByExtruder &segmented = by_extruder[extruder_id - 1]; segmented.bbox.defined && bbox.overlap(segmented.bbox)) { - // Find the target region. - for (; int(it_painted_region->extruder_id) < extruder_id; ++ it_painted_region) - assert(it_painted_region != layer_range.painted_regions.end()); - assert(layer_range.volume_regions[it_painted_region->parent].region == &layerm.region() && int(it_painted_region->extruder_id) == extruder_id); - //FIXME Don't trim by self, it is not reliable. - if (&layerm.region() == it_painted_region->region) { - self_extruder_id = extruder_id; - continue; - } - // Steal from this region. - int target_region_id = it_painted_region->region->print_object_region_id(); - ExPolygons stolen = intersection_ex(layerm.slices().surfaces, segmented.expolygons); - if (! stolen.empty()) { - ByRegion &dst = by_region[target_region_id]; - if (dst.expolygons.empty()) { - dst.expolygons = std::move(stolen); - } else { - append(dst.expolygons, std::move(stolen)); - dst.needs_merge = true; - } - } -#if 0 - if (&layerm.region() == it_painted_region->region) - // Slices of this LayerRegion were trimmed by a MMU region of the same PrintRegion. - self_trimmed = true; -#endif - } - if (! self_trimmed) { - // Trim slices of this LayerRegion with all the MMU regions. - Polygons mine = to_polygons(std::move(layerm.slices().surfaces)); - for (auto &segmented : by_extruder) - if (&segmented - by_extruder.data() + 1 != self_extruder_id && segmented.bbox.defined && bbox.overlap(segmented.bbox)) { - mine = diff(mine, segmented.expolygons); - if (mine.empty()) - break; - } - // Filter out unprintable polygons produced by subtraction multi-material painted regions from layerm.region(). - // ExPolygon returned from multi-material segmentation does not precisely match ExPolygons in layerm.region() - // (because of preprocessing of the input regions in multi-material segmentation). Therefore, subtraction from - // layerm.region() could produce a huge number of small unprintable regions for the model's base extruder. - // This could, on some models, produce bulges with the model's base color (#7109). - if (! mine.empty()) - mine = opening(union_ex(mine), float(scale_(5 * EPSILON)), float(scale_(5 * EPSILON))); - if (! mine.empty()) { - ByRegion &dst = by_region[layerm.region().print_object_region_id()]; - if (dst.expolygons.empty()) { - dst.expolygons = union_ex(mine); - } else { - append(dst.expolygons, union_ex(mine)); - dst.needs_merge = true; - } + auto it_painted_region_begin = layer_range.painted_regions.cbegin(); + for (int parent_layer_region_idx = 0; parent_layer_region_idx < layer.region_count(); ++parent_layer_region_idx) { + if (it_painted_region_begin == layer_range.painted_regions.cend()) + continue; + + const LayerRegion &parent_layer_region = *layer.get_region(parent_layer_region_idx); + const PrintRegion &parent_print_region = parent_layer_region.region(); + assert(parent_print_region.print_object_region_id() == parent_layer_region_idx); + if (parent_layer_region.slices().empty()) + continue; + + // Find the first PaintedRegion, which overrides the parent PrintRegion. + auto it_first_painted_region = std::find_if(it_painted_region_begin, layer_range.painted_regions.cend(), [&layer_range, &parent_print_region](const auto &painted_region) { + return layer_range.volume_regions[painted_region.parent].region->print_object_region_id() == parent_print_region.print_object_region_id(); + }); + + if (it_first_painted_region == layer_range.painted_regions.cend()) + continue; // This LayerRegion isn't overrides by any PaintedRegion. + + assert(&parent_print_region == layer_range.volume_regions[it_first_painted_region->parent].region); + + // Find the first PaintedRegion with different parent PrintRegion. + auto it_last_painted_region = std::find_if(it_first_painted_region, layer_range.painted_regions.cend(), [&it_first_painted_region](const auto &painted_region) { + return painted_region.parent != it_first_painted_region->parent; + }); + + // Update the beginning PaintedRegion iterator for the next iteration. + it_painted_region_begin = it_last_painted_region; + + const BoundingBox parent_layer_region_bbox = get_extents(parent_layer_region.slices().surfaces); + bool self_trimmed = false; + int self_extruder_id = -1; // 1-based extruder ID + for (auto it_painted_region = it_first_painted_region; it_painted_region != it_last_painted_region; ++it_painted_region) { + const int extruder_id = int(it_painted_region->extruder_id); // 1-based extruder ID + assert(extruder_id > 0 && (extruder_id - 1) < int(by_extruder.size())); + assert(layer_range.volume_regions[it_painted_region->parent].region == &parent_print_region); + + const ByExtruder &segmented = by_extruder[extruder_id - 1]; + if (!segmented.bbox.defined || !parent_layer_region_bbox.overlap(segmented.bbox)) + continue; + + // FIXME: Don't trim by self, it is not reliable. + if (it_painted_region->region == &parent_print_region) { + self_extruder_id = extruder_id; + continue; + } + + // Steal from this region. + int target_region_id = it_painted_region->region->print_object_region_id(); + ExPolygons stolen = intersection_ex(parent_layer_region.slices().surfaces, segmented.expolygons); + if (!stolen.empty()) { + ByRegion &dst = by_region[target_region_id]; + if (dst.expolygons.empty()) { + dst.expolygons = std::move(stolen); + } else { + append(dst.expolygons, std::move(stolen)); + dst.needs_merge = true; } } } + + if (!self_trimmed) { + // Trim slices of this LayerRegion with all the MM regions. + Polygons mine = to_polygons(parent_layer_region.slices().surfaces); + for (auto &segmented : by_extruder) { + if (&segmented - by_extruder.data() + 1 != self_extruder_id && segmented.bbox.defined && parent_layer_region_bbox.overlap(segmented.bbox)) { + mine = diff(mine, segmented.expolygons); + if (mine.empty()) + break; + } + } + + // Filter out unprintable polygons produced by subtraction multi-material painted regions from layerm.region(). + // ExPolygon returned from multi-material segmentation does not precisely match ExPolygons in layerm.region() + // (because of preprocessing of the input regions in multi-material segmentation). Therefore, subtraction from + // layerm.region() could produce a huge number of small unprintable regions for the model's base extruder. + // This could, on some models, produce bulges with the model's base color (#7109). + if (!mine.empty()) { + mine = opening(union_ex(mine), scaled(5. * EPSILON), scaled(5. * EPSILON)); + } + + if (!mine.empty()) { + ByRegion &dst = by_region[parent_print_region.print_object_region_id()]; + if (dst.expolygons.empty()) { + dst.expolygons = union_ex(mine); + } else { + append(dst.expolygons, union_ex(mine)); + dst.needs_merge = true; + } + } + } + } + // Re-create Surfaces of LayerRegions. - for (size_t region_id = 0; region_id < layer->region_count(); ++ region_id) { + for (int region_id = 0; region_id < layer.region_count(); ++region_id) { ByRegion &src = by_region[region_id]; - if (src.needs_merge) + if (src.needs_merge) { // Multiple regions were merged into one. - src.expolygons = closing_ex(src.expolygons, float(scale_(10 * EPSILON))); - layer->get_region(region_id)->m_slices.set(std::move(src.expolygons), stInternal); + src.expolygons = closing_ex(src.expolygons, scaled(10. * EPSILON)); + } + + layer.get_region(region_id)->m_slices.set(std::move(src.expolygons), stInternal); } } }); @@ -752,7 +780,7 @@ void PrintObject::slice_volumes() m_layers.back()->upper_layer = nullptr; m_print->throw_if_canceled(); - // Is any ModelVolume MMU painted? + // Is any ModelVolume multi-material painted? if (m_print->config().nozzle_diameter.size() > 1 && this->model_object()->is_mm_painted()) { // If XY Size compensation is also enabled, notify the user that XY Size compensation // would not be used because the object is multi-material painted.