diff --git a/src/manifold/include/manifold.h b/src/manifold/include/manifold.h index 27eb68a4e..311c4fc32 100644 --- a/src/manifold/include/manifold.h +++ b/src/manifold/include/manifold.h @@ -276,6 +276,7 @@ class Manifold { */ ///@{ CrossSection Slice(float height = 0) const; + std::vector Slices(float bottomZ, float topZ, int nSlices) const; CrossSection Project() const; ///@} diff --git a/src/manifold/src/face_op.cpp b/src/manifold/src/face_op.cpp index 29bc90488..501b3a8f6 100644 --- a/src/manifold/src/face_op.cpp +++ b/src/manifold/src/face_op.cpp @@ -290,6 +290,80 @@ CrossSection Manifold::Impl::Slice(float height) const { return CrossSection(polys); } +std::vector Manifold::Impl::Slices(float bottomZ, float topZ, int nSlices) const { + std::vector sections(nSlices); + std::vector sliceHeights(nSlices); + float step = (topZ - bottomZ) / (nSlices - 1); + + for (int i = 0; i < nSlices; ++i) { + sliceHeights[i] = bottomZ + i * step; + } + + std::unordered_map> sliceToTriangles; + + int numTriangles = halfedge_.size() / 3; + + for (int tri = 0; tri < numTriangles; ++tri) { + float minZ = std::numeric_limits::infinity(); + float maxZ = -std::numeric_limits::infinity(); + for (const int j : {0, 1, 2}) { + const float z = vertPos_[halfedge_[3 * tri + j].startVert].z; + minZ = std::min(minZ, z); + maxZ = std::max(maxZ, z); + } + + for (int s = 0; s < nSlices; ++s) { + if (minZ <= sliceHeights[s] && maxZ > sliceHeights[s]) { + sliceToTriangles[s].insert(tri); + } + } + } + + for (int s = 0; s < nSlices; ++s) { + float height = sliceHeights[s]; + std::unordered_set& tris = sliceToTriangles[s]; + Polygons polys; + + while (!tris.empty()) { + const int startTri = *tris.begin(); + SimplePolygon poly; + + int k = 0; + for (const int j : {0, 1, 2}) { + if (vertPos_[halfedge_[3 * startTri + j].startVert].z > height && + vertPos_[halfedge_[3 * startTri + Next3(j)].startVert].z <= height) { + k = Next3(j); + break; + } + } + + int tri = startTri; + do { + tris.erase(tris.find(tri)); + if (vertPos_[halfedge_[3 * tri + k].endVert].z <= height) { + k = Next3(k); + } + + Halfedge up = halfedge_[3 * tri + k]; + const glm::vec3 below = vertPos_[up.startVert]; + const glm::vec3 above = vertPos_[up.endVert]; + const float a = (height - below.z) / (above.z - below.z); + poly.push_back(glm::vec2(glm::mix(below, above, a))); + + const int pair = up.pairedHalfedge; + tri = pair / 3; + k = Next3(pair % 3); + } while (tri != startTri); + + polys.push_back(poly); + } + + sections[s] = CrossSection(polys); + } + + return sections; +} + CrossSection Manifold::Impl::Project() const { const glm::mat3x2 projection = GetAxisAlignedProjection({0, 0, 1}); auto policy = autoPolicy(halfedge_.size()); diff --git a/src/manifold/src/impl.h b/src/manifold/src/impl.h index e50e417e9..3da9829b2 100644 --- a/src/manifold/src/impl.h +++ b/src/manifold/src/impl.h @@ -143,6 +143,7 @@ struct Manifold::Impl { VecView::IterC end, glm::mat3x2 projection) const; CrossSection Slice(float height) const; + std::vector Slices(float bottomZ, float topZ, int nSlices) const; CrossSection Project() const; // edge_op.cu diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index 8026f52b2..936e549a7 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -889,6 +889,11 @@ CrossSection Manifold::Slice(float height) const { return GetCsgLeafNode().GetImpl()->Slice(height); } +std::vector Manifold::Slices(float bottomZ, float topZ, int nSlices) const { + return GetCsgLeafNode().GetImpl()->Slices(bottomZ, topZ, nSlices); + +} + /** * Returns a cross section representing the projected outline of this object * onto the X-Y plane. diff --git a/test/manifold_test.cpp b/test/manifold_test.cpp index 20c990150..d33b02ea8 100644 --- a/test/manifold_test.cpp +++ b/test/manifold_test.cpp @@ -609,6 +609,18 @@ TEST(Manifold, Slice) { EXPECT_EQ(top.Area(), 0); } +TEST(Manifold, Slices) { + Manifold cube = Manifold::Tetrahedron().Scale({10, 10, 10}); + std::vector slices = cube.Slices(1.0, 8.0, 8); + EXPECT_EQ(slices.size(), 8); + EXPECT_GT(slices[0].Area(), 0); + for (int i = 0; i < slices.size() - 1; i++) { + auto& slice = slices[i]; + auto& nSlice = slices[i+1]; + EXPECT_GT(slice.Area(), nSlice.Area()); + } +} + TEST(Manifold, MeshRelation) { Mesh gyroidMesh = Gyroid(); MeshGL gyroidMeshGL = WithIndexColors(gyroidMesh);