Refactor TriangleMeshSlicer to support slicing meshes with information about the color of facets.

ColorPolygons are produced for meshes with color information.
This commit is contained in:
Lukáš Hejl 2024-07-09 14:40:37 +02:00 committed by Lukas Matena
parent 2fa92a96a4
commit baf3d6aed9
7 changed files with 399 additions and 114 deletions

View File

@ -190,6 +190,10 @@ template<class L> bool intersection(const L &l1, const L &l2, Vec<Dim<L>, Scalar
return false; // not intersecting return false; // not intersecting
} }
inline Point midpoint(const Point &a, const Point &b) {
return (a + b) / 2;
}
} // namespace line_alg } // namespace line_alg
class Line class Line
@ -204,7 +208,7 @@ public:
void rotate(double angle, const Point &center) { this->a.rotate(angle, center); this->b.rotate(angle, center); } void rotate(double angle, const Point &center) { this->a.rotate(angle, center); this->b.rotate(angle, center); }
void reverse() { std::swap(this->a, this->b); } void reverse() { std::swap(this->a, this->b); }
double length() const { return (b.cast<double>() - a.cast<double>()).norm(); } double length() const { return (b.cast<double>() - a.cast<double>()).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 intersection_infinite(const Line &other, Point* point) const;
bool operator==(const Line &rhs) const { return this->a == rhs.a && this->b == rhs.b; } 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); } double distance_to_squared(const Point &point) const { return distance_to_squared(point, this->a, this->b); }

View File

@ -187,7 +187,7 @@ public:
void rotate(double angle) { this->rotate(cos(angle), sin(angle)); } void rotate(double angle) { this->rotate(cos(angle), sin(angle)); }
void rotate(double cos_angle, double sin_angle); void rotate(double cos_angle, double sin_angle);
void rotate(double angle, const Point &center); void rotate(double angle, const Point &center);
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& front() const { return this->points.front(); }
const Point& back() const { return this->points.back(); } const Point& back() const { return this->points.back(); }

View File

@ -34,11 +34,14 @@ namespace Slic3r {
class Polygon; class Polygon;
class BoundingBox; class BoundingBox;
class ColorPolygon;
using Polygons = std::vector<Polygon, PointsAllocator<Polygon>>; using Polygons = std::vector<Polygon, PointsAllocator<Polygon>>;
using PolygonPtrs = std::vector<Polygon*, PointsAllocator<Polygon*>>; using PolygonPtrs = std::vector<Polygon*, PointsAllocator<Polygon*>>;
using ConstPolygonPtrs = std::vector<const Polygon*, PointsAllocator<const Polygon*>>; using ConstPolygonPtrs = std::vector<const Polygon*, PointsAllocator<const Polygon*>>;
using ColorPolygons = std::vector<ColorPolygon>;
// Returns true if inside. Returns border_result if on boundary. // 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 Polygon& polygon, const Point& p, bool border_result = true);
bool contains(const Polygons& polygons, const Point& p, bool border_result = true); bool contains(const Polygons& polygons, const Point& p, bool border_result = true);
@ -328,12 +331,41 @@ template<class I> IntegerOnly<I, Polygons> reserve_polygons(I cap)
return reserve_vector<Polygon, I, typename Polygons::allocator_type>(cap); return reserve_vector<Polygon, I, typename Polygons::allocator_type>(cap);
} }
} // Slic3r class ColorPolygon : public Polygon
{
public:
using Color = uint8_t;
using Colors = std::vector<Color>;
Colors colors;
ColorPolygon() = default;
explicit ColorPolygon(const Points &points, const Colors &colors) : Polygon(points), colors(colors) {}
ColorPolygon(std::initializer_list<Point> points, std::initializer_list<Color> 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<ColorPolygon>;
} // namespace Slic3r
// start Boost // start Boost
#include <boost/polygon/polygon.hpp> #include <boost/polygon/polygon.hpp>
namespace boost { namespace polygon { namespace boost::polygon {
template <> template <>
struct geometry_concept<Slic3r::Polygon>{ typedef polygon_concept type; }; struct geometry_concept<Slic3r::Polygon>{ typedef polygon_concept type; };
@ -411,7 +443,7 @@ namespace boost { namespace polygon {
polygons.assign(input_begin, input_end); polygons.assign(input_begin, input_end);
} }
}; };
} } } // namespace boost::polygon
// end Boost // end Boost
#endif #endif

View File

