From 1b8ba7a6b83db0de42d9b4904371efb7e0396ad7 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Tue, 5 Dec 2023 14:28:34 -0800 Subject: [PATCH 1/3] [FEAT] Add Sweep Operation This PR is an attempt to break out a successful experiment from Python. Does this make sense to move to Impl? --- bindings/c/include/manifoldc.h | 5 +++ bindings/c/manifoldc.cpp | 7 ++++ bindings/python/manifold3d.cpp | 15 ++++++++ bindings/wasm/bindings.cpp | 1 + bindings/wasm/bindings.js | 4 ++ bindings/wasm/helpers.cpp | 7 ++++ .../wasm/manifold-encapsulated-types.d.ts | 10 +++++ src/manifold/include/manifold.h | 2 + src/manifold/src/manifold.cpp | 37 +++++++++++++++++++ test/manifold_test.cpp | 9 +++++ 10 files changed, 97 insertions(+) diff --git a/bindings/c/include/manifoldc.h b/bindings/c/include/manifoldc.h index e49e9ee46..1998aa703 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..134c52450 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", select_overload(&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..c7304b99b 100644 --- a/bindings/wasm/helpers.cpp +++ b/bindings/wasm/helpers.cpp @@ -187,6 +187,13 @@ 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..ed296a948 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..689f46ab4 100644 --- a/test/manifold_test.cpp +++ b/test/manifold_test.cpp @@ -759,3 +759,12 @@ 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); +} From 007acd970bfc7d21af9626d094989d873c97cb72 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Tue, 5 Dec 2023 14:33:30 -0800 Subject: [PATCH 2/3] Fix Formatting --- bindings/c/include/manifoldc.h | 2 +- bindings/wasm/helpers.cpp | 4 +++- src/manifold/src/manifold.cpp | 2 +- test/manifold_test.cpp | 4 ++-- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/bindings/c/include/manifoldc.h b/bindings/c/include/manifoldc.h index 1998aa703..77d29e4bf 100644 --- a/bindings/c/include/manifoldc.h +++ b/bindings/c/include/manifoldc.h @@ -120,7 +120,7 @@ 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); + float y, float z); // Manifold Transformations diff --git a/bindings/wasm/helpers.cpp b/bindings/wasm/helpers.cpp index c7304b99b..c5e8e81e8 100644 --- a/bindings/wasm/helpers.cpp +++ b/bindings/wasm/helpers.cpp @@ -190,7 +190,9 @@ Manifold IntersectionN(const std::vector& manifolds) { 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]; + offset[0] = array[0]; + offset[1] = array[1]; + offset[2] = array[2]; return manifold.Sweep(offset); } diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index ed296a948..9022a899f 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -845,7 +845,7 @@ Manifold Manifold::Sweep(glm::vec3 v) const { 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 + 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], diff --git a/test/manifold_test.cpp b/test/manifold_test.cpp index 689f46ab4..1db511d18 100644 --- a/test/manifold_test.cpp +++ b/test/manifold_test.cpp @@ -762,8 +762,8 @@ TEST(Manifold, EmptyHull) { 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 + {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); From e78c21aeba1aec52ec4dfb8051e20fa2db57b2a7 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Tue, 5 Dec 2023 14:52:37 -0800 Subject: [PATCH 3/3] Add More Test Coverage --- bindings/wasm/bindings.cpp | 2 +- test/manifold_test.cpp | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/bindings/wasm/bindings.cpp b/bindings/wasm/bindings.cpp index 134c52450..1da1bae7a 100644 --- a/bindings/wasm/bindings.cpp +++ b/bindings/wasm/bindings.cpp @@ -139,7 +139,7 @@ EMSCRIPTEN_BINDINGS(whatever) { .function("_SplitByPlane", &man_js::SplitByPlane) .function("_TrimByPlane", &Manifold::TrimByPlane) .function("hull", select_overload(&Manifold::Hull)) - .function("sweep", select_overload(&Manifold::Sweep)) + .function("sweep", &Manifold::Sweep) .function("_GetMeshJS", &js::GetMeshJS) .function("refine", &Manifold::Refine) .function("_Warp", &man_js::Warp) diff --git a/test/manifold_test.cpp b/test/manifold_test.cpp index 1db511d18..c59587825 100644 --- a/test/manifold_test.cpp +++ b/test/manifold_test.cpp @@ -768,3 +768,10 @@ TEST(Manifold, SweptCube) { 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); +}