diff --git a/src/libslic3r/MultiMaterialSegmentation.cpp b/src/libslic3r/MultiMaterialSegmentation.cpp index a44409cd4..122a0308c 100644 --- a/src/libslic3r/MultiMaterialSegmentation.cpp +++ b/src/libslic3r/MultiMaterialSegmentation.cpp @@ -149,18 +149,6 @@ struct PaintedLineVisitor static inline const double append_threshold2 = Slic3r::sqr(append_threshold); }; -static std::vector to_colored_lines(const EdgeGrid::Contour &contour, int color) -{ - std::vector lines; - if (contour.num_segments() > 2) { - lines.reserve(contour.num_segments()); - for (auto it = contour.begin(); it != contour.end() - 1; ++it) - lines.push_back({Line(*it, *(it + 1)), color}); - lines.push_back({Line(contour.back(), contour.front()), color}); - } - return lines; -} - static Polygon colored_points_to_polygon(const std::vector &lines) { Polygon out; @@ -244,25 +232,18 @@ static std::vector>> get_all_segments(cons return all_segments; } -static std::vector colorize_line(const Line & line_to_process, - const size_t start_idx, - const size_t end_idx, - std::vector &painted_lines) +static std::vector filter_painted_lines(const Line &line_to_process, const size_t start_idx, const size_t end_idx, const std::vector &painted_lines) { - std::vector internal_painted; - for (size_t line_idx = start_idx; line_idx <= end_idx; ++line_idx) - internal_painted.emplace_back(painted_lines[line_idx]); - const int filter_eps_value = scale_(0.1f); std::vector filtered_lines; - filtered_lines.emplace_back(internal_painted.front()); - for (size_t line_idx = 1; line_idx < internal_painted.size(); ++line_idx) { + filtered_lines.emplace_back(painted_lines[start_idx]); + for (size_t line_idx = start_idx + 1; line_idx <= end_idx; ++line_idx) { // line_to_process is already all colored. Skip another possible duplicate coloring. if(filtered_lines.back().projected_line.b == line_to_process.b) break; PaintedLine &prev = filtered_lines.back(); - PaintedLine &curr = internal_painted[line_idx]; + const PaintedLine &curr = painted_lines[line_idx]; double prev_length = prev.projected_line.length(); double curr_dist_start = (curr.projected_line.a - prev.projected_line.a).cast().norm(); @@ -280,31 +261,84 @@ static std::vector colorize_line(const Line & line_to_ } } else { double curr_dist_end = (curr.projected_line.b - prev.projected_line.a).cast().norm(); - if (curr_dist_end <= prev_length) { - } else { - if (prev.color == curr.color) { + if (curr_dist_end > prev_length) { + if (prev.color == curr.color) prev.projected_line.b = curr.projected_line.b; - } else { - curr.projected_line.a = prev.projected_line.b; - filtered_lines.emplace_back(curr); - } + else + filtered_lines.push_back({curr.contour_idx, curr.line_idx, Line{prev.projected_line.b, curr.projected_line.b}, curr.color}); } } } - std::vector final_lines; - double dist_to_start = (filtered_lines.front().projected_line.a - line_to_process.a).cast().norm(); - if (dist_to_start <= filter_eps_value) { + if (double dist_to_start = (filtered_lines.front().projected_line.a - line_to_process.a).cast().norm(); dist_to_start <= filter_eps_value) filtered_lines.front().projected_line.a = line_to_process.a; - final_lines.push_back({filtered_lines.front().projected_line, filtered_lines.front().color}); - } else { - final_lines.push_back({Line(line_to_process.a, filtered_lines.front().projected_line.a), 0}); - final_lines.push_back({filtered_lines.front().projected_line, filtered_lines.front().color}); + + if (double dist_to_end = (filtered_lines.back().projected_line.b - line_to_process.b).cast().norm(); dist_to_end <= filter_eps_value) + filtered_lines.back().projected_line.b = line_to_process.b; + + return filtered_lines; +} + +static std::vector> post_process_painted_lines(const std::vector &contours, std::vector &&painted_lines) +{ + if (painted_lines.empty()) + return {}; + + auto comp = [&contours](const PaintedLine &first, const PaintedLine &second) { + Point first_start_p = contours[first.contour_idx].segment_start(first.line_idx); + return first.contour_idx < second.contour_idx || + (first.contour_idx == second.contour_idx && + (first.line_idx < second.line_idx || + (first.line_idx == second.line_idx && + ((first.projected_line.a - first_start_p).cast().squaredNorm() < (second.projected_line.a - first_start_p).cast().squaredNorm() || + ((first.projected_line.a - first_start_p).cast().squaredNorm() == (second.projected_line.a - first_start_p).cast().squaredNorm() && + (first.projected_line.b - first.projected_line.a).cast().squaredNorm() < (second.projected_line.b - second.projected_line.a).cast().squaredNorm()))))); + }; + std::sort(painted_lines.begin(), painted_lines.end(), comp); + + std::vector> filtered_painted_lines(contours.size()); + size_t prev_painted_line_idx = 0; + for (size_t curr_painted_line_idx = 0; curr_painted_line_idx < painted_lines.size(); ++curr_painted_line_idx) { + size_t next_painted_line_idx = curr_painted_line_idx + 1; + if (next_painted_line_idx >= painted_lines.size() || painted_lines[curr_painted_line_idx].contour_idx != painted_lines[next_painted_line_idx].contour_idx || painted_lines[curr_painted_line_idx].line_idx != painted_lines[next_painted_line_idx].line_idx) { + const PaintedLine &start_line = painted_lines[prev_painted_line_idx]; + const Line &line_to_process = contours[start_line.contour_idx].get_segment(start_line.line_idx); + Slic3r::append(filtered_painted_lines[painted_lines[curr_painted_line_idx].contour_idx], filter_painted_lines(line_to_process, prev_painted_line_idx, curr_painted_line_idx, painted_lines)); + prev_painted_line_idx = next_painted_line_idx; + } } - for (size_t line_idx = 1; line_idx < filtered_lines.size(); ++line_idx) { - ColoredLine &prev = final_lines.back(); - PaintedLine &curr = filtered_lines[line_idx]; + return filtered_painted_lines; +} + +#ifndef NDEBUG +static bool are_lines_connected(const std::vector &colored_lines) +{ + for (size_t line_idx = 1; line_idx < colored_lines.size(); ++line_idx) + if (colored_lines[line_idx - 1].line.b != colored_lines[line_idx].line.a) + return false; + return true; +} +#endif + +static std::vector colorize_line(const Line &line_to_process, + const size_t start_idx, + const size_t end_idx, + const std::vector &painted_contour) +{ + assert(start_idx < painted_contour.size() && end_idx < painted_contour.size() && start_idx <= end_idx); + assert(std::all_of(painted_contour.begin() + start_idx, painted_contour.begin() + end_idx + 1, [&painted_contour, &start_idx](const auto &p_line) { return painted_contour[start_idx].line_idx == p_line.line_idx; })); + + const int filter_eps_value = scale_(0.1f); + std::vector final_lines; + const PaintedLine &first_line = painted_contour[start_idx]; + if (double dist_to_start = (first_line.projected_line.a - line_to_process.a).cast().norm(); dist_to_start > filter_eps_value) + final_lines.push_back({Line(line_to_process.a, first_line.projected_line.a), 0}); + final_lines.push_back({first_line.projected_line, first_line.color}); + + for (size_t line_idx = start_idx + 1; line_idx <= end_idx; ++line_idx) { + ColoredLine &prev = final_lines.back(); + const PaintedLine &curr = painted_contour[line_idx]; double line_dist = (curr.projected_line.a - prev.line.b).cast().norm(); if (line_dist <= filter_eps_value) { @@ -320,18 +354,16 @@ static std::vector colorize_line(const Line & line_to_ } } - double dist_to_end = (final_lines.back().line.b - line_to_process.b).cast().norm(); - if (dist_to_end <= filter_eps_value) - final_lines.back().line.b = line_to_process.b; - else + // If there is non-painted space, then inserts line painted by a default color. + if (double dist_to_end = (final_lines.back().line.b - line_to_process.b).cast().norm(); dist_to_end > filter_eps_value) final_lines.push_back({Line(final_lines.back().line.b, line_to_process.b), 0}); - for (size_t line_idx = 1; line_idx < final_lines.size(); ++line_idx) - assert(final_lines[line_idx - 1].line.b == final_lines[line_idx].line.a); + // Make sure all the lines are connected. + assert(are_lines_connected(final_lines)); for (size_t line_idx = 2; line_idx < final_lines.size(); ++line_idx) { const ColoredLine &line_0 = final_lines[line_idx - 2]; - ColoredLine & line_1 = final_lines[line_idx - 1]; + ColoredLine &line_1 = final_lines[line_idx - 1]; const ColoredLine &line_2 = final_lines[line_idx - 0]; if (line_0.color == line_2.color && line_0.color != line_1.color) @@ -351,52 +383,25 @@ static std::vector colorize_line(const Line & line_to_ final_lines = colored_lines_simple; - if (final_lines.size() > 1) { + if (final_lines.size() > 1) if (final_lines.front().color != final_lines[1].color && final_lines.front().line.length() <= scale_(0.2)) { final_lines[1].line.a = final_lines.front().line.a; final_lines.erase(final_lines.begin()); } - } - if (final_lines.size() > 1) { + if (final_lines.size() > 1) if (final_lines.back().color != final_lines[final_lines.size() - 2].color && final_lines.back().line.length() <= scale_(0.2)) { final_lines[final_lines.size() - 2].line.b = final_lines.back().line.b; final_lines.pop_back(); } - } return final_lines; } -static std::vector colorize_polygon(const EdgeGrid::Contour &contour, const size_t start_idx, const size_t end_idx, std::vector &painted_lines) -{ - std::vector new_lines; - new_lines.reserve(end_idx - start_idx + 1); - for (size_t idx = 0; idx < painted_lines[start_idx].line_idx; ++idx) - new_lines.emplace_back(ColoredLine{contour.get_segment(idx), 0}); - - for (size_t first_idx = start_idx; first_idx <= end_idx; ++first_idx) { - size_t second_idx = first_idx; - while (second_idx <= end_idx && painted_lines[first_idx].line_idx == painted_lines[second_idx].line_idx) ++second_idx; - --second_idx; - - assert(painted_lines[first_idx].line_idx == painted_lines[second_idx].line_idx); - std::vector lines_c_line = colorize_line(contour.get_segment(painted_lines[first_idx].line_idx), first_idx, second_idx, painted_lines); - new_lines.insert(new_lines.end(), lines_c_line.begin(), lines_c_line.end()); - - if (second_idx + 1 <= end_idx) - for (size_t idx = painted_lines[second_idx].line_idx + 1; idx < painted_lines[second_idx + 1].line_idx; ++idx) - new_lines.emplace_back(ColoredLine{contour.get_segment(idx), 0}); - - first_idx = second_idx; - } - - for (size_t idx = painted_lines[end_idx].line_idx + 1; idx < contour.num_segments(); ++idx) - new_lines.emplace_back(ColoredLine{contour.get_segment(idx), 0}); - +static std::vector filter_colorized_polygon(std::vector &&new_lines) { for (size_t line_idx = 2; line_idx < new_lines.size(); ++line_idx) { const ColoredLine &line_0 = new_lines[line_idx - 2]; - ColoredLine & line_1 = new_lines[line_idx - 1]; + ColoredLine &line_1 = new_lines[line_idx - 1]; const ColoredLine &line_2 = new_lines[line_idx - 0]; if (line_0.color == line_2.color && line_0.color != line_1.color && line_0.color >= 1) { @@ -406,8 +411,8 @@ static std::vector colorize_polygon(const EdgeGrid::Contour &contou for (size_t line_idx = 3; line_idx < new_lines.size(); ++line_idx) { const ColoredLine &line_0 = new_lines[line_idx - 3]; - ColoredLine & line_1 = new_lines[line_idx - 2]; - ColoredLine & line_2 = new_lines[line_idx - 1]; + ColoredLine &line_1 = new_lines[line_idx - 2]; + ColoredLine &line_2 = new_lines[line_idx - 1]; const ColoredLine &line_3 = new_lines[line_idx - 0]; if (line_0.color == line_3.color && (line_0.color != line_1.color || line_0.color != line_2.color) && line_0.color >= 1 && line_3.color >= 1) { @@ -466,40 +471,58 @@ static std::vector colorize_polygon(const EdgeGrid::Contour &contou } } - return new_lines; + return std::move(new_lines); } -static std::vector> colorize_polygons(const std::vector &contours, std::vector &painted_lines) -{ - const size_t start_idx = 0; - const size_t end_idx = painted_lines.size() - 1; +static std::vector colorize_contour(const EdgeGrid::Contour &contour, const std::vector &painted_contour) { + assert(painted_contour.empty() || std::all_of(painted_contour.begin(), painted_contour.end(), [&painted_contour](const auto &p_line) { return painted_contour.front().contour_idx == p_line.contour_idx; })); - std::vector> new_polygons; - new_polygons.reserve(contours.size()); - - for (size_t idx = 0; idx < painted_lines[start_idx].contour_idx; ++idx) - new_polygons.emplace_back(to_colored_lines(contours[idx], 0)); - - for (size_t first_idx = start_idx; first_idx <= end_idx; ++first_idx) { - size_t second_idx = first_idx; - while (second_idx <= end_idx && painted_lines[first_idx].contour_idx == painted_lines[second_idx].contour_idx) - ++second_idx; - --second_idx; - - assert(painted_lines[first_idx].contour_idx == painted_lines[second_idx].contour_idx); - new_polygons.emplace_back(colorize_polygon(contours[painted_lines[first_idx].contour_idx], first_idx, second_idx, painted_lines)); - - if (second_idx + 1 <= end_idx) - for (size_t idx = painted_lines[second_idx].contour_idx + 1; idx < painted_lines[second_idx + 1].contour_idx; ++idx) - new_polygons.emplace_back(to_colored_lines(contours[idx], 0)); - - first_idx = second_idx; + std::vector colorized_contour; + if (painted_contour.empty()) { + // Appends contour with default color for lines before the first PaintedLine. + colorized_contour.reserve(contour.num_segments()); + for (const Line &line : contour.get_segments()) + colorized_contour.emplace_back(ColoredLine{line, 0}); + return colorized_contour; } - for (size_t idx = painted_lines[end_idx].contour_idx + 1; idx < contours.size(); ++idx) - new_polygons.emplace_back(to_colored_lines(contours[idx], 0)); + colorized_contour.reserve(contour.num_segments() + painted_contour.size()); + for (size_t idx = 0; idx < painted_contour.front().line_idx; ++idx) + colorized_contour.emplace_back(ColoredLine{contour.get_segment(idx), 0}); - return new_polygons; + size_t prev_painted_line_idx = 0; + for (size_t curr_painted_line_idx = 0; curr_painted_line_idx < painted_contour.size(); ++curr_painted_line_idx) { + size_t next_painted_line_idx = curr_painted_line_idx + 1; + if (next_painted_line_idx >= painted_contour.size() || painted_contour[curr_painted_line_idx].line_idx != painted_contour[next_painted_line_idx].line_idx) { + const std::vector &painted_contour_copy = painted_contour; + Slic3r::append(colorized_contour, colorize_line(contour.get_segment(painted_contour[prev_painted_line_idx].line_idx), prev_painted_line_idx, curr_painted_line_idx, painted_contour_copy)); + + // Appends contour with default color for lines between the current and the next PaintedLine. + if (next_painted_line_idx < painted_contour.size()) + for (size_t idx = painted_contour[curr_painted_line_idx].line_idx + 1; idx < painted_contour[next_painted_line_idx].line_idx; ++idx) + colorized_contour.emplace_back(ColoredLine{contour.get_segment(idx), 0}); + + prev_painted_line_idx = next_painted_line_idx; + } + } + + // Appends contour with default color for lines after the last PaintedLine. + for (size_t idx = painted_contour.back().line_idx + 1; idx < contour.num_segments(); ++idx) + colorized_contour.emplace_back(ColoredLine{contour.get_segment(idx), 0}); + + assert(!colorized_contour.empty()); + return filter_colorized_polygon(std::move(colorized_contour)); +} + +static std::vector> colorize_contours(const std::vector &contours, const std::vector> &painted_contours) +{ + assert(contours.size() == painted_contours.size()); + std::vector> colorized_contours(contours.size()); + for (const std::vector &painted_contour : painted_contours) { + size_t contour_idx = &painted_contour - &painted_contours.front(); + colorized_contours[contour_idx] = colorize_contour(contours[contour_idx], painted_contours[contour_idx]); + } + return colorized_contours; } using boost::polygon::voronoi_diagram; @@ -1585,7 +1608,7 @@ void export_processed_input_expolygons_to_svg(const std::string &path, const Lay #endif // MMU_SEGMENTATION_DEBUG_INPUT #ifdef MMU_SEGMENTATION_DEBUG_PAINTED_LINES -static void export_painted_lines_to_svg(const std::string &path, const std::vector &painted_lines, const ExPolygons &lslices) +static void export_painted_lines_to_svg(const std::string &path, const std::vector> &all_painted_lines, const ExPolygons &lslices) { const std::vector colors = {"blue", "cyan", "red", "orange", "magenta", "pink", "purple", "yellow"}; coordf_t stroke_width = scale_(0.05); @@ -1596,8 +1619,9 @@ static void export_painted_lines_to_svg(const std::string &path, const std::vect for (const Line &line : to_lines(lslices)) svg.draw(line, "green", stroke_width); - for (const PaintedLine &painted_line : painted_lines) - svg.draw(painted_line.projected_line, painted_line.color < int(colors.size()) ? colors[painted_line.color] : "black", stroke_width); + for (const std::vector &painted_lines : all_painted_lines) + for (const PaintedLine &painted_line : painted_lines) + svg.draw(painted_line.projected_line, painted_line.color < int(colors.size()) ? colors[painted_line.color] : "black", stroke_width); } #endif // MMU_SEGMENTATION_DEBUG_PAINTED_LINES @@ -1765,29 +1789,24 @@ std::vector>> multi_material_segmentati 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) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { throw_on_cancel_callback(); - auto comp = [&edge_grids, layer_idx](const PaintedLine &first, const PaintedLine &second) { - Point first_start_p = edge_grids[layer_idx].contours()[first.contour_idx].segment_start(first.line_idx); - return first.contour_idx < second.contour_idx || - (first.contour_idx == second.contour_idx && - (first.line_idx < second.line_idx || - (first.line_idx == second.line_idx && - ((first.projected_line.a - first_start_p).cast().squaredNorm() < (second.projected_line.a - first_start_p).cast().squaredNorm() || - ((first.projected_line.a - first_start_p).cast().squaredNorm() == (second.projected_line.a - first_start_p).cast().squaredNorm() && - (first.projected_line.b - first.projected_line.a).cast().squaredNorm() < (second.projected_line.b - second.projected_line.a).cast().squaredNorm()))))); - }; - - std::sort(painted_lines[layer_idx].begin(), painted_lines[layer_idx].end(), comp); - std::vector &painted_lines_single = painted_lines[layer_idx]; - - if (!painted_lines_single.empty()) { + if (!painted_lines[layer_idx].empty()) { #ifdef MMU_SEGMENTATION_DEBUG_PAINTED_LINES { static int iRun = 0; - export_painted_lines_to_svg(debug_out_path("mm-painted-lines-%d-%d.svg", layer_idx, iRun++), painted_lines_single, input_expolygons[layer_idx]); + export_painted_lines_to_svg(debug_out_path("mm-painted-lines-%d-%d.svg", layer_idx, iRun++), {painted_lines[layer_idx]}, input_expolygons[layer_idx]); } #endif // MMU_SEGMENTATION_DEBUG_PAINTED_LINES - std::vector> color_poly = colorize_polygons(edge_grids[layer_idx].contours(), painted_lines_single); + std::vector> post_processed_painted_lines = post_process_painted_lines(edge_grids[layer_idx].contours(), std::move(painted_lines[layer_idx])); + +#ifdef MMU_SEGMENTATION_DEBUG_PAINTED_LINES + { + static int iRun = 0; + export_painted_lines_to_svg(debug_out_path("mm-painted-lines-post-processed-%d-%d.svg", layer_idx, iRun++), post_processed_painted_lines, input_expolygons[layer_idx]); + } +#endif // MMU_SEGMENTATION_DEBUG_PAINTED_LINES + + std::vector> color_poly = colorize_contours(edge_grids[layer_idx].contours(), post_processed_painted_lines); #ifdef MMU_SEGMENTATION_DEBUG_COLORIZED_POLYGONS {