diff --git a/bindings/c/include/manifoldc.h b/bindings/c/include/manifoldc.h index e49e9ee46..77d29e4bf 100644 --- a/bindings/c/include/manifoldc.h +++ b/bindings/c/include/manifoldc.h @@ -117,6 +117,11 @@ ManifoldManifold *manifold_hull(void *mem, ManifoldManifold *m); ManifoldManifold *manifold_batch_hull(void *mem, ManifoldManifoldVec *ms); ManifoldManifold *manifold_hull_pts(void *mem, ManifoldVec3 *ps, size_t length); +// Sweeps + +ManifoldManifold *manifold_sweep(void *mem, ManifoldManifold *m, float x, + float y, float z); + // Manifold Transformations ManifoldManifold *manifold_translate(void *mem, ManifoldManifold *m, float x, diff --git a/bindings/c/manifoldc.cpp b/bindings/c/manifoldc.cpp index 44843b80b..946f7d4c1 100644 --- a/bindings/c/manifoldc.cpp +++ b/bindings/c/manifoldc.cpp @@ -222,6 +222,13 @@ ManifoldManifold *manifold_hull_pts(void *mem, ManifoldVec3 *ps, return to_c(new (mem) Manifold(hulled)); } +ManifoldManifold *manifold_sweep(void *mem, ManifoldManifold *m, float x, + float y, float z) { + auto v = glm::vec3(x, y, z); + auto swept = from_c(m)->Sweep(v); + return to_c(new (mem) Manifold(swept)); +} + ManifoldManifold *manifold_translate(void *mem, ManifoldManifold *m, float x, float y, float z) { auto v = glm::vec3(x, y, z); diff --git a/bindings/python/manifold3d.cpp b/bindings/python/manifold3d.cpp index fab045af1..4381d53d8 100644 --- a/bindings/python/manifold3d.cpp +++ b/bindings/python/manifold3d.cpp @@ -153,6 +153,21 @@ NB_MODULE(manifold3d, m) { return Manifold::Hull(vec); }, "Compute the convex hull enveloping a set of 3d points.") + .def( + "sweep", + [](Manifold &self, float x = 0.0f, float y = 0.0f, float z = 0.0f) { + return self.Sweep(glm::vec3(x, y, z)); + }, + nb::arg("x") = 0.0f, nb::arg("y") = 0.0f, nb::arg("z") = 0.0f, + "Sweep this Manifold through space." + "\n\n" + ":param x: X axis translation. (default 0.0).\n" + ":param y: Y axis translation. (default 0.0).\n" + ":param z: Z axis translation. (default 0.0).") + .def("sweep", &Manifold::Sweep, nb::arg("t"), + "Sweep this Manifold through space." + "\n\n" + ":param v: The vector to add to every vertex.") .def( "transform", [](Manifold &self, nb::ndarray> &mat) { diff --git a/bindings/wasm/bindings.cpp b/bindings/wasm/bindings.cpp index 7357bb3ca..1da1bae7a 100644 --- a/bindings/wasm/bindings.cpp +++ b/bindings/wasm/bindings.cpp @@ -139,6 +139,7 @@ EMSCRIPTEN_BINDINGS(whatever) { .function("_SplitByPlane", &man_js::SplitByPlane) .function("_TrimByPlane", &Manifold::TrimByPlane) .function("hull", select_overload(&Manifold::Hull)) + .function("sweep", &Manifold::Sweep) .function("_GetMeshJS", &js::GetMeshJS) .function("refine", &Manifold::Refine) .function("_Warp", &man_js::Warp) diff --git a/bindings/wasm/bindings.js b/bindings/wasm/bindings.js index ac49132c5..4cf365907 100644 --- a/bindings/wasm/bindings.js +++ b/bindings/wasm/bindings.js @@ -244,6 +244,10 @@ Module.setup = function() { return out; }; + Module.Manifold.prototype.sweep = function(...vec) { + return this.sweep(vararg2vec3(vec)); + }; + Module.Manifold.prototype.translate = function(...vec) { return this._Translate(vararg2vec3(vec)); }; diff --git a/bindings/wasm/helpers.cpp b/bindings/wasm/helpers.cpp index c8da6b956..c5e8e81e8 100644 --- a/bindings/wasm/helpers.cpp +++ b/bindings/wasm/helpers.cpp @@ -187,6 +187,15 @@ Manifold IntersectionN(const std::vector& manifolds) { return Manifold::BatchBoolean(manifolds, OpType::Intersect); } +Manifold Sweep(Manifold& manifold, const val& v) { + std::vector array = convertJSArrayToNumberVector(v); + glm::vec3 offset; + offset[0] = array[0]; + offset[1] = array[1]; + offset[2] = array[2]; + return manifold.Sweep(offset); +} + Manifold Transform(Manifold& manifold, const val& mat) { std::vector array = convertJSArrayToNumberVector(mat); glm::mat4x3 matrix; diff --git a/bindings/wasm/manifold-encapsulated-types.d.ts b/bindings/wasm/manifold-encapsulated-types.d.ts index 747204777..546395439 100644 --- a/bindings/wasm/manifold-encapsulated-types.d.ts +++ b/bindings/wasm/manifold-encapsulated-types.d.ts @@ -698,6 +698,16 @@ export class Manifold { */ static hull(points: (Manifold|Vec3)[]): Manifold; + // Sweeps + + /** + * Sweep this Manifold through space. + * + * @param v The vector to add to every vertex. + */ + sweep(v: Vec3): Manifold; + sweep(x: number, y?: number, z?: number): Manifold; + // Topological Operations /** diff --git a/src/manifold/include/manifold.h b/src/manifold/include/manifold.h index a4430e63f..51b0488ba 100644 --- a/src/manifold/include/manifold.h +++ b/src/manifold/include/manifold.h @@ -238,6 +238,8 @@ class Manifold { static Manifold Hull(const std::vector& manifolds); static Manifold Hull(const std::vector& pts); + Manifold Sweep(glm::vec3) const; + /** @name Testing hooks * These are just for internal testing. */ diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index 2753242b8..9022a899f 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -824,4 +824,41 @@ Manifold Manifold::Hull() const { return Hull(GetMesh().vertPos); } Manifold Manifold::Hull(const std::vector& manifolds) { return Compose(manifolds).Hull(); } + +/** + * Sweep this Manifold through space. + * + * @param v The vector to add to every vertex. + */ +Manifold Manifold::Sweep(glm::vec3 v) const { + // Slow(?) isConvex Check - Make Faster?? + bool isConvex = GetProperties().volume == Hull().GetProperties().volume; + + if (isConvex) { + // If Convex, just hull with its translated self + return Hull({*this, Translate(v)}); + } else { + // Else, sweep each triangle individually + Mesh mesh = GetMesh(); + Manifold output = AsOriginal(); + for (size_t i = 0; i < mesh.triVerts.size(); i++) { + glm::vec3 normal = glm::cross( + mesh.vertPos[mesh.triVerts[i].y] - mesh.vertPos[mesh.triVerts[i].x], + mesh.vertPos[mesh.triVerts[i].z] - mesh.vertPos[mesh.triVerts[i].x]); + if (glm::dot(normal, v) > -0.0001f) { // Only Sweep Forward Triangles + Manifold sweptTriangle = Hull({ + mesh.vertPos[mesh.triVerts[i].x], + mesh.vertPos[mesh.triVerts[i].y], + mesh.vertPos[mesh.triVerts[i].z], + mesh.vertPos[mesh.triVerts[i].x] + v, + mesh.vertPos[mesh.triVerts[i].y] + v, + mesh.vertPos[mesh.triVerts[i].z] + v, + }); + output += sweptTriangle; + } + } + return output; + } +} + } // namespace manifold diff --git a/test/manifold_test.cpp b/test/manifold_test.cpp index 15a94d97d..c59587825 100644 --- a/test/manifold_test.cpp +++ b/test/manifold_test.cpp @@ -759,3 +759,19 @@ TEST(Manifold, EmptyHull) { {0, 0, 0}, {1, 0, 0}, {0, 1, 0}, {1, 1, 0}}; EXPECT_TRUE(Manifold::Hull(coplanar).IsEmpty()); } + +TEST(Manifold, SweptCube) { + std::vector cubePts = { + {0, 0, 0}, {1, 0, 0}, {0, 1, 0}, {0, 0, 1}, // corners + {1, 1, 0}, {0, 1, 1}, {1, 0, 1}, {1, 1, 1}, // corners + }; + auto sweptCube = Manifold::Hull(cubePts).Sweep({1, 0, 0}); + EXPECT_FLOAT_EQ(sweptCube.GetProperties().volume, 2); +} + +TEST(Manifold, SweptComplex) { + auto sphere = Manifold::Sphere(0.6, 20); + auto cube = Manifold::Cube({1.0, 1.0, 1.0}, true); + auto sweptShape = (cube - sphere).Sweep({0.1, 0.3, 0.1}); + EXPECT_FLOAT_EQ(sweptShape.GetProperties().volume, 0.7779319882392883); +}