@ -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); } bool operator<(const EdgeToFace &other) const { return vertex_low < other.vertex_low || (vertex_low == other.vertex_low && vertex_high < other.vertex_high); }
}; };
template<typename FaceFilter, typename ThrowOnCancelCallback> template<AdditionalMeshInfo mesh_info = AdditionalMeshInfo::None, typename FaceFilter, typename ThrowOnCancelCallback>
static std::vector<EdgeToFace> create_edge_map( static std::vector<EdgeToFace> create_edge_map(const typename IndexedTriangleSetType<mesh_info>::type &its,
const indexed_triangle_set &its, FaceFilter face_filter, ThrowOnCancelCallback throw_on_cancel) FaceFilter face_filter,
ThrowOnCancelCallback throw_on_cancel)
{ {
std::vector<EdgeToFace> edges_map; std::vector<EdgeToFace> edges_map;
edges_map.reserve(its.indices.size() * 3); edges_map.reserve(its.indices.size() * 3);
@ -599,12 +600,14 @@ static std::vector<EdgeToFace> create_edge_map(
// Map from a face edge to a unique edge identifier or -1 if no neighbor exists. // 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. // Two neighbor faces share a unique edge identifier even if they are flipped.
template<typename FaceFilter, typename ThrowOnCancelCallback> template<AdditionalMeshInfo mesh_info = AdditionalMeshInfo::None, typename FaceFilter, typename ThrowOnCancelCallback>
static inline std::vector<Vec3i> its_face_edge_ids_impl(const indexed_triangle_set &its, FaceFilter face_filter, ThrowOnCancelCallback throw_on_cancel) static inline std::vector<Vec3i> its_face_edge_ids_impl(const typename IndexedTriangleSetType<mesh_info>::type &its,
FaceFilter face_filter,
ThrowOnCancelCallback throw_on_cancel)
{ {
std::vector<Vec3i> out(its.indices.size(), Vec3i(-1, -1, -1)); std::vector<Vec3i> out(its.indices.size(), Vec3i(-1, -1, -1));
std::vector<EdgeToFace> edges_map = create_edge_map(its, face_filter, throw_on_cancel); std::vector<EdgeToFace> edges_map = create_edge_map<mesh_info>(its, face_filter, throw_on_cancel);
// Assign a unique common edge id to touching triangle edges. // Assign a unique common edge id to touching triangle edges.
int num_edges = 0; int num_edges = 0;
@ -650,9 +653,16 @@ static inline std::vector<Vec3i> its_face_edge_ids_impl(const indexed_triangle_s
return out; return out;
} }
std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its) // Explicit template instantiation.
template std::vector<Vec3i> its_face_edge_ids<AdditionalMeshInfo::None>(const IndexedTriangleSetType<AdditionalMeshInfo::None>::type &);
template std::vector<Vec3i> its_face_edge_ids<AdditionalMeshInfo::Color>(const IndexedTriangleSetType<AdditionalMeshInfo::Color>::type &);
template std::vector<Vec3i> its_face_edge_ids<AdditionalMeshInfo::None>(const IndexedTriangleSetType<AdditionalMeshInfo::None>::type &, const std::vector<char> &);
template std::vector<Vec3i> its_face_edge_ids<AdditionalMeshInfo::Color>(const IndexedTriangleSetType<AdditionalMeshInfo::Color>::type &, const std::vector<char> &);
template<AdditionalMeshInfo mesh_info>
std::vector<Vec3i> its_face_edge_ids(const typename IndexedTriangleSetType<mesh_info>::type &its)
{ {
return its_face_edge_ids_impl(its, [](const uint32_t){ return true; }, [](){}); return its_face_edge_ids_impl<mesh_info>(its, [](const uint32_t){ return true; }, [](){});
} }
std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, std::function<void()> throw_on_cancel_callback) std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, std::function<void()> throw_on_cancel_callback)
@ -660,9 +670,10 @@ std::vector<Vec3i> 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); return its_face_edge_ids_impl(its, [](const uint32_t){ return true; }, throw_on_cancel_callback);
} }
std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, const std::vector<char> &face_mask) template<AdditionalMeshInfo mesh_info>
std::vector<Vec3i> its_face_edge_ids(const typename IndexedTriangleSetType<mesh_info>::type &its, const std::vector<char> &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<mesh_info>(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. // Having the face neighbors available, assign unique edge IDs to face edges for chaining of polygons over slices.

View File

@ -234,9 +234,14 @@ private:
// Map from a face edge to a unique edge identifier or -1 if no neighbor exists. // 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. // Two neighbor faces share a unique edge identifier even if they are flipped.
// Used for chaining slice lines into polygons. // Used for chaining slice lines into polygons.
std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its); template<AdditionalMeshInfo mesh_info = AdditionalMeshInfo::None>
std::vector<Vec3i> its_face_edge_ids(const typename IndexedTriangleSetType<mesh_info>::type &its);
std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, std::function<void()> throw_on_cancel_callback); std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, std::function<void()> throw_on_cancel_callback);
std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, const std::vector<char> &face_mask);
template<AdditionalMeshInfo mesh_info = AdditionalMeshInfo::None>
std::vector<Vec3i> its_face_edge_ids(const typename IndexedTriangleSetType<mesh_info>::type &its, const std::vector<char> &face_mask);
// Having the face neighbors available, assign unique edge IDs to face edges for chaining of polygons over slices. // Having the face neighbors available, assign unique edge IDs to face edges for chaining of polygons over slices.
std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, std::vector<Vec3i> &face_neighbors, bool assign_unbound_edges = false, int *num_edges = nullptr); std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, std::vector<Vec3i> &face_neighbors, bool assign_unbound_edges = false, int *num_edges = nullptr);

View File

