From eb6392dccdba6486a4ff6c1858651a85f19dc264 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik <bubnikv@gmail.com> Date: Mon, 26 Jul 2021 17:02:56 +0200 Subject: [PATCH] New slice_mesh() variant slicing with a single plane only, running on a single thread only (not parallelized). The new slice_mesh() is used to calculate contour of objects sunken below the print bed. --- src/libslic3r/TriangleMesh.cpp | 49 ++++++++------- src/libslic3r/TriangleMesh.hpp | 1 + src/libslic3r/TriangleMeshSlicer.cpp | 92 ++++++++++++++++++++++++++++ src/libslic3r/TriangleMeshSlicer.hpp | 6 ++ src/slic3r/GUI/3DScene.cpp | 10 ++- 5 files changed, 131 insertions(+), 27 deletions(-) diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index d709a67cd..360a8b14e 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -698,27 +698,29 @@ 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<typename ThrowOnCancelCallback> +template<typename FaceFilter, typename ThrowOnCancelCallback> static std::vector<EdgeToFace> create_edge_map( - const indexed_triangle_set &its, ThrowOnCancelCallback throw_on_cancel) + const indexed_triangle_set &its, FaceFilter face_filter, ThrowOnCancelCallback throw_on_cancel) { std::vector<EdgeToFace> edges_map; - edges_map.assign(its.indices.size() * 3, EdgeToFace()); + edges_map.reserve(its.indices.size() * 3); for (uint32_t facet_idx = 0; facet_idx < its.indices.size(); ++ facet_idx) - for (int i = 0; i < 3; ++ i) { - EdgeToFace &e2f = edges_map[facet_idx * 3 + i]; - e2f.vertex_low = its.indices[facet_idx][i]; - e2f.vertex_high = its.indices[facet_idx][(i + 1) % 3]; - e2f.face = facet_idx; - // 1 based indexing, to be always strictly positive. - e2f.face_edge = i + 1; - if (e2f.vertex_low > e2f.vertex_high) { - // Sort the vertices - std::swap(e2f.vertex_low, e2f.vertex_high); - // and make the face_edge negative to indicate a flipped edge. - e2f.face_edge = - e2f.face_edge; + if (face_filter(facet_idx)) + for (int i = 0; i < 3; ++ i) { + edges_map.push_back({}); + EdgeToFace &e2f = edges_map.back(); + e2f.vertex_low = its.indices[facet_idx][i]; + e2f.vertex_high = its.indices[facet_idx][(i + 1) % 3]; + e2f.face = facet_idx; + // 1 based indexing, to be always strictly positive. + e2f.face_edge = i + 1; + if (e2f.vertex_low > e2f.vertex_high) { + // Sort the vertices + std::swap(e2f.vertex_low, e2f.vertex_high); + // and make the face_edge negative to indicate a flipped edge. + e2f.face_edge = - e2f.face_edge; + } } - } throw_on_cancel(); std::sort(edges_map.begin(), edges_map.end()); @@ -727,12 +729,12 @@ static std::vector<EdgeToFace> 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<typename ThrowOnCancelCallback> -static inline std::vector<Vec3i> its_face_edge_ids_impl(const indexed_triangle_set &its, ThrowOnCancelCallback throw_on_cancel) +template<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) { std::vector<Vec3i> out(its.indices.size(), Vec3i(-1, -1, -1)); - std::vector<EdgeToFace> edges_map = create_edge_map(its, throw_on_cancel); + std::vector<EdgeToFace> 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; @@ -780,12 +782,17 @@ static inline std::vector<Vec3i> its_face_edge_ids_impl(const indexed_triangle_s std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its) { - return its_face_edge_ids_impl(its, [](){}); + return its_face_edge_ids_impl(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) { - return its_face_edge_ids_impl(its, 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<bool> &face_mask) +{ + 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 7aef3055a..c8c7e0dd7 100644 --- a/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -118,6 +118,7 @@ private: // Used for chaining slice lines into polygons. std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &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, const std::vector<bool> &face_mask); // 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); diff --git a/src/libslic3r/TriangleMeshSlicer.cpp b/src/libslic3r/TriangleMeshSlicer.cpp index 681ec32f0..ef90402a9 100644 --- a/src/libslic3r/TriangleMeshSlicer.cpp +++ b/src/libslic3r/TriangleMeshSlicer.cpp @@ -362,6 +362,35 @@ static inline std::vector<IntersectionLines> slice_make_lines( return lines; } +template<typename TransformVertex, typename FaceFilter> +static inline IntersectionLines slice_make_lines( + const std::vector<stl_vertex> &mesh_vertices, + const TransformVertex &transform_vertex_fn, + const std::vector<stl_triangle_vertex_indices> &mesh_faces, + const std::vector<Vec3i> &face_edge_ids, + const float plane_z, + FaceFilter face_filter) +{ + IntersectionLines lines; + for (int face_idx = 0; face_idx < 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)]) }; + // find facet extents + 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); + 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) { + assert(il.edge_type != IntersectionLine::FacetEdgeType::Horizontal); + lines.emplace_back(il); + } + } + return lines; +} + // For projecting triangle sets onto slice slabs. struct SlabLines { // Intersection lines of a slice with a triangle set, CCW oriented. @@ -1720,6 +1749,69 @@ std::vector<Polygons> slice_mesh( return layers; } +// Specialized version for a single slicing plane only, running on a single thread. +Polygons slice_mesh( + const indexed_triangle_set &mesh, + // Unscaled Zs + const float plane_z, + const MeshSlicingParams ¶ms) +{ + std::vector<IntersectionLines> lines; + + { + bool trafo_identity = is_identity(params.trafo); + Transform3f tf; + std::vector<bool> face_mask(mesh.indices.size(), false); + + { + // 1) Mark vertices as below or above the slicing plane. + std::vector<char> vertex_side(mesh.vertices.size(), 0); + if (trafo_identity) { + for (size_t i = 0; i < mesh.vertices.size(); ++ i) { + float z = mesh.vertices[i].z(); + char s = z < plane_z ? -1 : z == plane_z ? 0 : 1; + vertex_side[i] = s; + } + } else { + tf = make_trafo_for_slicing(params.trafo); + for (size_t i = 0; i < mesh.vertices.size(); ++ i) { + //FIXME don't need to transform x & y, just Z. + float z = (tf * mesh.vertices[i]).z(); + char s = z < plane_z ? -1 : z == plane_z ? 0 : 1; + vertex_side[i] = s; + } + } + + // 2) Mark faces crossing the plane. + for (size_t i = 0; i < mesh.indices.size(); ++ i) { + const Vec3i &face = mesh.indices[i]; + int sides[3] = { vertex_side[face(0)], vertex_side[face(1)], vertex_side[face(2)] }; + face_mask[i] = sides[0] * sides[1] <= 0 || sides[1] * sides[2] <= 0 || sides[0] * sides[2] <= 0; + } + } + + // 3) Calculate face neighbors for just the faces in face_mask. + std::vector<Vec3i> 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( + 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]; })); + } 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, + [&face_mask](int face_idx) { return face_mask[face_idx]; })); + } + } + + // 5) Chain the line segments. + std::vector<Polygons> layers = make_loops(lines, params, [](){}); + assert(layers.size() == 1); + return layers.front(); +} + std::vector<ExPolygons> slice_mesh_ex( const indexed_triangle_set &mesh, const std::vector<float> &zs, diff --git a/src/libslic3r/TriangleMeshSlicer.hpp b/src/libslic3r/TriangleMeshSlicer.hpp index 9b02f5573..7ea7ac3a9 100644 --- a/src/libslic3r/TriangleMeshSlicer.hpp +++ b/src/libslic3r/TriangleMeshSlicer.hpp @@ -52,6 +52,12 @@ std::vector<Polygons> slice_mesh( const MeshSlicingParams ¶ms, std::function<void()> 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, + const MeshSlicingParams ¶ms); + std::vector<ExPolygons> slice_mesh_ex( const indexed_triangle_set &mesh, const std::vector<float> &zs, diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index f2c2ee833..4e2c21cf6 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -302,7 +302,7 @@ void GLVolume::SinkingContours::update() #if ALG_SLICE == ALG_SLICE_MESH MeshSlicingParams slicing_params; slicing_params.trafo = m_parent.world_matrix(); - std::vector<Polygons> list_of_polys = slice_mesh(mesh.its, std::vector<float>{ 0.0f }, slicing_params); + Polygons polygons = slice_mesh(mesh.its, 0.0f, slicing_params); auto append_polygon = [this](const Polygon& polygon, GUI::GLModel::InitializationData& data) { if (!polygon.empty()) { @@ -326,11 +326,9 @@ void GLVolume::SinkingContours::update() m_model.reset(); GUI::GLModel::InitializationData init_data; - for (const Polygons& polygons : list_of_polys) { - for (const Polygon& polygon : polygons) { - // contour - append_polygon(polygon, init_data); - } + for (const Polygon& polygon : polygons) { + // contour + append_polygon(polygon, init_data); } #else MeshSlicingParamsEx slicing_params;