diff --git a/src/libslic3r/Line.hpp b/src/libslic3r/Line.hpp index 081117c775..a32d6eac75 100644 --- a/src/libslic3r/Line.hpp +++ b/src/libslic3r/Line.hpp @@ -190,6 +190,10 @@ template bool intersection(const L &l1, const L &l2, Vec, Scalar return false; // not intersecting } +inline Point midpoint(const Point &a, const Point &b) { + return (a + b) / 2; +} + } // namespace line_alg class Line @@ -204,7 +208,7 @@ public: void rotate(double angle, const Point ¢er) { this->a.rotate(angle, center); this->b.rotate(angle, center); } void reverse() { std::swap(this->a, this->b); } double length() const { return (b.cast() - a.cast()).norm(); } - Point midpoint() const { return (this->a + this->b) / 2; } + Point midpoint() const { return line_alg::midpoint(this->a, this->b); } bool intersection_infinite(const Line &other, Point* point) const; bool operator==(const Line &rhs) const { return this->a == rhs.a && this->b == rhs.b; } double distance_to_squared(const Point &point) const { return distance_to_squared(point, this->a, this->b); } diff --git a/src/libslic3r/MultiPoint.hpp b/src/libslic3r/MultiPoint.hpp index 39353e989d..40a912630d 100644 --- a/src/libslic3r/MultiPoint.hpp +++ b/src/libslic3r/MultiPoint.hpp @@ -187,7 +187,7 @@ public: void rotate(double angle) { this->rotate(cos(angle), sin(angle)); } void rotate(double cos_angle, double sin_angle); void rotate(double angle, const Point ¢er); - void reverse() { std::reverse(this->points.begin(), this->points.end()); } + virtual void reverse() { std::reverse(this->points.begin(), this->points.end()); } const Point& front() const { return this->points.front(); } const Point& back() const { return this->points.back(); } diff --git a/src/libslic3r/Polygon.hpp b/src/libslic3r/Polygon.hpp index c7d59f35d8..9ecd5ddd99 100644 --- a/src/libslic3r/Polygon.hpp +++ b/src/libslic3r/Polygon.hpp @@ -34,11 +34,14 @@ namespace Slic3r { class Polygon; class BoundingBox; +class ColorPolygon; using Polygons = std::vector>; using PolygonPtrs = std::vector>; using ConstPolygonPtrs = std::vector>; +using ColorPolygons = std::vector; + // Returns true if inside. Returns border_result if on boundary. bool contains(const Polygon& polygon, const Point& p, bool border_result = true); bool contains(const Polygons& polygons, const Point& p, bool border_result = true); @@ -328,12 +331,41 @@ template IntegerOnly reserve_polygons(I cap) return reserve_vector(cap); } -} // Slic3r +class ColorPolygon : public Polygon +{ +public: + using Color = uint8_t; + using Colors = std::vector; + + Colors colors; + + ColorPolygon() = default; + explicit ColorPolygon(const Points &points, const Colors &colors) : Polygon(points), colors(colors) {} + ColorPolygon(std::initializer_list points, std::initializer_list colors) : Polygon(points), colors(colors) {} + ColorPolygon(const ColorPolygon &other) : ColorPolygon(other.points, other.colors) {} + ColorPolygon(ColorPolygon &&other) noexcept : ColorPolygon(std::move(other.points), std::move(other.colors)) {} + ColorPolygon(Points &&points, Colors &&colors) : Polygon(std::move(points)), colors(std::move(colors)) {} + + void reverse() override { + Polygon::reverse(); + std::reverse(this->colors.begin(), this->colors.end()); + } + + ColorPolygon &operator=(const ColorPolygon &other) { + this->points = other.points; + this->colors = other.colors; + return *this; + } +}; + +using ColorPolygons = std::vector; + +} // namespace Slic3r // start Boost #include -namespace boost { namespace polygon { +namespace boost::polygon { template <> struct geometry_concept{ typedef polygon_concept type; }; @@ -411,7 +443,7 @@ namespace boost { namespace polygon { polygons.assign(input_begin, input_end); } }; -} } +} // namespace boost::polygon // end Boost #endif diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index 85ec9011ad..03cbe01496 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -568,9 +568,10 @@ struct EdgeToFace { bool operator<(const EdgeToFace &other) const { return vertex_low < other.vertex_low || (vertex_low == other.vertex_low && vertex_high < other.vertex_high); } }; -template -static std::vector create_edge_map( - const indexed_triangle_set &its, FaceFilter face_filter, ThrowOnCancelCallback throw_on_cancel) +template +static std::vector create_edge_map(const typename IndexedTriangleSetType::type &its, + FaceFilter face_filter, + ThrowOnCancelCallback throw_on_cancel) { std::vector edges_map; edges_map.reserve(its.indices.size() * 3); @@ -599,12 +600,14 @@ static std::vector create_edge_map( // Map from a face edge to a unique edge identifier or -1 if no neighbor exists. // Two neighbor faces share a unique edge identifier even if they are flipped. -template -static inline std::vector its_face_edge_ids_impl(const indexed_triangle_set &its, FaceFilter face_filter, ThrowOnCancelCallback throw_on_cancel) +template +static inline std::vector its_face_edge_ids_impl(const typename IndexedTriangleSetType::type &its, + FaceFilter face_filter, + ThrowOnCancelCallback throw_on_cancel) { std::vector out(its.indices.size(), Vec3i(-1, -1, -1)); - std::vector edges_map = create_edge_map(its, face_filter, throw_on_cancel); + std::vector edges_map = create_edge_map(its, face_filter, throw_on_cancel); // Assign a unique common edge id to touching triangle edges. int num_edges = 0; @@ -624,8 +627,8 @@ static inline std::vector its_face_edge_ids_impl(const indexed_triangle_s } if (! found) { //FIXME Vojtech: Trying to find an edge with equal orientation. This smells. - // admesh can assign the same edge ID to more than two facets (which is - // still topologically correct), so we have to search for a duplicate of + // admesh can assign the same edge ID to more than two facets (which is + // still topologically correct), so we have to search for a duplicate of // this edge too in case it was already seen in this orientation for (j = i + 1; j < edges_map.size() && edge_i == edges_map[j]; ++ j) if (edges_map[j].face != -1) { @@ -650,9 +653,16 @@ static inline std::vector its_face_edge_ids_impl(const indexed_triangle_s return out; } -std::vector its_face_edge_ids(const indexed_triangle_set &its) +// Explicit template instantiation. +template std::vector its_face_edge_ids(const IndexedTriangleSetType::type &); +template std::vector its_face_edge_ids(const IndexedTriangleSetType::type &); +template std::vector its_face_edge_ids(const IndexedTriangleSetType::type &, const std::vector &); +template std::vector its_face_edge_ids(const IndexedTriangleSetType::type &, const std::vector &); + +template +std::vector its_face_edge_ids(const typename IndexedTriangleSetType::type &its) { - return its_face_edge_ids_impl(its, [](const uint32_t){ return true; }, [](){}); + return its_face_edge_ids_impl(its, [](const uint32_t){ return true; }, [](){}); } std::vector its_face_edge_ids(const indexed_triangle_set &its, std::function throw_on_cancel_callback) @@ -660,9 +670,10 @@ std::vector its_face_edge_ids(const indexed_triangle_set &its, std::funct return its_face_edge_ids_impl(its, [](const uint32_t){ return true; }, throw_on_cancel_callback); } -std::vector its_face_edge_ids(const indexed_triangle_set &its, const std::vector &face_mask) +template +std::vector its_face_edge_ids(const typename IndexedTriangleSetType::type &its, const std::vector &face_mask) { - return its_face_edge_ids_impl(its, [&face_mask](const uint32_t idx){ return face_mask[idx]; }, [](){}); + return its_face_edge_ids_impl(its, [&face_mask](const uint32_t idx){ return face_mask[idx]; }, [](){}); } // Having the face neighbors available, assign unique edge IDs to face edges for chaining of polygons over slices. diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp index 75bb095bf3..9b8fa5e2b2 100644 --- a/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -234,9 +234,14 @@ private: // Map from a face edge to a unique edge identifier or -1 if no neighbor exists. // Two neighbor faces share a unique edge identifier even if they are flipped. // Used for chaining slice lines into polygons. -std::vector its_face_edge_ids(const indexed_triangle_set &its); +template +std::vector its_face_edge_ids(const typename IndexedTriangleSetType::type &its); + std::vector its_face_edge_ids(const indexed_triangle_set &its, std::function throw_on_cancel_callback); -std::vector its_face_edge_ids(const indexed_triangle_set &its, const std::vector &face_mask); + +template +std::vector its_face_edge_ids(const typename IndexedTriangleSetType::type &its, const std::vector &face_mask); + // Having the face neighbors available, assign unique edge IDs to face edges for chaining of polygons over slices. std::vector its_face_edge_ids(const indexed_triangle_set &its, std::vector &face_neighbors, bool assign_unbound_edges = false, int *num_edges = nullptr); diff --git a/src/libslic3r/TriangleMeshSlicer.cpp b/src/libslic3r/TriangleMeshSlicer.cpp index fd1d419c49..81d555aba6 100644 --- a/src/libslic3r/TriangleMeshSlicer.cpp +++ b/src/libslic3r/TriangleMeshSlicer.cpp @@ -58,6 +58,39 @@ namespace Slic3r { +template struct PolygonsType; + +template<> struct PolygonsType +{ + using type = Polygons; +}; + +template<> struct PolygonsType +{ + using type = ColorPolygons; +}; + +template struct FacetColorFunctor; + +template<> struct FacetColorFunctor +{ + constexpr ColorPolygon::Color operator()(size_t facet_idx) const { return 0; } +}; + +template<> struct FacetColorFunctor +{ + FacetColorFunctor() = delete; + explicit FacetColorFunctor(const ColorPolygon::Colors &colors) : colors(colors) {} + + ColorPolygon::Color operator()(size_t facet_idx) const { + assert(facet_idx < this->colors.size()); + return this->colors[facet_idx]; + } + +private: + const ColorPolygon::Colors &colors; +}; + class IntersectionReference { public: @@ -141,7 +174,10 @@ public: NO_SEED = 0x100, SKIP = 0x200, }; - uint32_t flags { 0 }; + + uint16_t flags { 0 }; + // Color id of sliced facet. + uint8_t color { 0 }; #ifdef DEBUG_INTERSECTIONLINE enum class Source { @@ -193,6 +229,7 @@ inline FacetSliceType slice_facet( const Vec3i &edge_ids, const int idx_vertex_lowest, const bool horizontal, + const ColorPolygon::Color facet_color, IntersectionLine &line_out) { using Vector = Eigen::Matrix; @@ -256,6 +293,7 @@ inline FacetSliceType slice_facet( line_out.b = v3f_scaled_to_contour_point(*b); line_out.a_id = a_id; line_out.b_id = b_id; + line_out.color = facet_color; assert(line_out.a != line_out.b); return result; } @@ -333,6 +371,7 @@ inline FacetSliceType slice_facet( line_out.b_id = points[0].point_id; line_out.edge_a_id = points[1].edge_id; line_out.edge_b_id = points[0].edge_id; + line_out.color = facet_color; // Not a zero lenght edge. //FIXME slice_facet() may create zero length edges due to rounding of doubles into coord_t. //assert(line_out.a != line_out.b); @@ -387,6 +426,7 @@ void slice_facet_at_zs( const TransformVertex &transform_vertex_fn, const stl_triangle_vertex_indices &indices, const Vec3i &edge_ids, + const ColorPolygon::Color facet_color, // Scaled or unscaled zs. If vertices have their zs scaled or transform_vertex_fn scales them, then zs have to be scaled as well. const std::vector &zs, std::vector &lines, @@ -406,7 +446,7 @@ void slice_facet_at_zs( for (auto it = min_layer; it != max_layer; ++ it) { IntersectionLine il; // Ignore horizontal triangles. Any valid horizontal triangle must have a vertical triangle connected, otherwise the part has zero volume. - if (min_z != max_z && slice_facet(*it, vertices, indices, edge_ids, idx_vertex_lowest, false, il) == FacetSliceType::Slicing) { + if (min_z != max_z && slice_facet(*it, vertices, indices, edge_ids, idx_vertex_lowest, false, facet_color, il) == FacetSliceType::Slicing) { assert(il.edge_type != IntersectionLine::FacetEdgeType::Horizontal); size_t slice_id = it - zs.begin(); boost::lock_guard l(lines_mutex(slice_id)); @@ -415,41 +455,44 @@ void slice_facet_at_zs( } } -template +template static inline std::vector slice_make_lines( const std::vector &vertices, const TransformVertex &transform_vertex_fn, const std::vector &indices, const std::vector &face_edge_ids, + const FacetColorFunctor &facet_color_fn, const std::vector &zs, const ThrowOnCancel throw_on_cancel_fn) { - std::vector lines(zs.size(), IntersectionLines{}); - LinesMutexes lines_mutex; + std::vector lines(zs.size(), IntersectionLines{}); + LinesMutexes lines_mutex; tbb::parallel_for( tbb::blocked_range(0, int(indices.size())), - [&vertices, &transform_vertex_fn, &indices, &face_edge_ids, &zs, &lines, &lines_mutex, throw_on_cancel_fn](const tbb::blocked_range &range) { + [&vertices, &transform_vertex_fn, &indices, &face_edge_ids, &facet_color_fn, &zs, &lines, &lines_mutex, throw_on_cancel_fn](const tbb::blocked_range &range) { for (int face_idx = range.begin(); face_idx < range.end(); ++ face_idx) { if ((face_idx & 0x0ffff) == 0) throw_on_cancel_fn(); - slice_facet_at_zs(vertices, transform_vertex_fn, indices[face_idx], face_edge_ids[face_idx], zs, lines, lines_mutex); + slice_facet_at_zs(vertices, transform_vertex_fn, indices[face_idx], face_edge_ids[face_idx], facet_color_fn(face_idx), zs, lines, lines_mutex); } } ); + return lines; } -template +template static inline IntersectionLines slice_make_lines( const std::vector &mesh_vertices, const TransformVertex &transform_vertex_fn, const std::vector &mesh_faces, const std::vector &face_edge_ids, - const float plane_z, + const FacetColorFunctor &facet_color_fn, + const float plane_z, FaceFilter face_filter) { IntersectionLines lines; - for (int face_idx = 0; face_idx < int(mesh_faces.size()); ++ face_idx) + for (int face_idx = 0; face_idx < int(mesh_faces.size()); ++ face_idx) { if (face_filter(face_idx)) { const Vec3i &indices = mesh_faces[face_idx]; stl_vertex vertices[3] { transform_vertex_fn(mesh_vertices[indices(0)]), transform_vertex_fn(mesh_vertices[indices(1)]), transform_vertex_fn(mesh_vertices[indices(2)]) }; @@ -457,14 +500,16 @@ static inline IntersectionLines slice_make_lines( const float min_z = fminf(vertices[0].z(), fminf(vertices[1].z(), vertices[2].z())); const float max_z = fmaxf(vertices[0].z(), fmaxf(vertices[1].z(), vertices[2].z())); assert(min_z <= plane_z && max_z >= plane_z); - int idx_vertex_lowest = (vertices[1].z() == min_z) ? 1 : ((vertices[2].z() == min_z) ? 2 : 0); + int idx_vertex_lowest = (vertices[1].z() == min_z) ? 1 : ((vertices[2].z() == min_z) ? 2 : 0); IntersectionLine il; // Ignore horizontal triangles. Any valid horizontal triangle must have a vertical triangle connected, otherwise the part has zero volume. - if (min_z != max_z && slice_facet(plane_z, vertices, indices, face_edge_ids[face_idx], idx_vertex_lowest, false, il) == FacetSliceType::Slicing) { + if (min_z != max_z && slice_facet(plane_z, vertices, indices, face_edge_ids[face_idx], idx_vertex_lowest, false, facet_color_fn(face_idx), il) == FacetSliceType::Slicing) { assert(il.edge_type != IntersectionLine::FacetEdgeType::Horizontal); lines.emplace_back(il); } } + } + return lines; } @@ -606,7 +651,7 @@ void slice_facet_with_slabs( IntersectionLine il_prev; for (auto it = min_layer; it != max_layer; ++ it) { IntersectionLine il; - auto type = slice_facet(*it, vertices, indices, facet_edge_ids, idx_vertex_lowest, false, il); + auto type = slice_facet(*it, vertices, indices, facet_edge_ids, idx_vertex_lowest, false, 0, il); if (type == FacetSliceType::NoSlice) { // One and exactly one vertex is touching the slicing plane. } else { @@ -950,8 +995,8 @@ static inline void remove_tangent_edges(std::vector &lines) struct OpenPolyline { OpenPolyline() = default; - OpenPolyline(const IntersectionReference &start, const IntersectionReference &end, Points &&points) : - start(start), end(end), points(std::move(points)), consumed(false) { this->length = Slic3r::length(this->points); } + OpenPolyline(const IntersectionReference &start, const IntersectionReference &end, Points &&points, ColorPolygon::Colors &&colors) : + start(start), end(end), points(std::move(points)), colors(std::move(colors)), length(Slic3r::length(this->points)), consumed(false) {} void reverse() { std::swap(start, end); std::reverse(points.begin(), points.end()); @@ -959,13 +1004,17 @@ struct OpenPolyline { IntersectionReference start; IntersectionReference end; Points points; + ColorPolygon::Colors colors; double length; bool consumed; }; // called by make_loops() to connect sliced triangles into closed loops and open polylines by the triangle connectivity. // Only connects segments crossing triangles of the same orientation. -static void chain_lines_by_triangle_connectivity(IntersectionLines &lines, Polygons &loops, std::vector &open_polylines) +template +static void chain_lines_by_triangle_connectivity(IntersectionLines &lines, + typename PolygonsType::type &loops, + std::vector &open_polylines) { // Build a map of lines by edge_a_id and a_id. std::vector by_edge_a_id; @@ -997,17 +1046,24 @@ static void chain_lines_by_triangle_connectivity(IntersectionLines &lines, Polyg } if (first_line == nullptr) break; + first_line->set_skip(); Points loop_pts; loop_pts.emplace_back(first_line->a); + + ColorPolygon::Colors loop_colors; + if constexpr (mesh_info == AdditionalMeshInfo::Color) { + loop_colors.emplace_back(first_line->color); + } + IntersectionLine *last_line = first_line; - + /* printf("first_line edge_a_id = %d, edge_b_id = %d, a_id = %d, b_id = %d, a = %d,%d, b = %d,%d\n", first_line->edge_a_id, first_line->edge_b_id, first_line->a_id, first_line->b_id, first_line->a.x, first_line->a.y, first_line->b.x, first_line->b.y); */ - + IntersectionLine key; for (;;) { // find a line starting where last one finishes @@ -1042,7 +1098,13 @@ static void chain_lines_by_triangle_connectivity(IntersectionLines &lines, Polyg (first_line->a_id != -1 && first_line->a_id == last_line->b_id)) { // The current loop is complete. Add it to the output. assert(first_line->a == last_line->b); - loops.emplace_back(std::move(loop_pts)); + + if constexpr (mesh_info == AdditionalMeshInfo::Color) { + loops.emplace_back(std::move(loop_pts), std::move(loop_colors)); + } else { + loops.emplace_back(std::move(loop_pts)); + } + #ifdef SLIC3R_TRIANGLEMESH_DEBUG printf(" Discovered %s polygon of %d points\n", (p.is_counter_clockwise() ? "ccw" : "cw"), (int)p.points.size()); #endif @@ -1051,7 +1113,7 @@ static void chain_lines_by_triangle_connectivity(IntersectionLines &lines, Polyg loop_pts.emplace_back(last_line->b); open_polylines.emplace_back(OpenPolyline( IntersectionReference(first_line->a_id, first_line->edge_a_id), - IntersectionReference(last_line->b_id, last_line->edge_b_id), std::move(loop_pts))); + IntersectionReference(last_line->b_id, last_line->edge_b_id), std::move(loop_pts), std::move(loop_colors))); } break; } @@ -1062,6 +1124,11 @@ static void chain_lines_by_triangle_connectivity(IntersectionLines &lines, Polyg */ assert(last_line->b == next_line->a); loop_pts.emplace_back(next_line->a); + + if constexpr (mesh_info == AdditionalMeshInfo::Color) { + loop_colors.emplace_back(next_line->color); + } + last_line = next_line; next_line->set_skip(); } @@ -1084,7 +1151,10 @@ std::vector open_polylines_sorted(std::vector &open // called by make_loops() to connect remaining open polylines across shared triangle edges and vertices. // Depending on "try_connect_reversed", it may or may not connect segments crossing triangles of opposite orientation. -static void chain_open_polylines_exact(std::vector &open_polylines, Polygons &loops, bool try_connect_reversed) +template +static void chain_open_polylines_exact(std::vector &open_polylines, + typename PolygonsType::type &loops, + bool try_connect_reversed) { // Store the end points of open_polylines into vectors sorted struct OpenPolylineEnd { @@ -1109,7 +1179,7 @@ static void chain_open_polylines_exact(std::vector &open_polylines } std::sort(by_id.begin(), by_id.end(), by_id_lower); // Find an iterator to by_id_lower for the particular end of OpenPolyline (by comparing the OpenPolyline pointer and the start attribute). - auto find_polyline_end = [&by_id, by_id_lower](const OpenPolylineEnd &end) -> std::vector::iterator { + auto find_polyline_end = [&by_id, by_id_lower](const OpenPolylineEnd &end) -> typename std::vector::iterator { for (auto it = std::lower_bound(by_id.begin(), by_id.end(), end, by_id_lower); it != by_id.end() && it->id() == end.id(); ++ it) if (*it == end) @@ -1135,15 +1205,20 @@ static void chain_open_polylines_exact(std::vector &open_polylines found: // Attach this polyline to the end of the initial polyline. if (it_next_start->start) { - auto it = it_next_start->polyline->points.begin(); - std::copy(++ it, it_next_start->polyline->points.end(), back_inserter(opl->points)); + auto pt_it = it_next_start->polyline->points.begin(); + auto color_it = it_next_start->polyline->colors.begin(); + std::copy(++pt_it, it_next_start->polyline->points.end(), back_inserter(opl->points)); + std::copy(color_it, it_next_start->polyline->colors.end(), back_inserter(opl->colors)); } else { - auto it = it_next_start->polyline->points.rbegin(); - std::copy(++ it, it_next_start->polyline->points.rend(), back_inserter(opl->points)); + auto pt_it = it_next_start->polyline->points.rbegin(); + auto color_it = it_next_start->polyline->colors.rbegin(); + std::copy(++pt_it, it_next_start->polyline->points.rend(), back_inserter(opl->points)); + std::copy(color_it, it_next_start->polyline->colors.rend(), back_inserter(opl->colors)); } opl->length += it_next_start->polyline->length; // Mark the next polyline as consumed. it_next_start->polyline->points.clear(); + it_next_start->polyline->colors.clear(); it_next_start->polyline->length = 0.; it_next_start->polyline->consumed = true; if (try_connect_reversed) { @@ -1163,16 +1238,26 @@ static void chain_open_polylines_exact(std::vector &open_polylines //assert(opl->points.front().point_id == opl->points.back().point_id); //assert(opl->points.front().edge_id == opl->points.back().edge_id); // Remove the duplicate last point. + // Contrary to the points, the assigned colors will not be duplicated, so we will not remove them. opl->points.pop_back(); if (opl->points.size() >= 3) { - if (try_connect_reversed && area(opl->points) < 0) + if (try_connect_reversed && area(opl->points) < 0) { // The closed polygon is patched from pieces with messed up orientation, therefore // the orientation of the patched up polygon is not known. // Orient the patched up polygons CCW. This heuristic may close some holes and cavities. std::reverse(opl->points.begin(), opl->points.end()); - loops.emplace_back(std::move(opl->points)); + std::reverse(opl->colors.begin(), opl->colors.end()); + } + + if constexpr (mesh_info == AdditionalMeshInfo::Color) { + loops.emplace_back(std::move(opl->points), std::move(opl->colors)); + } else { + loops.emplace_back(std::move(opl->points)); + } } + opl->points.clear(); + opl->colors.clear(); break; } // Continue with the current loop. @@ -1180,10 +1265,41 @@ static void chain_open_polylines_exact(std::vector &open_polylines } } -// called by make_loops() to connect remaining open polylines across shared triangle edges and vertices, +// The midpoint is inserted when color differs on both endpoints. +// Return true when a midpoint is inserted. +template +bool handle_color_at_gap_between_open_polylines(OpenPolyline &opl, + const Point &next_polyline_first_pt, + const ColorPolygon::Color &next_polyline_first_color) +{ + if constexpr (mesh_info == AdditionalMeshInfo::Color) { + bool midpoint_inserted = false; + if (opl.colors.back() == next_polyline_first_color) { + // Both endpoints around the gap have the same color, so we also use the same color for the gap. + opl.colors.emplace_back(opl.colors.back()); + } else { + // Endpoints around the gap have different colors, so we split the gap into two pieces, + // each with a different color. + opl.points.emplace_back(line_alg::midpoint(opl.points.back(), next_polyline_first_pt)); + opl.colors.emplace_back(opl.colors.back()); + opl.colors.emplace_back(next_polyline_first_color); + midpoint_inserted = true; + } + + return midpoint_inserted; + } + + return false; +} + +// called by make_loops() to connect remaining open polylines across shared triangle edges and vertices, // possibly closing small gaps. // Depending on "try_connect_reversed", it may or may not connect segments crossing triangles of opposite orientation. -static void chain_open_polylines_close_gaps(std::vector &open_polylines, Polygons &loops, double max_gap, bool try_connect_reversed) +template +static void chain_open_polylines_close_gaps(std::vector &open_polylines, + typename PolygonsType::type &loops, + double max_gap, + bool try_connect_reversed) { const coord_t max_gap_scaled = (coord_t)scale_(max_gap); @@ -1214,10 +1330,13 @@ static void chain_open_polylines_close_gaps(std::vector &open_poly for (OpenPolyline *opl : sorted_by_length) { if (opl->consumed) continue; + OpenPolylineEnd end(opl, false); - if (try_connect_reversed) + if (try_connect_reversed) { // The end point of this polyline will be modified, thus the following entry will become invalid. Remove it. closest_end_point_lookup.erase(end); + } + opl->consumed = true; size_t n_segments_joined = 1; for (;;) { @@ -1226,7 +1345,7 @@ static void chain_open_polylines_close_gaps(std::vector &open_poly const OpenPolylineEnd *next_start = next_start_and_dist.first; // Check whether we closed this loop. double current_loop_closing_distance2 = (opl->points.back() - opl->points.front()).cast().squaredNorm(); - bool loop_closed = current_loop_closing_distance2 < coordf_t(max_gap_scaled) * coordf_t(max_gap_scaled); + bool loop_closed = current_loop_closing_distance2 < Slic3r::sqr(coordf_t(max_gap_scaled)); if (next_start != nullptr && loop_closed && current_loop_closing_distance2 < next_start_and_dist.second) { // Heuristics to decide, whether to close the loop, or connect another polyline. // One should avoid closing loops shorter than max_gap_scaled. @@ -1237,21 +1356,35 @@ static void chain_open_polylines_close_gaps(std::vector &open_poly // Mark the current segment as not consumed, otherwise the closest_end_point_lookup.erase() would fail. opl->consumed = false; closest_end_point_lookup.erase(OpenPolylineEnd(opl, true)); + + bool midpoint_inserted = false; if (current_loop_closing_distance2 == 0.) { // Remove the duplicate last point. opl->points.pop_back(); } else { // The end points are different, keep both of them. + midpoint_inserted = handle_color_at_gap_between_open_polylines(*opl, opl->points.front(), opl->colors.front()); } - if (opl->points.size() >= 3) { - if (try_connect_reversed && n_segments_joined > 1 && area(opl->points) < 0) + + // When we split the gap into two pieces by adding a midpoint, then a valid polygon has at least 4 points. + if (opl->points.size() >= (3 + size_t(midpoint_inserted))) { + if (try_connect_reversed && n_segments_joined > 1 && area(opl->points) < 0) { // The closed polygon is patched from pieces with messed up orientation, therefore // the orientation of the patched up polygon is not known. // Orient the patched up polygons CCW. This heuristic may close some holes and cavities. std::reverse(opl->points.begin(), opl->points.end()); - loops.emplace_back(std::move(opl->points)); + std::reverse(opl->colors.begin(), opl->colors.end()); + } + + if constexpr (mesh_info == AdditionalMeshInfo::Color) { + loops.emplace_back(std::move(opl->points), std::move(opl->colors)); + } else { + loops.emplace_back(std::move(opl->points)); + } } + opl->points.clear(); + opl->colors.clear(); opl->consumed = true; break; } @@ -1263,36 +1396,56 @@ static void chain_open_polylines_close_gaps(std::vector &open_poly closest_end_point_lookup.insert(OpenPolylineEnd(opl, false)); break; } + // Attach this polyline to the end of the initial polyline. if (next_start->start) { - auto it = next_start->polyline->points.begin(); - if (*it == opl->points.back()) - ++ it; - std::copy(it, next_start->polyline->points.end(), back_inserter(opl->points)); + auto pt_it = next_start->polyline->points.begin(); + auto color_it = next_start->polyline->colors.begin(); + if (*pt_it == opl->points.back()) { + ++pt_it; + } else { + handle_color_at_gap_between_open_polylines(*opl, *pt_it, *color_it); + } + + std::copy(pt_it, next_start->polyline->points.end(), back_inserter(opl->points)); + std::copy(color_it, next_start->polyline->colors.end(), back_inserter(opl->colors)); } else { - auto it = next_start->polyline->points.rbegin(); - if (*it == opl->points.back()) - ++ it; - std::copy(it, next_start->polyline->points.rend(), back_inserter(opl->points)); + auto pt_it = next_start->polyline->points.rbegin(); + auto color_it = next_start->polyline->colors.rbegin(); + if (*pt_it == opl->points.back()) { + ++pt_it; + } else { + handle_color_at_gap_between_open_polylines(*opl, *pt_it, *color_it); + } + + std::copy(pt_it, next_start->polyline->points.rend(), back_inserter(opl->points)); + std::copy(color_it, next_start->polyline->colors.rend(), back_inserter(opl->colors)); } - ++ n_segments_joined; + + ++n_segments_joined; // Remove the end points of the consumed polyline segment from the lookup. OpenPolyline *opl2 = next_start->polyline; closest_end_point_lookup.erase(OpenPolylineEnd(opl2, true)); - if (try_connect_reversed) + if (try_connect_reversed) { closest_end_point_lookup.erase(OpenPolylineEnd(opl2, false)); + } + opl2->points.clear(); + opl2->colors.clear(); opl2->consumed = true; // Continue with the current loop. } } } -static Polygons make_loops( +template +static typename PolygonsType::type make_loops( // Lines will have their flags modified. - IntersectionLines &lines) -{ - Polygons loops; + IntersectionLines &lines +) { + using PolygonsType = typename PolygonsType::type; + + PolygonsType loops; #if 0 //FIXME slice_facet() may create zero length edges due to rounding of doubles into coord_t. //#ifdef _DEBUG @@ -1320,7 +1473,7 @@ static Polygons make_loops( #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ std::vector open_polylines; - chain_lines_by_triangle_connectivity(lines, loops, open_polylines); + chain_lines_by_triangle_connectivity(lines, loops, open_polylines); #ifdef SLIC3R_DEBUG_SLICE_PROCESSING { @@ -1336,8 +1489,8 @@ static Polygons make_loops( // Now process the open polylines. // Do it in two rounds, first try to connect in the same direction only, // then try to connect the open polylines in reversed order as well. - chain_open_polylines_exact(open_polylines, loops, false); - chain_open_polylines_exact(open_polylines, loops, true); + chain_open_polylines_exact(open_polylines, loops, false); + chain_open_polylines_exact(open_polylines, loops, true); #ifdef SLIC3R_DEBUG_SLICE_PROCESSING { @@ -1365,8 +1518,8 @@ static Polygons make_loops( } #else const double max_gap = 2.; //mm - chain_open_polylines_close_gaps(open_polylines, loops, max_gap, false); - chain_open_polylines_close_gaps(open_polylines, loops, max_gap, true); + chain_open_polylines_close_gaps(open_polylines, loops, max_gap, false); + chain_open_polylines_close_gaps(open_polylines, loops, max_gap, true); #endif #ifdef SLIC3R_DEBUG_SLICE_PROCESSING @@ -1388,14 +1541,17 @@ static Polygons make_loops( return loops; } -template -static std::vector make_loops( +template +static std::vector::type> make_loops( // Lines will have their flags modified. std::vector &lines, const MeshSlicingParams ¶ms, ThrowOnCancel throw_on_cancel) { - std::vector layers; + using PolygonsType = typename PolygonsType::type; + using PolygonType = typename PolygonsType::value_type; + + std::vector layers; layers.resize(lines.size()); tbb::parallel_for( tbb::blocked_range(0, lines.size()), @@ -1404,31 +1560,33 @@ static std::vector make_loops( if ((line_idx & 0x0ffff) == 0) throw_on_cancel(); - Polygons &polygons = layers[line_idx]; - polygons = make_loops(lines[line_idx]); + PolygonsType &polygons = layers[line_idx]; + polygons = make_loops(lines[line_idx]); auto this_mode = line_idx < params.slicing_mode_normal_below_layer ? params.mode_below : params.mode; if (! polygons.empty()) { if (this_mode == MeshSlicingParams::SlicingMode::Positive) { // Reorient all loops to be CCW. - for (Polygon& p : polygons) + for (PolygonType &p : polygons) { p.make_counter_clockwise(); - } - else if (this_mode == MeshSlicingParams::SlicingMode::PositiveLargestContour) { + } + } else if (this_mode == MeshSlicingParams::SlicingMode::PositiveLargestContour) { // Keep just the largest polygon, make it CCW. - double max_area = 0.; - Polygon* max_area_polygon = nullptr; - for (Polygon& p : polygons) { - double a = p.area(); - if (std::abs(a) > std::abs(max_area)) { - max_area = a; + double max_area = 0.; + PolygonType *max_area_polygon = nullptr; + for (PolygonType &p : polygons) { + if (const double a = p.area(); std::abs(a) > std::abs(max_area)) { + max_area = a; max_area_polygon = &p; } } + assert(max_area_polygon != nullptr); - if (max_area < 0.) + if (max_area < 0.) { max_area_polygon->reverse(); - Polygon p(std::move(*max_area_polygon)); + } + + PolygonType p(std::move(*max_area_polygon)); polygons.clear(); polygons.emplace_back(std::move(p)); } @@ -1534,7 +1692,7 @@ static std::vector make_slab_loops( #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ Polygons &loops = layers[line_idx]; std::vector open_polylines; - chain_lines_by_triangle_connectivity(in, loops, open_polylines); + chain_lines_by_triangle_connectivity(in, loops, open_polylines); #ifdef SLIC3R_DEBUG_SLICE_PROCESSING { SVG svg(debug_out_path("make_slab_loops-out-%d-%d-%s.svg", iRun, line_idx, ProjectionFromTop ? "top" : "bottom").c_str(), bbox_svg); @@ -1574,7 +1732,7 @@ static ExPolygons make_expolygons_simple(IntersectionLines &lines) ExPolygons slices; Polygons holes; - for (Polygon &loop : make_loops(lines)) + for (Polygon &loop : make_loops(lines)) if (loop.area() >= 0.) slices.emplace_back(std::move(loop)); else @@ -1745,7 +1903,8 @@ static inline bool is_identity(const Transform3d &trafo) return trafo.matrix() == Transform3d::Identity().matrix(); } -static std::vector transform_mesh_vertices_for_slicing(const indexed_triangle_set &mesh, const Transform3d &trafo) +template +static std::vector transform_mesh_vertices_for_slicing(const typename IndexedTriangleSetType::type &mesh, const Transform3d &trafo) { // Copy and scale vertices in XY, don't scale in Z. // Possibly apply the transformation. @@ -1769,13 +1928,23 @@ static std::vector transform_mesh_vertices_for_slicing(const indexed return out; } -std::vector slice_mesh( - const indexed_triangle_set &mesh, +template +std::vector::type> slice_mesh( + const typename IndexedTriangleSetType::type &mesh, // Unscaled Zs - const std::vector &zs, - const MeshSlicingParams ¶ms, - std::function throw_on_cancel) + const std::vector &zs, + const MeshSlicingParams ¶ms, + std::function throw_on_cancel) { + using PolygonsType = typename PolygonsType::type; + + const FacetColorFunctor facet_color_fn = [&] { + if constexpr (mesh_info == AdditionalMeshInfo::Color) + return FacetColorFunctor(mesh.colors); + else + return FacetColorFunctor(); + }(); + BOOST_LOG_TRIVIAL(debug) << "slice_mesh to polygons"; std::vector lines; @@ -1785,29 +1954,29 @@ std::vector slice_mesh( // Instead of edge identifiers, one shall use a sorted pair of edge vertex indices. // However facets_edges assigns a single edge ID to two triangles only, thus when factoring facets_edges out, one will have // to make sure that no code relies on it. - std::vector face_edge_ids = its_face_edge_ids(mesh); + std::vector face_edge_ids = its_face_edge_ids(mesh); if (zs.size() <= 1) { // It likely is not worthwile to copy the vertices. Apply the transformation in place. if (is_identity(params.trafo)) { lines = slice_make_lines( mesh.vertices, [](const Vec3f &p) { return Vec3f(scaled(p.x()), scaled(p.y()), p.z()); }, - mesh.indices, face_edge_ids, zs, throw_on_cancel); + mesh.indices, face_edge_ids, facet_color_fn, zs, throw_on_cancel); } else { // Transform the vertices, scale up in XY, not in Z. Transform3f tf = make_trafo_for_slicing(params.trafo); - lines = slice_make_lines(mesh.vertices, [tf](const Vec3f &p) { return tf * p; }, mesh.indices, face_edge_ids, zs, throw_on_cancel); + lines = slice_make_lines(mesh.vertices, [tf](const Vec3f &p) { return tf * p; }, mesh.indices, face_edge_ids, facet_color_fn, zs, throw_on_cancel); } } else { // Copy and scale vertices in XY, don't scale in Z. Possibly apply the transformation. lines = slice_make_lines( - transform_mesh_vertices_for_slicing(mesh, params.trafo), - [](const Vec3f &p) { return p; }, mesh.indices, face_edge_ids, zs, throw_on_cancel); + transform_mesh_vertices_for_slicing(mesh, params.trafo), + [](const Vec3f &p) { return p; }, mesh.indices, face_edge_ids, facet_color_fn, zs, throw_on_cancel); } } throw_on_cancel(); - std::vector layers = make_loops(lines, params, throw_on_cancel); + std::vector layers = make_loops(lines, params, throw_on_cancel); #ifdef SLIC3R_DEBUG { @@ -1845,13 +2014,43 @@ std::vector slice_mesh( return layers; } -// Specialized version for a single slicing plane only, running on a single thread. -Polygons slice_mesh( +std::vector slice_mesh( const indexed_triangle_set &mesh, // Unscaled Zs - const float plane_z, - const MeshSlicingParams ¶ms) + const std::vector &zs, + const MeshSlicingParams ¶ms, + std::function throw_on_cancel) { + return slice_mesh(mesh, zs, params, throw_on_cancel); +} + +std::vector slice_mesh( + const indexed_triangle_set_with_color &mesh, + // Unscaled Zs + const std::vector &zs, + const MeshSlicingParams ¶ms, + std::function throw_on_cancel) +{ + return slice_mesh(mesh, zs, params, throw_on_cancel); +} + +// Specialized version for a single slicing plane only, running on a single thread. +template +typename PolygonsType::type slice_mesh( + const typename IndexedTriangleSetType::type &mesh, + // Unscaled Zs + const float plane_z, + const MeshSlicingParams ¶ms) +{ + using PolygonsType = typename PolygonsType::type; + + const FacetColorFunctor facet_color_fn = [&] { + if constexpr (mesh_info == AdditionalMeshInfo::Color) + return FacetColorFunctor(mesh.colors); + else + return FacetColorFunctor(); + }(); + std::vector lines; { @@ -1887,27 +2086,45 @@ Polygons slice_mesh( } // 3) Calculate face neighbors for just the faces in face_mask. - std::vector face_edge_ids = its_face_edge_ids(mesh, face_mask); + std::vector face_edge_ids = its_face_edge_ids(mesh, face_mask); // 4) Slice "face_mask" triangles, collect line segments. // It likely is not worthwile to copy the vertices. Apply the transformation in place. if (trafo_identity) { - lines.emplace_back(slice_make_lines( + lines.emplace_back(slice_make_lines( mesh.vertices, [](const Vec3f &p) { return Vec3f(scaled(p.x()), scaled(p.y()), p.z()); }, - mesh.indices, face_edge_ids, plane_z, [&face_mask](int face_idx) { return face_mask[face_idx]; })); + mesh.indices, face_edge_ids, facet_color_fn, plane_z, [&face_mask](int face_idx) { return face_mask[face_idx]; })); } else { // Transform the vertices, scale up in XY, not in Z. - lines.emplace_back(slice_make_lines(mesh.vertices, [tf](const Vec3f& p) { return tf * p; }, mesh.indices, face_edge_ids, plane_z, + lines.emplace_back(slice_make_lines(mesh.vertices, [tf](const Vec3f& p) { return tf * p; }, mesh.indices, face_edge_ids, facet_color_fn, plane_z, [&face_mask](int face_idx) { return face_mask[face_idx]; })); } } // 5) Chain the line segments. - std::vector layers = make_loops(lines, params, [](){}); + std::vector layers = make_loops(lines, params, [](){}); assert(layers.size() == 1); return layers.front(); } +Polygons slice_mesh( + const indexed_triangle_set &mesh, + // Unscaled Zs + const float plane_z, + const MeshSlicingParams ¶ms) +{ + return slice_mesh(mesh, plane_z, params); +} + +ColorPolygons slice_mesh( + const indexed_triangle_set_with_color &mesh, + // Unscaled Zs + const float plane_z, + const MeshSlicingParams ¶ms) +{ + return slice_mesh(mesh, plane_z, params); +} + std::vector slice_mesh_ex( const indexed_triangle_set &mesh, const std::vector &zs, @@ -2272,7 +2489,7 @@ void cut_mesh(const indexed_triangle_set &mesh, float z, indexed_triangle_set *u dst.y() = scaled(src.y()); dst.z() = src.z(); } - slice_type = slice_facet(double(z), vertices_scaled, mesh.indices[facet_idx], facets_edge_ids[facet_idx], idx_vertex_lowest, min_z == max_z, line); + slice_type = slice_facet(double(z), vertices_scaled, mesh.indices[facet_idx], facets_edge_ids[facet_idx], idx_vertex_lowest, min_z == max_z, 0, line); } if (slice_type != FacetSliceType::NoSlice) { diff --git a/src/libslic3r/TriangleMeshSlicer.hpp b/src/libslic3r/TriangleMeshSlicer.hpp index f346542c96..b703d328e3 100644 --- a/src/libslic3r/TriangleMeshSlicer.hpp +++ b/src/libslic3r/TriangleMeshSlicer.hpp @@ -20,6 +20,8 @@ struct indexed_triangle_set; namespace Slic3r { +struct indexed_triangle_set_with_color; + struct MeshSlicingParams { enum class SlicingMode : uint32_t { @@ -37,6 +39,9 @@ struct MeshSlicingParams PositiveLargestContour, }; + MeshSlicingParams() = default; + explicit MeshSlicingParams(const Transform3d &trafo) : trafo(trafo) {} + SlicingMode mode { SlicingMode::Regular }; // For vase mode: below this layer a different slicing mode will be used to produce a single contour. // 0 = ignore. @@ -75,12 +80,23 @@ std::vector slice_mesh( const MeshSlicingParams ¶ms, std::function throw_on_cancel = []{}); +std::vector slice_mesh( + const indexed_triangle_set_with_color &mesh, + const std::vector &zs, + const MeshSlicingParams ¶ms, + std::function throw_on_cancel = []{}); + // Specialized version for a single slicing plane only, running on a single thread. Polygons slice_mesh( const indexed_triangle_set &mesh, - const float plane_z, + float plane_z, const MeshSlicingParams ¶ms); +ColorPolygons slice_mesh( + const indexed_triangle_set_with_color &mesh, + float plane_z, + const MeshSlicingParams ¶ms); + std::vector slice_mesh_ex( const indexed_triangle_set &mesh, const std::vector &zs,