From b572588fc5b646b7f1cb80f6fde41856f1070f79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Fri, 5 Nov 2021 10:07:21 +0100 Subject: [PATCH 1/3] Small refactoring of storing colored polygons in multi-material segmentation. Previously, colored polygons were stored so that each polygon had a color assigned to it, which made it difficult to perform operations like union or so on all polygons of the same color. Now polygons are stored grouped by their assigned color/extruder. --- src/libslic3r/MultiMaterialSegmentation.cpp | 133 ++++++++++---------- src/libslic3r/MultiMaterialSegmentation.hpp | 2 +- src/libslic3r/PrintObjectSlice.cpp | 6 +- 3 files changed, 70 insertions(+), 71 deletions(-) diff --git a/src/libslic3r/MultiMaterialSegmentation.cpp b/src/libslic3r/MultiMaterialSegmentation.cpp index 11e263299..ddc17713b 100644 --- a/src/libslic3r/MultiMaterialSegmentation.cpp +++ b/src/libslic3r/MultiMaterialSegmentation.cpp @@ -1113,7 +1113,7 @@ static inline Polygon to_polygon(const std::vector &lines) // It iterates through all nodes on the border between two different colors, and from this point, // start selection always left most edges for every node to construct CCW polygons. // Assumes that graph is planar (without self-intersection edges) -static std::vector> extract_colored_segments(const MMU_Graph &graph) +static std::vector extract_colored_segments(const MMU_Graph &graph, const size_t num_extruders) { std::vector used_arcs(graph.arcs.size(), false); // When there is no next arc, then is returned original_arc or edge with is marked as used @@ -1153,7 +1153,7 @@ static std::vector> extract_colored_segments(const MM return std::all_of(node.arc_idxs.cbegin(), node.arc_idxs.cend(), [&used_arcs](const size_t &arc_idx) -> bool { return used_arcs[arc_idx]; }); }; - std::vector> polygons_segments; + std::vector expolygons_segments(num_extruders + 1); for (size_t node_idx = 0; node_idx < graph.all_border_points; ++node_idx) { const MMU_Graph::Node &node = graph.nodes[node_idx]; @@ -1183,12 +1183,11 @@ static std::vector> extract_colored_segments(const MM p_arc = &next; } while (graph.nodes[p_arc->to_idx].point != start_p || !all_arc_used(graph.nodes[p_arc->to_idx])); - Polygon poly = to_polygon(face_lines); - if (poly.is_counter_clockwise() && poly.is_valid()) - polygons_segments.emplace_back(poly, arc.color); + if (Polygon poly = to_polygon(face_lines); poly.is_counter_clockwise() && poly.is_valid()) + expolygons_segments[arc.color].emplace_back(std::move(poly)); } } - return polygons_segments; + return expolygons_segments; } // Used in remove_multiple_edges_in_vertices() @@ -1269,21 +1268,20 @@ static void remove_multiple_edges_in_vertices(MMU_Graph &graph, const std::vecto } } -static void cut_segmented_layers(const std::vector &input_expolygons, - std::vector>> &segmented_regions, - const float cut_width, - const std::function &throw_on_cancel_callback) +static void cut_segmented_layers(const std::vector &input_expolygons, + std::vector> &segmented_regions, + const float cut_width, + const std::function &throw_on_cancel_callback) { BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - cutting segmented layers in parallel - begin"; tbb::parallel_for(tbb::blocked_range(0, segmented_regions.size()),[&segmented_regions, &input_expolygons, &cut_width, &throw_on_cancel_callback](const tbb::blocked_range& range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { throw_on_cancel_callback(); - std::vector> segmented_regions_cuts; - for (const std::pair &colored_expoly : segmented_regions[layer_idx]) { - ExPolygons cut_colored_expoly = diff_ex(colored_expoly.first, offset_ex(input_expolygons[layer_idx], cut_width)); - for (ExPolygon &expoly : cut_colored_expoly) - segmented_regions_cuts.emplace_back(std::move(expoly), colored_expoly.second); - } + const size_t num_extruders_plus_one = segmented_regions[layer_idx].size(); + std::vector segmented_regions_cuts(num_extruders_plus_one); // Indexed by extruder_id + for (size_t extruder_idx = 0; extruder_idx < num_extruders_plus_one; ++extruder_idx) + if (const ExPolygons &ex_polygons = segmented_regions[layer_idx][extruder_idx]; !ex_polygons.empty()) + segmented_regions_cuts[extruder_idx] = diff_ex(ex_polygons, offset_ex(input_expolygons[layer_idx], cut_width)); segmented_regions[layer_idx] = std::move(segmented_regions_cuts); } }); // end of parallel_for @@ -1323,7 +1321,7 @@ static inline std::vector> mmu_segmentation_top_and_bott // Project upwards pointing painted triangles over top surfaces, // project downards pointing painted triangles over bottom surfaces. std::vector> top_raw(num_extruders), bottom_raw(num_extruders); - std::vector zs = zs_from_layers(print_object.layers()); + std::vector zs = zs_from_layers(layers); Transform3d object_trafo = print_object.trafo_centered(); #ifdef MMU_SEGMENTATION_DEBUG_TOP_BOTTOM @@ -1532,31 +1530,35 @@ static inline std::vector> mmu_segmentation_top_and_bott return triangles_by_color_merged; } -static std::vector>> merge_segmented_layers( - const std::vector>> &segmented_regions, - std::vector> &&top_and_bottom_layers, - const std::function &throw_on_cancel_callback) +static std::vector> merge_segmented_layers( + const std::vector> &segmented_regions, + std::vector> &&top_and_bottom_layers, + const size_t num_extruders, + const std::function &throw_on_cancel_callback) { - std::vector>> segmented_regions_merged(segmented_regions.size()); + const size_t num_layers = segmented_regions.size(); + std::vector> segmented_regions_merged(num_layers); + segmented_regions_merged.assign(num_layers, std::vector(num_extruders)); + assert(num_extruders + 1 == top_and_bottom_layers.size()); BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - merging segmented layers in parallel - begin"; - tbb::parallel_for(tbb::blocked_range(0, segmented_regions.size()), [&segmented_regions, &top_and_bottom_layers, &segmented_regions_merged, &throw_on_cancel_callback](const tbb::blocked_range &range) { + tbb::parallel_for(tbb::blocked_range(0, num_layers), [&segmented_regions, &top_and_bottom_layers, &segmented_regions_merged, &num_extruders, &throw_on_cancel_callback](const tbb::blocked_range &range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { - for (const std::pair &colored_expoly : segmented_regions[layer_idx]) { + assert(segmented_regions[layer_idx].size() == num_extruders + 1); + // Zero is skipped because it is the default color of the volume + for (size_t extruder_id = 1; extruder_id < num_extruders + 1; ++extruder_id) { throw_on_cancel_callback(); - // Zero is the default color of the volume. - if(colored_expoly.second == 0) - continue; - ExPolygons cut_colored_expoly = {colored_expoly.first}; - for (const std::vector &top_and_bottom_layer : top_and_bottom_layers) - cut_colored_expoly = diff_ex(cut_colored_expoly, top_and_bottom_layer[layer_idx]); - for (ExPolygon &ex_poly : cut_colored_expoly) - segmented_regions_merged[layer_idx].emplace_back(std::move(ex_poly), colored_expoly.second - 1); - } + if (!segmented_regions[layer_idx][extruder_id].empty()) { + ExPolygons segmented_regions_trimmed = segmented_regions[layer_idx][extruder_id]; + for (const std::vector &top_and_bottom_by_extruder : top_and_bottom_layers) + if (!top_and_bottom_by_extruder[layer_idx].empty() && !segmented_regions_trimmed.empty()) + segmented_regions_trimmed = diff_ex(segmented_regions_trimmed, top_and_bottom_by_extruder[layer_idx]); - for (size_t color_idx = 1; color_idx < top_and_bottom_layers.size(); ++color_idx) - for (ExPolygon &expoly : top_and_bottom_layers[color_idx][layer_idx]) - segmented_regions_merged[layer_idx].emplace_back(std::move(expoly), color_idx - 1); + segmented_regions_merged[layer_idx][extruder_id - 1] = std::move(segmented_regions_trimmed); + } + + append(segmented_regions_merged[layer_idx][extruder_id - 1], top_and_bottom_layers[extruder_id][layer_idx]); + } } }); // end of parallel_for BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - merging segmented layers in parallel - end"; @@ -1565,7 +1567,7 @@ static std::vector>> merge_segmented_la } #ifdef MMU_SEGMENTATION_DEBUG_REGIONS -static void export_regions_to_svg(const std::string &path, const std::vector> ®ions, const ExPolygons &lslices) +static void export_regions_to_svg(const std::string &path, const std::vector ®ions, const ExPolygons &lslices) { const std::vector colors = {"blue", "cyan", "red", "orange", "magenta", "pink", "purple", "yellow"}; coordf_t stroke_width = scale_(0.05); @@ -1574,12 +1576,12 @@ static void export_regions_to_svg(const std::string &path, const std::vector ®ion : regions) { - int region_color = int(region.second); - if (region_color >= 0 && region_color < int(colors.size())) - svg.draw(region.first, colors[region_color]); + for (const ExPolygons &by_extruder : regions) { + size_t extrude_idx = &by_extruder - ®ions.front(); + if (extrude_idx >= 0 && extrude_idx < int(colors.size())) + svg.draw(by_extruder, colors[extrude_idx], stroke_width); else - svg.draw(region.first, "black"); + svg.draw(by_extruder, "black", stroke_width); } } #endif // MMU_SEGMENTATION_DEBUG_REGIONS @@ -1667,20 +1669,23 @@ static bool has_layer_only_one_color(const std::vector> return true; } -std::vector>> multi_material_segmentation_by_painting(const PrintObject &print_object, const std::function &throw_on_cancel_callback) +std::vector> multi_material_segmentation_by_painting(const PrintObject &print_object, const std::function &throw_on_cancel_callback) { - std::vector>> segmented_regions(print_object.layers().size()); - std::vector> painted_lines(print_object.layers().size()); - std::array painted_lines_mutex; - std::vector edge_grids(print_object.layers().size()); - const ConstLayerPtrsAdaptor layers = print_object.layers(); - std::vector input_expolygons(layers.size()); + const size_t num_extruders = print_object.print()->config().nozzle_diameter.size(); + const size_t num_layers = print_object.layers().size(); + std::vector> segmented_regions(num_layers); + segmented_regions.assign(num_layers, std::vector(num_extruders + 1)); + std::vector> painted_lines(num_layers); + std::array painted_lines_mutex; + std::vector edge_grids(num_layers); + const ConstLayerPtrsAdaptor layers = print_object.layers(); + std::vector input_expolygons(num_layers); throw_on_cancel_callback(); // Merge all regions and remove small holes BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - slices preparation in parallel - begin"; - tbb::parallel_for(tbb::blocked_range(0, layers.size()), [&layers, &input_expolygons, &throw_on_cancel_callback](const tbb::blocked_range &range) { + tbb::parallel_for(tbb::blocked_range(0, num_layers), [&layers, &input_expolygons, &throw_on_cancel_callback](const tbb::blocked_range &range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { throw_on_cancel_callback(); ExPolygons ex_polygons; @@ -1711,7 +1716,7 @@ std::vector>> multi_material_segmentati }); // end of parallel_for BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - slices preparation in parallel - end"; - for (size_t layer_idx = 0; layer_idx < layers.size(); ++layer_idx) { + for (size_t layer_idx = 0; layer_idx < num_layers; ++layer_idx) { throw_on_cancel_callback(); BoundingBox bbox(get_extents(layers[layer_idx]->regions())); bbox.merge(get_extents(input_expolygons[layer_idx])); @@ -1723,8 +1728,7 @@ std::vector>> multi_material_segmentati BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - projection of painted triangles - begin"; for (const ModelVolume *mv : print_object.model_object()->volumes) { - const size_t num_extruders = print_object.print()->config().nozzle_diameter.size() + 1; - tbb::parallel_for(tbb::blocked_range(1, num_extruders), [&mv, &print_object, &edge_grids, &painted_lines, &painted_lines_mutex, &input_expolygons, &throw_on_cancel_callback](const tbb::blocked_range &range) { + tbb::parallel_for(tbb::blocked_range(1, num_extruders + 1), [&mv, &print_object, &layers, &edge_grids, &painted_lines, &painted_lines_mutex, &input_expolygons, &throw_on_cancel_callback](const tbb::blocked_range &range) { for (size_t extruder_idx = range.begin(); extruder_idx < range.end(); ++extruder_idx) { throw_on_cancel_callback(); const indexed_triangle_set custom_facets = mv->mmu_segmentation_facets.get_facets(*mv, EnforcerBlockerType(extruder_idx)); @@ -1732,7 +1736,7 @@ std::vector>> multi_material_segmentati continue; const Transform3f tr = print_object.trafo().cast() * mv->get_matrix().cast(); - tbb::parallel_for(tbb::blocked_range(0, custom_facets.indices.size()), [&tr, &custom_facets, &print_object, &edge_grids, &input_expolygons, &painted_lines, &painted_lines_mutex, &extruder_idx](const tbb::blocked_range &range) { + tbb::parallel_for(tbb::blocked_range(0, custom_facets.indices.size()), [&tr, &custom_facets, &print_object, &layers, &edge_grids, &input_expolygons, &painted_lines, &painted_lines_mutex, &extruder_idx](const tbb::blocked_range &range) { for (size_t facet_idx = range.begin(); facet_idx < range.end(); ++facet_idx) { float min_z = std::numeric_limits::max(); float max_z = std::numeric_limits::lowest(); @@ -1748,15 +1752,15 @@ std::vector>> multi_material_segmentati std::sort(facet.begin(), facet.end(), [](const Vec3f &p1, const Vec3f &p2) { return p1.z() < p2.z(); }); // Find lowest slice not below the triangle. - auto first_layer = std::upper_bound(print_object.layers().begin(), print_object.layers().end(), float(min_z - EPSILON), + auto first_layer = std::upper_bound(layers.begin(), layers.end(), float(min_z - EPSILON), [](float z, const Layer *l1) { return z < l1->slice_z; }); - auto last_layer = std::upper_bound(print_object.layers().begin(), print_object.layers().end(), float(max_z + EPSILON), + auto last_layer = std::upper_bound(layers.begin(), layers.end(), float(max_z + EPSILON), [](float z, const Layer *l1) { return z < l1->slice_z; }); --last_layer; for (auto layer_it = first_layer; layer_it != (last_layer + 1); ++layer_it) { const Layer *layer = *layer_it; - size_t layer_idx = layer_it - print_object.layers().begin(); + size_t layer_idx = layer_it - layers.begin(); if (input_expolygons[layer_idx].empty() || facet[0].z() > layer->slice_z || layer->slice_z > facet[2].z()) continue; @@ -1799,7 +1803,7 @@ std::vector>> multi_material_segmentati << std::count_if(painted_lines.begin(), painted_lines.end(), [](const std::vector &pl) { return !pl.empty(); }); BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - layers segmentation in parallel - begin"; - tbb::parallel_for(tbb::blocked_range(0, print_object.layers().size()), [&edge_grids, &input_expolygons, &painted_lines, &segmented_regions, &throw_on_cancel_callback](const tbb::blocked_range &range) { + tbb::parallel_for(tbb::blocked_range(0, num_layers), [&edge_grids, &input_expolygons, &painted_lines, &segmented_regions, &num_extruders, &throw_on_cancel_callback](const tbb::blocked_range &range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { throw_on_cancel_callback(); if (!painted_lines[layer_idx].empty()) { @@ -1832,8 +1836,7 @@ std::vector>> multi_material_segmentati assert(!color_poly.front().empty()); if (has_layer_only_one_color(color_poly)) { // If the whole layer is painted using the same color, it is not needed to construct a Voronoi diagram for the segmentation of this layer. - for (const ExPolygon &ex_polygon : input_expolygons[layer_idx]) - segmented_regions[layer_idx].emplace_back(ex_polygon, size_t(color_poly.front().front().color)); + segmented_regions[layer_idx][size_t(color_poly.front().front().color)] = input_expolygons[layer_idx]; } else { MMU_Graph graph = build_graph(layer_idx, color_poly); remove_multiple_edges_in_vertices(graph, color_poly); @@ -1846,9 +1849,7 @@ std::vector>> multi_material_segmentati } #endif // MMU_SEGMENTATION_DEBUG_GRAPH - std::vector> segmentation = extract_colored_segments(graph); - for (std::pair ®ion : segmentation) - segmented_regions[layer_idx].emplace_back(std::move(region)); + segmented_regions[layer_idx] = extract_colored_segments(graph, num_extruders); } #ifdef MMU_SEGMENTATION_DEBUG_REGIONS @@ -1868,11 +1869,11 @@ std::vector>> multi_material_segmentati throw_on_cancel_callback(); } -// return segmented_regions; - std::vector> top_and_bottom_layers = mmu_segmentation_top_and_bottom_layers(print_object, input_expolygons, throw_on_cancel_callback); + // The first index is extruder number (includes default extruder), and the second one is layer number + std::vector> top_and_bottom_layers = mmu_segmentation_top_and_bottom_layers(print_object, input_expolygons, throw_on_cancel_callback); throw_on_cancel_callback(); - std::vector>> segmented_regions_merged = merge_segmented_layers(segmented_regions, std::move(top_and_bottom_layers), throw_on_cancel_callback); + std::vector> segmented_regions_merged = merge_segmented_layers(segmented_regions, std::move(top_and_bottom_layers), num_extruders, throw_on_cancel_callback); throw_on_cancel_callback(); #ifdef MMU_SEGMENTATION_DEBUG_REGIONS diff --git a/src/libslic3r/MultiMaterialSegmentation.hpp b/src/libslic3r/MultiMaterialSegmentation.hpp index 07767111a..4efdc6951 100644 --- a/src/libslic3r/MultiMaterialSegmentation.hpp +++ b/src/libslic3r/MultiMaterialSegmentation.hpp @@ -11,7 +11,7 @@ class PrintObject; class ExPolygon; // Returns MMU segmentation based on painting in MMU segmentation gizmo -std::vector>> multi_material_segmentation_by_painting(const PrintObject &print_object, const std::function &throw_on_cancel_callback); +std::vector> multi_material_segmentation_by_painting(const PrintObject &print_object, const std::function &throw_on_cancel_callback); } // namespace Slic3r diff --git a/src/libslic3r/PrintObjectSlice.cpp b/src/libslic3r/PrintObjectSlice.cpp index e2844a624..a8936c32e 100644 --- a/src/libslic3r/PrintObjectSlice.cpp +++ b/src/libslic3r/PrintObjectSlice.cpp @@ -538,7 +538,7 @@ template static inline void apply_mm_segmentation(PrintObject &print_object, ThrowOnCancel throw_on_cancel) { // Returns MMU segmentation based on painting in MMU segmentation gizmo - std::vector>> segmentation = multi_material_segmentation_by_painting(print_object, throw_on_cancel); + 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))), @@ -568,9 +568,7 @@ static inline void apply_mm_segmentation(PrintObject &print_object, ThrowOnCance bool layer_split = false; for (size_t extruder_id = 0; extruder_id < num_extruders; ++ extruder_id) { ByExtruder ®ion = by_extruder[extruder_id]; - for (const std::pair &colored_polygon : segmentation[layer_id]) - if (colored_polygon.second == extruder_id) - region.expolygons.emplace_back(std::move(colored_polygon.first)); + append(region.expolygons, std::move(segmentation[layer_id][extruder_id])); if (! region.expolygons.empty()) { region.bbox = get_extents(region.expolygons); layer_split = true; From f5a6e532982b42c36f3a39b02353c5a44325326f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Fri, 5 Nov 2021 10:12:40 +0100 Subject: [PATCH 2/3] Fix of #7235 (Dimples in external perimeter after multi-material segmentation) --- src/libslic3r/MultiMaterialSegmentation.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/MultiMaterialSegmentation.cpp b/src/libslic3r/MultiMaterialSegmentation.cpp index ddc17713b..ba758d8a2 100644 --- a/src/libslic3r/MultiMaterialSegmentation.cpp +++ b/src/libslic3r/MultiMaterialSegmentation.cpp @@ -1557,7 +1557,14 @@ static std::vector> merge_segmented_layers( segmented_regions_merged[layer_idx][extruder_id - 1] = std::move(segmented_regions_trimmed); } - append(segmented_regions_merged[layer_idx][extruder_id - 1], top_and_bottom_layers[extruder_id][layer_idx]); + if (!top_and_bottom_layers[extruder_id][layer_idx].empty()) { + bool was_top_and_bottom_empty = segmented_regions_merged[layer_idx][extruder_id - 1].empty(); + append(segmented_regions_merged[layer_idx][extruder_id - 1], top_and_bottom_layers[extruder_id][layer_idx]); + + // Remove dimples (#7235) appearing after merging side segmentation of the model with tops and bottoms painted layers. + if (!was_top_and_bottom_empty) + segmented_regions_merged[layer_idx][extruder_id - 1] = offset2_ex(union_ex(segmented_regions_merged[layer_idx][extruder_id - 1]), SCALED_EPSILON, -SCALED_EPSILON); + } } } }); // end of parallel_for From e13be902836e52b0f859be00dbe39fd96d5b035a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Fri, 5 Nov 2021 10:18:11 +0100 Subject: [PATCH 3/3] Fix of #7109 (Bulges with model's base color on multi-material painted models) --- src/libslic3r/PrintObjectSlice.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/libslic3r/PrintObjectSlice.cpp b/src/libslic3r/PrintObjectSlice.cpp index a8936c32e..84b212938 100644 --- a/src/libslic3r/PrintObjectSlice.cpp +++ b/src/libslic3r/PrintObjectSlice.cpp @@ -630,6 +630,13 @@ static inline void apply_mm_segmentation(PrintObject &print_object, ThrowOnCance 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()) {