@ -58,6 +58,39 @@
namespace Slic3r { namespace Slic3r {
template<AdditionalMeshInfo mesh_info> struct PolygonsType;
template<> struct PolygonsType<AdditionalMeshInfo::None>
{
using type = Polygons;
};
template<> struct PolygonsType<AdditionalMeshInfo::Color>
{
using type = ColorPolygons;
};
template<AdditionalMeshInfo mesh_info> struct FacetColorFunctor;
template<> struct FacetColorFunctor<AdditionalMeshInfo::None>
{
constexpr ColorPolygon::Color operator()(size_t facet_idx) const { return 0; }
};
template<> struct FacetColorFunctor<AdditionalMeshInfo::Color>
{
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 class IntersectionReference
{ {
public: public:
@ -141,7 +174,10 @@ public:
NO_SEED = 0x100, NO_SEED = 0x100,
SKIP = 0x200, SKIP = 0x200,
}; };
uint32_t flags { 0 };
uint16_t flags { 0 };
// Color id of sliced facet.
uint8_t color { 0 };
#ifdef DEBUG_INTERSECTIONLINE #ifdef DEBUG_INTERSECTIONLINE
enum class Source { enum class Source {
@ -193,6 +229,7 @@ inline FacetSliceType slice_facet(
const Vec3i &edge_ids, const Vec3i &edge_ids,
const int idx_vertex_lowest, const int idx_vertex_lowest,
const bool horizontal, const bool horizontal,
const ColorPolygon::Color facet_color,
IntersectionLine &line_out) IntersectionLine &line_out)
{ {
using Vector = Eigen::Matrix<T, 3, 1, Eigen::DontAlign>; using Vector = Eigen::Matrix<T, 3, 1, Eigen::DontAlign>;
@ -256,6 +293,7 @@ inline FacetSliceType slice_facet(
line_out.b = v3f_scaled_to_contour_point(*b); line_out.b = v3f_scaled_to_contour_point(*b);
line_out.a_id = a_id; line_out.a_id = a_id;
line_out.b_id = b_id; line_out.b_id = b_id;
line_out.color = facet_color;
assert(line_out.a != line_out.b); assert(line_out.a != line_out.b);
return result; return result;
} }
@ -333,6 +371,7 @@ inline FacetSliceType slice_facet(
line_out.b_id = points[0].point_id; line_out.b_id = points[0].point_id;
line_out.edge_a_id = points[1].edge_id; line_out.edge_a_id = points[1].edge_id;
line_out.edge_b_id = points[0].edge_id; line_out.edge_b_id = points[0].edge_id;
line_out.color = facet_color;
// Not a zero lenght edge. // Not a zero lenght edge.
//FIXME slice_facet() may create zero length edges due to rounding of doubles into coord_t. //FIXME slice_facet() may create zero length edges due to rounding of doubles into coord_t.
//assert(line_out.a != line_out.b); //assert(line_out.a != line_out.b);
@ -387,6 +426,7 @@ void slice_facet_at_zs(
const TransformVertex &transform_vertex_fn, const TransformVertex &transform_vertex_fn,
const stl_triangle_vertex_indices &indices, const stl_triangle_vertex_indices &indices,
const Vec3i &edge_ids, 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. // 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<float> &zs, const std::vector<float> &zs,
std::vector<IntersectionLines> &lines, std::vector<IntersectionLines> &lines,
@ -406,7 +446,7 @@ void slice_facet_at_zs(
for (auto it = min_layer; it != max_layer; ++ it) { for (auto it = min_layer; it != max_layer; ++ it) {
IntersectionLine il; IntersectionLine il;
// Ignore horizontal triangles. Any valid horizontal triangle must have a vertical triangle connected, otherwise the part has zero volume. // 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); assert(il.edge_type != IntersectionLine::FacetEdgeType::Horizontal);
size_t slice_id = it - zs.begin(); size_t slice_id = it - zs.begin();
boost::lock_guard<std::mutex> l(lines_mutex(slice_id)); boost::lock_guard<std::mutex> l(lines_mutex(slice_id));
@ -415,12 +455,13 @@ void slice_facet_at_zs(
} }
} }
template<typename TransformVertex, typename ThrowOnCancel> template<AdditionalMeshInfo mesh_info, typename TransformVertex, typename ThrowOnCancel>
static inline std::vector<IntersectionLines> slice_make_lines( static inline std::vector<IntersectionLines> slice_make_lines(
const std::vector<stl_vertex> &vertices, const std::vector<stl_vertex> &vertices,
const TransformVertex &transform_vertex_fn, const TransformVertex &transform_vertex_fn,
const std::vector<stl_triangle_vertex_indices> &indices, const std::vector<stl_triangle_vertex_indices> &indices,
const std::vector<Vec3i> &face_edge_ids, const std::vector<Vec3i> &face_edge_ids,
const FacetColorFunctor<mesh_info> &facet_color_fn,
const std::vector<float> &zs, const std::vector<float> &zs,
const ThrowOnCancel throw_on_cancel_fn) const ThrowOnCancel throw_on_cancel_fn)
{ {
@ -428,28 +469,30 @@ static inline std::vector<IntersectionLines> slice_make_lines(
LinesMutexes lines_mutex; LinesMutexes lines_mutex;
tbb::parallel_for( tbb::parallel_for(
tbb::blocked_range<int>(0, int(indices.size())), tbb::blocked_range<int>(0, int(indices.size())),
[&vertices, &transform_vertex_fn, &indices, &face_edge_ids, &zs, &lines, &lines_mutex, throw_on_cancel_fn](const tbb::blocked_range<int> &range) { [&vertices, &transform_vertex_fn, &indices, &face_edge_ids, &facet_color_fn, &zs, &lines, &lines_mutex, throw_on_cancel_fn](const tbb::blocked_range<int> &range) {
for (int face_idx = range.begin(); face_idx < range.end(); ++ face_idx) { for (int face_idx = range.begin(); face_idx < range.end(); ++ face_idx) {
if ((face_idx & 0x0ffff) == 0) if ((face_idx & 0x0ffff) == 0)
throw_on_cancel_fn(); 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; return lines;
} }
template<typename TransformVertex, typename FaceFilter> template<AdditionalMeshInfo mesh_info, typename TransformVertex, typename FaceFilter>
static inline IntersectionLines slice_make_lines( static inline IntersectionLines slice_make_lines(
const std::vector<stl_vertex> &mesh_vertices, const std::vector<stl_vertex> &mesh_vertices,
const TransformVertex &transform_vertex_fn, const TransformVertex &transform_vertex_fn,
const std::vector<stl_triangle_vertex_indices> &mesh_faces, const std::vector<stl_triangle_vertex_indices> &mesh_faces,
const std::vector<Vec3i> &face_edge_ids, const std::vector<Vec3i> &face_edge_ids,
const FacetColorFunctor<mesh_info> &facet_color_fn,
const float plane_z, const float plane_z,
FaceFilter face_filter) FaceFilter face_filter)
{ {
IntersectionLines lines; 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)) { if (face_filter(face_idx)) {
const Vec3i &indices = mesh_faces[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)]) }; 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)]) };
@ -460,11 +503,13 @@ static inline IntersectionLines slice_make_lines(
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; IntersectionLine il;
// Ignore horizontal triangles. Any valid horizontal triangle must have a vertical triangle connected, otherwise the part has zero volume. // 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); assert(il.edge_type != IntersectionLine::FacetEdgeType::Horizontal);
lines.emplace_back(il); lines.emplace_back(il);
} }
} }
}
return lines; return lines;
} }
@ -606,7 +651,7 @@ void slice_facet_with_slabs(
IntersectionLine il_prev; IntersectionLine il_prev;
for (auto it = min_layer; it != max_layer; ++ it) { for (auto it = min_layer; it != max_layer; ++ it) {
IntersectionLine il; 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) { if (type == FacetSliceType::NoSlice) {
// One and exactly one vertex is touching the slicing plane. // One and exactly one vertex is touching the slicing plane.
} else { } else {
@ -950,8 +995,8 @@ static inline void remove_tangent_edges(std::vector<IntersectionLine> &lines)
struct OpenPolyline { struct OpenPolyline {
OpenPolyline() = default; OpenPolyline() = default;
OpenPolyline(const IntersectionReference &start, const IntersectionReference &end, Points &&points) : OpenPolyline(const IntersectionReference &start, const IntersectionReference &end, Points &&points, ColorPolygon::Colors &&colors) :
start(start), end(end), points(std::move(points)), consumed(false) { this->length = Slic3r::length(this->points); } start(start), end(end), points(std::move(points)), colors(std::move(colors)), length(Slic3r::length(this->points)), consumed(false) {}
void reverse() { void reverse() {
std::swap(start, end); std::swap(start, end);
std::reverse(points.begin(), points.end()); std::reverse(points.begin(), points.end());
@ -959,13 +1004,17 @@ struct OpenPolyline {
IntersectionReference start; IntersectionReference start;
IntersectionReference end; IntersectionReference end;
Points points; Points points;
ColorPolygon::Colors colors;
double length; double length;
bool consumed; bool consumed;
}; };
// called by make_loops() to connect sliced triangles into closed loops and open polylines by the triangle connectivity. // 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. // Only connects segments crossing triangles of the same orientation.
static void chain_lines_by_triangle_connectivity(IntersectionLines &lines, Polygons &loops, std::vector<OpenPolyline> &open_polylines) template<AdditionalMeshInfo mesh_info>
static void chain_lines_by_triangle_connectivity(IntersectionLines &lines,
typename PolygonsType<mesh_info>::type &loops,
std::vector<OpenPolyline> &open_polylines)
{ {
// Build a map of lines by edge_a_id and a_id. // Build a map of lines by edge_a_id and a_id.
std::vector<IntersectionLine*> by_edge_a_id; std::vector<IntersectionLine*> by_edge_a_id;
@ -997,9 +1046,16 @@ static void chain_lines_by_triangle_connectivity(IntersectionLines &lines, Polyg
} }
if (first_line == nullptr) if (first_line == nullptr)
break; break;
first_line->set_skip(); first_line->set_skip();
Points loop_pts; Points loop_pts;
loop_pts.emplace_back(first_line->a); 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; IntersectionLine *last_line = first_line;
/* /*
@ -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)) { (first_line->a_id != -1 && first_line->a_id == last_line->b_id)) {
// The current loop is complete. Add it to the output. // The current loop is complete. Add it to the output.
assert(first_line->a == last_line->b); assert(first_line->a == last_line->b);
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)); loops.emplace_back(std::move(loop_pts));
}
#ifdef SLIC3R_TRIANGLEMESH_DEBUG #ifdef SLIC3R_TRIANGLEMESH_DEBUG
printf(" Discovered %s polygon of %d points\n", (p.is_counter_clockwise() ? "ccw" : "cw"), (int)p.points.size()); printf(" Discovered %s polygon of %d points\n", (p.is_counter_clockwise() ? "ccw" : "cw"), (int)p.points.size());
#endif #endif
@ -1051,7 +1113,7 @@ static void chain_lines_by_triangle_connectivity(IntersectionLines &lines, Polyg
loop_pts.emplace_back(last_line->b); loop_pts.emplace_back(last_line->b);
open_polylines.emplace_back(OpenPolyline( open_polylines.emplace_back(OpenPolyline(
IntersectionReference(first_line->a_id, first_line->edge_a_id), 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; break;
} }
@ -1062,6 +1124,11 @@ static void chain_lines_by_triangle_connectivity(IntersectionLines &lines, Polyg
*/ */
assert(last_line->b == next_line->a); assert(last_line->b == next_line->a);
loop_pts.emplace_back(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; last_line = next_line;
next_line->set_skip(); next_line->set_skip();
} }
@ -1084,7 +1151,10 @@ std::vector<OpenPolyline*> open_polylines_sorted(std::vector<OpenPolyline> &open
// called by make_loops() to connect remaining open polylines across shared triangle edges and vertices. // 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. // 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<OpenPolyline> &open_polylines, Polygons &loops, bool try_connect_reversed) template<AdditionalMeshInfo mesh_info>
static void chain_open_polylines_exact(std::vector<OpenPolyline> &open_polylines,
typename PolygonsType<mesh_info>::type &loops,
bool try_connect_reversed)
{ {
// Store the end points of open_polylines into vectors sorted // Store the end points of open_polylines into vectors sorted
struct OpenPolylineEnd { struct OpenPolylineEnd {
@ -1109,7 +1179,7 @@ static void chain_open_polylines_exact(std::vector<OpenPolyline> &open_polylines
} }
std::sort(by_id.begin(), by_id.end(), by_id_lower); 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). // 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<OpenPolylineEnd>::iterator { auto find_polyline_end = [&by_id, by_id_lower](const OpenPolylineEnd &end) -> typename std::vector<OpenPolylineEnd>::iterator {
for (auto it = std::lower_bound(by_id.begin(), by_id.end(), end, by_id_lower); 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) it != by_id.end() && it->id() == end.id(); ++ it)
if (*it == end) if (*it == end)
@ -1135,15 +1205,20 @@ static void chain_open_polylines_exact(std::vector<OpenPolyline> &open_polylines
found: found:
// Attach this polyline to the end of the initial polyline. // Attach this polyline to the end of the initial polyline.
if (it_next_start->start) { if (it_next_start->start) {
auto it = it_next_start->polyline->points.begin(); auto pt_it = it_next_start->polyline->points.begin();
std::copy(++ it, it_next_start->polyline->points.end(), back_inserter(opl->points)); 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 { } else {
auto it = it_next_start->polyline->points.rbegin(); auto pt_it = it_next_start->polyline->points.rbegin();
std::copy(++ it, it_next_start->polyline->points.rend(), back_inserter(opl->points)); 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; opl->length += it_next_start->polyline->length;
// Mark the next polyline as consumed. // Mark the next polyline as consumed.
it_next_start->polyline->points.clear(); it_next_start->polyline->points.clear();
it_next_start->polyline->colors.clear();
it_next_start->polyline->length = 0.; it_next_start->polyline->length = 0.;
it_next_start->polyline->consumed = true; it_next_start->polyline->consumed = true;
if (try_connect_reversed) { if (try_connect_reversed) {
@ -1163,16 +1238,26 @@ static void chain_open_polylines_exact(std::vector<OpenPolyline> &open_polylines
//assert(opl->points.front().point_id == opl->points.back().point_id); //assert(opl->points.front().point_id == opl->points.back().point_id);
//assert(opl->points.front().edge_id == opl->points.back().edge_id); //assert(opl->points.front().edge_id == opl->points.back().edge_id);
// Remove the duplicate last point. // 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(); opl->points.pop_back();
if (opl->points.size() >= 3) { 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 closed polygon is patched from pieces with messed up orientation, therefore
// the orientation of the patched up polygon is not known. // the orientation of the patched up polygon is not known.
// Orient the patched up polygons CCW. This heuristic may close some holes and cavities. // Orient the patched up polygons CCW. This heuristic may close some holes and cavities.
std::reverse(opl->points.begin(), opl->points.end()); std::reverse(opl->points.begin(), opl->points.end());
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)); loops.emplace_back(std::move(opl->points));
} }
}
opl->points.clear(); opl->points.clear();
opl->colors.clear();
break; break;
} }
// Continue with the current loop. // Continue with the current loop.
@ -1180,10 +1265,41 @@ static void chain_open_polylines_exact(std::vector<OpenPolyline> &open_polylines
} }
} }
// The midpoint is inserted when color differs on both endpoints.
// Return true when a midpoint is inserted.
template<AdditionalMeshInfo mesh_info>
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, // called by make_loops() to connect remaining open polylines across shared triangle edges and vertices,
// possibly closing small gaps. // possibly closing small gaps.
// Depending on "try_connect_reversed", it may or may not connect segments crossing triangles of opposite orientation. // 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<OpenPolyline> &open_polylines, Polygons &loops, double max_gap, bool try_connect_reversed) template<AdditionalMeshInfo mesh_info>
static void chain_open_polylines_close_gaps(std::vector<OpenPolyline> &open_polylines,
typename PolygonsType<mesh_info>::type &loops,
double max_gap,
bool try_connect_reversed)
{ {
const coord_t max_gap_scaled = (coord_t)scale_(max_gap); const coord_t max_gap_scaled = (coord_t)scale_(max_gap);
@ -1214,10 +1330,13 @@ static void chain_open_polylines_close_gaps(std::vector<OpenPolyline> &open_poly
for (OpenPolyline *opl : sorted_by_length) { for (OpenPolyline *opl : sorted_by_length) {
if (opl->consumed) if (opl->consumed)
continue; continue;
OpenPolylineEnd end(opl, false); 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. // The end point of this polyline will be modified, thus the following entry will become invalid. Remove it.
closest_end_point_lookup.erase(end); closest_end_point_lookup.erase(end);
}
opl->consumed = true; opl->consumed = true;
size_t n_segments_joined = 1; size_t n_segments_joined = 1;
for (;;) { for (;;) {
@ -1226,7 +1345,7 @@ static void chain_open_polylines_close_gaps(std::vector<OpenPolyline> &open_poly
const OpenPolylineEnd *next_start = next_start_and_dist.first; const OpenPolylineEnd *next_start = next_start_and_dist.first;
// Check whether we closed this loop. // Check whether we closed this loop.
double current_loop_closing_distance2 = (opl->points.back() - opl->points.front()).cast<double>().squaredNorm(); double current_loop_closing_distance2 = (opl->points.back() - opl->points.front()).cast<double>().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) { 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. // Heuristics to decide, whether to close the loop, or connect another polyline.
// One should avoid closing loops shorter than max_gap_scaled. // One should avoid closing loops shorter than max_gap_scaled.
@ -1237,21 +1356,35 @@ static void chain_open_polylines_close_gaps(std::vector<OpenPolyline> &open_poly
// Mark the current segment as not consumed, otherwise the closest_end_point_lookup.erase() would fail. // Mark the current segment as not consumed, otherwise the closest_end_point_lookup.erase() would fail.
opl->consumed = false; opl->consumed = false;
closest_end_point_lookup.erase(OpenPolylineEnd(opl, true)); closest_end_point_lookup.erase(OpenPolylineEnd(opl, true));
bool midpoint_inserted = false;
if (current_loop_closing_distance2 == 0.) { if (current_loop_closing_distance2 == 0.) {
// Remove the duplicate last point. // Remove the duplicate last point.
opl->points.pop_back(); opl->points.pop_back();
} else { } else {
// The end points are different, keep both of them. // The end points are different, keep both of them.
midpoint_inserted = handle_color_at_gap_between_open_polylines<mesh_info>(*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 closed polygon is patched from pieces with messed up orientation, therefore
// the orientation of the patched up polygon is not known. // the orientation of the patched up polygon is not known.
// Orient the patched up polygons CCW. This heuristic may close some holes and cavities. // Orient the patched up polygons CCW. This heuristic may close some holes and cavities.
std::reverse(opl->points.begin(), opl->points.end()); std::reverse(opl->points.begin(), opl->points.end());
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)); loops.emplace_back(std::move(opl->points));
} }
}
opl->points.clear(); opl->points.clear();
opl->colors.clear();
opl->consumed = true; opl->consumed = true;
break; break;
} }
@ -1263,36 +1396,56 @@ static void chain_open_polylines_close_gaps(std::vector<OpenPolyline> &open_poly
closest_end_point_lookup.insert(OpenPolylineEnd(opl, false)); closest_end_point_lookup.insert(OpenPolylineEnd(opl, false));
break; break;
} }
// Attach this polyline to the end of the initial polyline. // Attach this polyline to the end of the initial polyline.
if (next_start->start) { if (next_start->start) {
auto it = next_start->polyline->points.begin(); auto pt_it = next_start->polyline->points.begin();
if (*it == opl->points.back()) auto color_it = next_start->polyline->colors.begin();
++ it; if (*pt_it == opl->points.back()) {
std::copy(it, next_start->polyline->points.end(), back_inserter(opl->points)); ++pt_it;
} else { } else {
auto it = next_start->polyline->points.rbegin(); handle_color_at_gap_between_open_polylines<mesh_info>(*opl, *pt_it, *color_it);
if (*it == opl->points.back())
++ it;
std::copy(it, next_start->polyline->points.rend(), back_inserter(opl->points));
} }
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 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<mesh_info>(*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. // Remove the end points of the consumed polyline segment from the lookup.
OpenPolyline *opl2 = next_start->polyline; OpenPolyline *opl2 = next_start->polyline;
closest_end_point_lookup.erase(OpenPolylineEnd(opl2, true)); closest_end_point_lookup.erase(OpenPolylineEnd(opl2, true));
if (try_connect_reversed) if (try_connect_reversed) {
closest_end_point_lookup.erase(OpenPolylineEnd(opl2, false)); closest_end_point_lookup.erase(OpenPolylineEnd(opl2, false));
}
opl2->points.clear(); opl2->points.clear();
opl2->colors.clear();
opl2->consumed = true; opl2->consumed = true;
// Continue with the current loop. // Continue with the current loop.
} }
} }
} }
static Polygons make_loops( template<AdditionalMeshInfo mesh_info>
static typename PolygonsType<mesh_info>::type make_loops(
// Lines will have their flags modified. // Lines will have their flags modified.
IntersectionLines &lines) IntersectionLines &lines
{ ) {
Polygons loops; using PolygonsType = typename PolygonsType<mesh_info>::type;
PolygonsType loops;
#if 0 #if 0
//FIXME slice_facet() may create zero length edges due to rounding of doubles into coord_t. //FIXME slice_facet() may create zero length edges due to rounding of doubles into coord_t.
//#ifdef _DEBUG //#ifdef _DEBUG
@ -1320,7 +1473,7 @@ static Polygons make_loops(
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
std::vector<OpenPolyline> open_polylines; std::vector<OpenPolyline> open_polylines;
chain_lines_by_triangle_connectivity(lines, loops, open_polylines); chain_lines_by_triangle_connectivity<mesh_info>(lines, loops, open_polylines);
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{ {
@ -1336,8 +1489,8 @@ static Polygons make_loops(
// Now process the open polylines. // Now process the open polylines.
// Do it in two rounds, first try to connect in the same direction only, // 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. // then try to connect the open polylines in reversed order as well.
chain_open_polylines_exact(open_polylines, loops, false); chain_open_polylines_exact<mesh_info>(open_polylines, loops, false);
chain_open_polylines_exact(open_polylines, loops, true); chain_open_polylines_exact<mesh_info>(open_polylines, loops, true);
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{ {
@ -1365,8 +1518,8 @@ static Polygons make_loops(
} }
#else #else
const double max_gap = 2.; //mm const double max_gap = 2.; //mm
chain_open_polylines_close_gaps(open_polylines, loops, max_gap, false); chain_open_polylines_close_gaps<mesh_info>(open_polylines, loops, max_gap, false);
chain_open_polylines_close_gaps(open_polylines, loops, max_gap, true); chain_open_polylines_close_gaps<mesh_info>(open_polylines, loops, max_gap, true);
#endif #endif
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
@ -1388,14 +1541,17 @@ static Polygons make_loops(
return loops; return loops;
} }
template<typename ThrowOnCancel> template<AdditionalMeshInfo mesh_info, typename ThrowOnCancel>
static std::vector<Polygons> make_loops( static std::vector<typename PolygonsType<mesh_info>::type> make_loops(
// Lines will have their flags modified. // Lines will have their flags modified.
std::vector<IntersectionLines> &lines, std::vector<IntersectionLines> &lines,
const MeshSlicingParams &params, const MeshSlicingParams &params,
ThrowOnCancel throw_on_cancel) ThrowOnCancel throw_on_cancel)
{ {
std::vector<Polygons> layers; using PolygonsType = typename PolygonsType<mesh_info>::type;
using PolygonType = typename PolygonsType::value_type;
std::vector<PolygonsType> layers;
layers.resize(lines.size()); layers.resize(lines.size());
tbb::parallel_for( tbb::parallel_for(
tbb::blocked_range<size_t>(0, lines.size()), tbb::blocked_range<size_t>(0, lines.size()),
@ -1404,31 +1560,33 @@ static std::vector<Polygons> make_loops(
if ((line_idx & 0x0ffff) == 0) if ((line_idx & 0x0ffff) == 0)
throw_on_cancel(); throw_on_cancel();
Polygons &polygons = layers[line_idx]; PolygonsType &polygons = layers[line_idx];
polygons = make_loops(lines[line_idx]); polygons = make_loops<mesh_info>(lines[line_idx]);
auto this_mode = line_idx < params.slicing_mode_normal_below_layer ? params.mode_below : params.mode; auto this_mode = line_idx < params.slicing_mode_normal_below_layer ? params.mode_below : params.mode;
if (! polygons.empty()) { if (! polygons.empty()) {
if (this_mode == MeshSlicingParams::SlicingMode::Positive) { if (this_mode == MeshSlicingParams::SlicingMode::Positive) {
// Reorient all loops to be CCW. // Reorient all loops to be CCW.
for (Polygon& p : polygons) for (PolygonType &p : polygons) {
p.make_counter_clockwise(); 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. // Keep just the largest polygon, make it CCW.
double max_area = 0.; double max_area = 0.;
Polygon* max_area_polygon = nullptr; PolygonType *max_area_polygon = nullptr;
for (Polygon& p : polygons) { for (PolygonType &p : polygons) {
double a = p.area(); if (const double a = p.area(); std::abs(a) > std::abs(max_area)) {
if (std::abs(a) > std::abs(max_area)) {
max_area = a; max_area = a;
max_area_polygon = &p; max_area_polygon = &p;
} }
} }
assert(max_area_polygon != nullptr); assert(max_area_polygon != nullptr);
if (max_area < 0.) if (max_area < 0.) {
max_area_polygon->reverse(); max_area_polygon->reverse();
Polygon p(std::move(*max_area_polygon)); }
PolygonType p(std::move(*max_area_polygon));
polygons.clear(); polygons.clear();
polygons.emplace_back(std::move(p)); polygons.emplace_back(std::move(p));
} }
@ -1534,7 +1692,7 @@ static std::vector<Polygons> make_slab_loops(
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
Polygons &loops = layers[line_idx]; Polygons &loops = layers[line_idx];
std::vector<OpenPolyline> open_polylines; std::vector<OpenPolyline> open_polylines;
chain_lines_by_triangle_connectivity(in, loops, open_polylines); chain_lines_by_triangle_connectivity<AdditionalMeshInfo::None>(in, loops, open_polylines);
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING #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); 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; ExPolygons slices;
Polygons holes; Polygons holes;
for (Polygon &loop : make_loops(lines)) for (Polygon &loop : make_loops<AdditionalMeshInfo::None>(lines))
if (loop.area() >= 0.) if (loop.area() >= 0.)
slices.emplace_back(std::move(loop)); slices.emplace_back(std::move(loop));
else else
@ -1745,7 +1903,8 @@ static inline bool is_identity(const Transform3d &trafo)
return trafo.matrix() == Transform3d::Identity().matrix(); return trafo.matrix() == Transform3d::Identity().matrix();
} }
static std::vector<stl_vertex> transform_mesh_vertices_for_slicing(const indexed_triangle_set &mesh, const Transform3d &trafo) template<AdditionalMeshInfo mesh_info = AdditionalMeshInfo::None>
static std::vector<stl_vertex> transform_mesh_vertices_for_slicing(const typename IndexedTriangleSetType<mesh_info>::type &mesh, const Transform3d &trafo)
{ {
// Copy and scale vertices in XY, don't scale in Z. // Copy and scale vertices in XY, don't scale in Z.
// Possibly apply the transformation. // Possibly apply the transformation.
@ -1769,13 +1928,23 @@ static std::vector<stl_vertex> transform_mesh_vertices_for_slicing(const indexed
return out; return out;
} }
std::vector<Polygons> slice_mesh( template<AdditionalMeshInfo mesh_info = AdditionalMeshInfo::None>
const indexed_triangle_set &mesh, std::vector<typename PolygonsType<mesh_info>::type> slice_mesh(
const typename IndexedTriangleSetType<mesh_info>::type &mesh,
// Unscaled Zs // Unscaled Zs
const std::vector<float> &zs, const std::vector<float> &zs,
const MeshSlicingParams &params, const MeshSlicingParams &params,
std::function<void()> throw_on_cancel) std::function<void()> throw_on_cancel)
{ {
using PolygonsType = typename PolygonsType<mesh_info>::type;
const FacetColorFunctor<mesh_info> facet_color_fn = [&] {
if constexpr (mesh_info == AdditionalMeshInfo::Color)
return FacetColorFunctor<mesh_info>(mesh.colors);
else
return FacetColorFunctor<mesh_info>();
}();
BOOST_LOG_TRIVIAL(debug) << "slice_mesh to polygons"; BOOST_LOG_TRIVIAL(debug) << "slice_mesh to polygons";
std::vector<IntersectionLines> lines; std::vector<IntersectionLines> lines;
@ -1785,29 +1954,29 @@ std::vector<Polygons> slice_mesh(
// Instead of edge identifiers, one shall use a sorted pair of edge vertex indices. // 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 // 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. // to make sure that no code relies on it.
std::vector<Vec3i> face_edge_ids = its_face_edge_ids(mesh); std::vector<Vec3i> face_edge_ids = its_face_edge_ids<mesh_info>(mesh);
if (zs.size() <= 1) { if (zs.size() <= 1) {
// It likely is not worthwile to copy the vertices. Apply the transformation in place. // It likely is not worthwile to copy the vertices. Apply the transformation in place.
if (is_identity(params.trafo)) { if (is_identity(params.trafo)) {
lines = slice_make_lines( lines = slice_make_lines(
mesh.vertices, [](const Vec3f &p) { return Vec3f(scaled<float>(p.x()), scaled<float>(p.y()), p.z()); }, mesh.vertices, [](const Vec3f &p) { return Vec3f(scaled<float>(p.x()), scaled<float>(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 { } else {
// Transform the vertices, scale up in XY, not in Z. // Transform the vertices, scale up in XY, not in Z.
Transform3f tf = make_trafo_for_slicing(params.trafo); 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 { } else {
// Copy and scale vertices in XY, don't scale in Z. Possibly apply the transformation. // Copy and scale vertices in XY, don't scale in Z. Possibly apply the transformation.
lines = slice_make_lines( lines = slice_make_lines(
transform_mesh_vertices_for_slicing(mesh, params.trafo), transform_mesh_vertices_for_slicing<mesh_info>(mesh, params.trafo),
[](const Vec3f &p) { return p; }, mesh.indices, face_edge_ids, zs, throw_on_cancel); [](const Vec3f &p) { return p; }, mesh.indices, face_edge_ids, facet_color_fn, zs, throw_on_cancel);
} }
} }
throw_on_cancel(); throw_on_cancel();
std::vector<Polygons> layers = make_loops(lines, params, throw_on_cancel); std::vector<PolygonsType> layers = make_loops<mesh_info>(lines, params, throw_on_cancel);
#ifdef SLIC3R_DEBUG #ifdef SLIC3R_DEBUG
{ {
@ -1845,13 +2014,43 @@ std::vector<Polygons> slice_mesh(
return layers; return layers;
} }
// Specialized version for a single slicing plane only, running on a single thread. std::vector<Polygons> slice_mesh(
Polygons slice_mesh(
const indexed_triangle_set &mesh, const indexed_triangle_set &mesh,
// Unscaled Zs // Unscaled Zs
const std::vector<float> &zs,
const MeshSlicingParams &params,
std::function<void()> throw_on_cancel)
{
return slice_mesh<AdditionalMeshInfo::None>(mesh, zs, params, throw_on_cancel);
}
std::vector<ColorPolygons> slice_mesh(
const indexed_triangle_set_with_color &mesh,
// Unscaled Zs
const std::vector<float> &zs,
const MeshSlicingParams &params,
std::function<void()> throw_on_cancel)
{
return slice_mesh<AdditionalMeshInfo::Color>(mesh, zs, params, throw_on_cancel);
}
// Specialized version for a single slicing plane only, running on a single thread.
template<AdditionalMeshInfo mesh_info = AdditionalMeshInfo::None>
typename PolygonsType<mesh_info>::type slice_mesh(
const typename IndexedTriangleSetType<mesh_info>::type &mesh,
// Unscaled Zs
const float plane_z, const float plane_z,
const MeshSlicingParams &params) const MeshSlicingParams &params)
{ {
using PolygonsType = typename PolygonsType<mesh_info>::type;
const FacetColorFunctor<mesh_info> facet_color_fn = [&] {
if constexpr (mesh_info == AdditionalMeshInfo::Color)
return FacetColorFunctor<mesh_info>(mesh.colors);
else
return FacetColorFunctor<mesh_info>();
}();
std::vector<IntersectionLines> lines; std::vector<IntersectionLines> lines;
{ {
@ -1887,27 +2086,45 @@ Polygons slice_mesh(
} }
// 3) Calculate face neighbors for just the faces in face_mask. // 3) Calculate face neighbors for just the faces in face_mask.
std::vector<Vec3i> face_edge_ids = its_face_edge_ids(mesh, face_mask); std::vector<Vec3i> face_edge_ids = its_face_edge_ids<mesh_info>(mesh, face_mask);
// 4) Slice "face_mask" triangles, collect line segments. // 4) Slice "face_mask" triangles, collect line segments.
// It likely is not worthwile to copy the vertices. Apply the transformation in place. // It likely is not worthwile to copy the vertices. Apply the transformation in place.
if (trafo_identity) { if (trafo_identity) {
lines.emplace_back(slice_make_lines( lines.emplace_back(slice_make_lines<mesh_info>(
mesh.vertices, [](const Vec3f &p) { return Vec3f(scaled<float>(p.x()), scaled<float>(p.y()), p.z()); }, mesh.vertices, [](const Vec3f &p) { return Vec3f(scaled<float>(p.x()), scaled<float>(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 { } else {
// Transform the vertices, scale up in XY, not in Z. // 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_info>(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]; })); [&face_mask](int face_idx) { return face_mask[face_idx]; }));
} }
} }
// 5) Chain the line segments. // 5) Chain the line segments.
std::vector<Polygons> layers = make_loops(lines, params, [](){}); std::vector<PolygonsType> layers = make_loops<mesh_info>(lines, params, [](){});
assert(layers.size() == 1); assert(layers.size() == 1);
return layers.front(); return layers.front();
} }
Polygons slice_mesh(
const indexed_triangle_set &mesh,
// Unscaled Zs
const float plane_z,
const MeshSlicingParams &params)
{
return slice_mesh<AdditionalMeshInfo::None>(mesh, plane_z, params);
}
ColorPolygons slice_mesh(
const indexed_triangle_set_with_color &mesh,
// Unscaled Zs
const float plane_z,
const MeshSlicingParams &params)
{
return slice_mesh<AdditionalMeshInfo::Color>(mesh, plane_z, params);
}
std::vector<ExPolygons> slice_mesh_ex( std::vector<ExPolygons> slice_mesh_ex(
const indexed_triangle_set &mesh, const indexed_triangle_set &mesh,
const std::vector<float> &zs, const std::vector<float> &zs,
@ -2272,7 +2489,7 @@ void cut_mesh(const indexed_triangle_set &mesh, float z, indexed_triangle_set *u
dst.y() = scaled<double>(src.y()); dst.y() = scaled<double>(src.y());
dst.z() = src.z(); 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) { if (slice_type != FacetSliceType::NoSlice) {

View File

@ -20,6 +20,8 @@ struct indexed_triangle_set;
namespace Slic3r { namespace Slic3r {
struct indexed_triangle_set_with_color;
struct MeshSlicingParams struct MeshSlicingParams
{ {
enum class SlicingMode : uint32_t { enum class SlicingMode : uint32_t {
@ -37,6 +39,9 @@ struct MeshSlicingParams
PositiveLargestContour, PositiveLargestContour,
}; };
MeshSlicingParams() = default;
explicit MeshSlicingParams(const Transform3d &trafo) : trafo(trafo) {}
SlicingMode mode { SlicingMode::Regular }; SlicingMode mode { SlicingMode::Regular };
// For vase mode: below this layer a different slicing mode will be used to produce a single contour. // For vase mode: below this layer a different slicing mode will be used to produce a single contour.
// 0 = ignore. // 0 = ignore.
@ -75,10 +80,21 @@ std::vector<Polygons> slice_mesh(
const MeshSlicingParams &params, const MeshSlicingParams &params,
std::function<void()> throw_on_cancel = []{}); std::function<void()> throw_on_cancel = []{});
std::vector<ColorPolygons> slice_mesh(
const indexed_triangle_set_with_color &mesh,
const std::vector<float> &zs,
const MeshSlicingParams &params,
std::function<void()> throw_on_cancel = []{});
// Specialized version for a single slicing plane only, running on a single thread. // Specialized version for a single slicing plane only, running on a single thread.
Polygons slice_mesh( Polygons slice_mesh(
const indexed_triangle_set &mesh, const indexed_triangle_set &mesh,
const float plane_z, float plane_z,
const MeshSlicingParams &params);
ColorPolygons slice_mesh(
const indexed_triangle_set_with_color &mesh,
float plane_z,
const MeshSlicingParams &params); const MeshSlicingParams &params);
std::vector<ExPolygons> slice_mesh_ex( std::vector<ExPolygons> slice_mesh_ex(