From bcd06a43927dd952ce360a011489db987bd012e3 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Tue, 19 Dec 2023 15:15:28 -0800 Subject: [PATCH 01/47] Add Naive Minkowski Sum Implementation --- bindings/python/manifold3d.cpp | 6 ++ src/manifold/include/manifold.h | 9 +++ src/manifold/src/face_op.cpp | 24 ++++++ src/manifold/src/impl.h | 1 + src/manifold/src/manifold.cpp | 137 ++++++++++++++++++++++++++++++++ test/manifold_test.cpp | 70 ++++++++++++++++ 6 files changed, 247 insertions(+) diff --git a/bindings/python/manifold3d.cpp b/bindings/python/manifold3d.cpp index 9c2fd7daf..bb49a5b1e 100644 --- a/bindings/python/manifold3d.cpp +++ b/bindings/python/manifold3d.cpp @@ -209,6 +209,9 @@ NB_MODULE(manifold3d, m) { "batch_hull", [](std::vector &ms) { return Manifold::Hull(ms); }, "Compute the convex hull enveloping a set of manifolds.") + .def_static( + "batch_batch_hull", Manifold::BatchHull, + "Compute the convex hulls enveloping multiple sets of manifolds.") .def_static( "hull_points", [](std::vector &pts) { @@ -220,6 +223,9 @@ NB_MODULE(manifold3d, m) { return Manifold::Hull(vec); }, "Compute the convex hull enveloping a set of 3d points.") + .def_static("minkowski", Manifold::Minkowski, nb::arg("a"), nb::arg("b"), + nb::arg("useThreading"), + "Compute the minkowski sum of two manifolds.") .def( "transform", [](Manifold &self, nb::ndarray> &mat) { diff --git a/src/manifold/include/manifold.h b/src/manifold/include/manifold.h index 11366d5c8..1deef1bfe 100644 --- a/src/manifold/include/manifold.h +++ b/src/manifold/include/manifold.h @@ -249,6 +249,15 @@ class Manifold { Manifold Hull() const; static Manifold Hull(const std::vector& manifolds); static Manifold Hull(const std::vector& pts); + static std::vector BatchHull( + const std::vector>& manifolds); + ///@} + + /** @name Minkowski Functions + */ + ///@{ + static Manifold Minkowski(const Manifold& a, const Manifold& b, + bool useThreading = false); ///@} /** @name Testing hooks diff --git a/src/manifold/src/face_op.cpp b/src/manifold/src/face_op.cpp index 331287655..80aaa320f 100644 --- a/src/manifold/src/face_op.cpp +++ b/src/manifold/src/face_op.cpp @@ -316,4 +316,28 @@ CrossSection Manifold::Impl::Project() const { return CrossSection(polys).Simplify(precision_); } + +std::vector Manifold::Impl::ReflexFaces(double tolerance) const { + std::unordered_set uniqueReflexFaceSet; + for (size_t i = 0; i < halfedge_.size(); i++) { + Halfedge halfedge = halfedge_[i]; + int faceA = halfedge.face; + int faceB = halfedge_[halfedge.pairedHalfedge].face; + glm::dvec3 tangent = + glm::cross((glm::dvec3)faceNormal_[faceA], + (glm::dvec3)vertPos_[halfedge_[i].endVert] - + (glm::dvec3)vertPos_[halfedge_[i].startVert]); + double tangentProjection = + glm::dot((glm::dvec3)faceNormal_[faceB], tangent); + // If we've found a pair of reflex triangles, add them to the set + if (tangentProjection > tolerance) { + uniqueReflexFaceSet.insert(faceA); + uniqueReflexFaceSet.insert(faceB); + } + } + std::vector uniqueFaces; // Copy to a vector for indexed access + uniqueFaces.insert(uniqueFaces.end(), uniqueReflexFaceSet.begin(), + uniqueReflexFaceSet.end()); + return uniqueFaces; +} } // namespace manifold diff --git a/src/manifold/src/impl.h b/src/manifold/src/impl.h index 36e37fcc0..007fa1657 100644 --- a/src/manifold/src/impl.h +++ b/src/manifold/src/impl.h @@ -121,6 +121,7 @@ struct Manifold::Impl { glm::mat3x2 projection) const; CrossSection Slice(float height) const; CrossSection Project() const; + std::vector ReflexFaces(double tolerance = 1e-8) const; // edge_op.cu void SimplifyTopology(); diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index 841339aac..e64dd907f 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -61,6 +61,40 @@ struct UpdateProperties { } }; +struct ComputeTriangleHull { + const Manifold* manifold; + std::vector* vertPos; + std::vector* output; + void operator()(thrust::tuple inOut) { + const int idx = thrust::get<0>(inOut); + glm::ivec3& tri = thrust::get<1>(inOut); + (*output)[idx] = Manifold::Hull({(*manifold).Translate((*vertPos)[tri.x]), + (*manifold).Translate((*vertPos)[tri.y]), + (*manifold).Translate((*vertPos)[tri.z])}); + } +}; + +struct ComputeTriangleTriangleHull { + std::vector* vertPosAPtr; + std::vector* vertPosBPtr; + std::vector* output; + void operator()(thrust::tuple> inOut) { + const int idx = thrust::get<0>(inOut); + std::pair tris = thrust::get<1>(inOut); + std::vector vertPosA = *vertPosAPtr, vertPosB = *vertPosBPtr; + (*output)[idx] = + Manifold::Hull({vertPosA[tris.first.x] + vertPosB[tris.second.x], + vertPosA[tris.first.x] + vertPosB[tris.second.y], + vertPosA[tris.first.x] + vertPosB[tris.second.z], + vertPosA[tris.first.y] + vertPosB[tris.second.x], + vertPosA[tris.first.y] + vertPosB[tris.second.y], + vertPosA[tris.first.y] + vertPosB[tris.second.z], + vertPosA[tris.first.z] + vertPosB[tris.second.x], + vertPosA[tris.first.z] + vertPosB[tris.second.y], + vertPosA[tris.first.z] + vertPosB[tris.second.z]}); + } +}; + Manifold Halfspace(Box bBox, glm::vec3 normal, float originOffset) { normal = glm::normalize(normal); Manifold cutter = @@ -859,4 +893,107 @@ Manifold Manifold::Hull() const { return Hull(GetMesh().vertPos); } Manifold Manifold::Hull(const std::vector& manifolds) { return Compose(manifolds).Hull(); } + +/** + * Compute the convex hulls enveloping many sets of manifolds. + * + * @param manifolds A vector of vectors of manifolds over which compute hulls. + */ +std::vector Manifold::BatchHull( + const std::vector>& manifolds) { + ZoneScoped; + std::vector output; + output.reserve(manifolds.size()); + output.resize(manifolds.size()); + thrust::for_each_n( + thrust::host, zip(manifolds.begin(), output.begin()), manifolds.size(), + [](thrust::tuple, Manifold&> inOut) { + thrust::get<1>(inOut) = Manifold::Hull(thrust::get<0>(inOut)); + }); + return output; +} + +/** + * Compute the minkowski sum of two manifolds. + * + * @param a The first manifold in the sum. + * @param b The second manifold in the sum. + */ +Manifold Manifold::Minkowski(const Manifold& a, const Manifold& b, + bool useThreading) { + std::vector composedHulls({a}); + bool aConvex = a.GetCsgLeafNode().GetImpl()->ReflexFaces().size() == 0; + bool bConvex = b.GetCsgLeafNode().GetImpl()->ReflexFaces().size() == 0; + + // If the convex manifold was supplied first, swap them! + Manifold aM = a, bM = b; + if (aConvex && !bConvex) { + aM = b; + bM = a; + aConvex = !aConvex; + bConvex = !bConvex; + } + + manifold::Mesh aMesh = aM.GetMesh(); + + // Convex-Convex Minkowski: Very Fast + if (aConvex && bConvex) { + std::vector simpleHull; + for (glm::vec3 vertex : aMesh.vertPos) { + simpleHull.push_back(bM.Translate(vertex)); + } + composedHulls.push_back(Manifold::Hull(simpleHull)); + // Convex - Non-Convex Minkowski: Slower + } else if (!aConvex && bConvex) { + if (useThreading) { + composedHulls.resize(aMesh.triVerts.size() + 1); + thrust::for_each_n( + thrust::host, zip(countAt(1), aMesh.triVerts.begin()), + aMesh.triVerts.size(), + ComputeTriangleHull({&bM, &aMesh.vertPos, &composedHulls})); + } else { + std::vector> composedParts; + for (glm::ivec3 vertexIndices : aMesh.triVerts) { + composedParts.push_back({bM.Translate(aMesh.vertPos[vertexIndices.x]), + bM.Translate(aMesh.vertPos[vertexIndices.y]), + bM.Translate(aMesh.vertPos[vertexIndices.z])}); + } + auto newHulls = Manifold::BatchHull(composedParts); + composedHulls.insert(composedHulls.end(), newHulls.begin(), + newHulls.end()); + } + // Non-Convex - Non-Convex Minkowski: Very Slow + } else if (!aConvex && !bConvex) { + manifold::Mesh bMesh = bM.GetMesh(); + if (useThreading) { + std::vector> trianglePairs; + for (glm::ivec3 aVertexIndices : aMesh.triVerts) { + for (glm::ivec3 bVertexIndices : bMesh.triVerts) { + trianglePairs.push_back({aVertexIndices, bVertexIndices}); + } + } + composedHulls.resize(trianglePairs.size() + 1); + thrust::for_each_n(thrust::host, zip(countAt(1), trianglePairs.begin()), + trianglePairs.size(), + ComputeTriangleTriangleHull( + {&aMesh.vertPos, &bMesh.vertPos, &composedHulls})); + } else { + for (glm::ivec3 aIndices : aMesh.triVerts) { + for (glm::ivec3 bIndices : bMesh.triVerts) { + composedHulls.push_back(Manifold::Hull( + {aMesh.vertPos[aIndices.x] + bMesh.vertPos[bIndices.x], + aMesh.vertPos[aIndices.x] + bMesh.vertPos[bIndices.y], + aMesh.vertPos[aIndices.x] + bMesh.vertPos[bIndices.z], + aMesh.vertPos[aIndices.y] + bMesh.vertPos[bIndices.x], + aMesh.vertPos[aIndices.y] + bMesh.vertPos[bIndices.y], + aMesh.vertPos[aIndices.y] + bMesh.vertPos[bIndices.z], + aMesh.vertPos[aIndices.z] + bMesh.vertPos[bIndices.x], + aMesh.vertPos[aIndices.z] + bMesh.vertPos[bIndices.y], + aMesh.vertPos[aIndices.z] + bMesh.vertPos[bIndices.z]})); + } + } + } + } + return Manifold::BatchBoolean(composedHulls, manifold::OpType::Add); +} } // namespace manifold diff --git a/test/manifold_test.cpp b/test/manifold_test.cpp index 14884af8a..5c6f6c3fa 100644 --- a/test/manifold_test.cpp +++ b/test/manifold_test.cpp @@ -784,3 +784,73 @@ TEST(Manifold, EmptyHull) { {0, 0, 0}, {1, 0, 0}, {0, 1, 0}, {1, 1, 0}}; EXPECT_TRUE(Manifold::Hull(coplanar).IsEmpty()); } + +TEST(Manifold, ConvexConvexMinkowski) { + Manifold sphere = Manifold::Sphere(0.1, 20); + Manifold cube = Manifold::Cube(); + Manifold sum = Manifold::Minkowski(cube, sphere); + EXPECT_FLOAT_EQ(sum.GetProperties().volume, 1.6966598f); +} + +TEST(Manifold, ConvexConvexMinkowskiThreaded) { + Manifold sphere = Manifold::Sphere(0.1, 20); + Manifold cube = Manifold::Cube(); + Manifold sum = Manifold::Minkowski(cube, sphere, true); + EXPECT_FLOAT_EQ(sum.GetProperties().volume, 1.6966598f); +} + +TEST(Manifold, NonConvexConvexMinkowski) { + Manifold sphere = Manifold::Sphere(0.6, 20); + Manifold cube = Manifold::Cube({1.0, 1.0, 1.0}, true); + Manifold nonConvex = cube - sphere; + Manifold sum = Manifold::Minkowski(nonConvex, Manifold::Sphere(0.1, 20)); + EXPECT_FLOAT_EQ(sum.GetProperties().volume, 1.0686193f); +} + +TEST(Manifold, NonConvexConvexMinkowskiThreaded) { + Manifold sphere = Manifold::Sphere(0.6, 20); + Manifold cube = Manifold::Cube({1.0, 1.0, 1.0}, true); + Manifold nonConvex = cube - sphere; + Manifold sum = Manifold::Minkowski(nonConvex, Manifold::Sphere(0.1, 20), true); + EXPECT_FLOAT_EQ(sum.GetProperties().volume, 1.0686193f); +} + +TEST(Manifold, NonConvexNonConvexMinkowski) { + Manifold smallCube = Manifold::Cube({0.1, 0.1, 0.1}, true); + std::vector verts = smallCube.GetMesh().vertPos; + Manifold star = Manifold::Manifold(); + for (glm::vec3 point : + {glm::vec3(0.2, 0.0, 0.0), glm::vec3(-0.2, 0.0, 0.0), + glm::vec3(0.0, 0.2, 0.0), glm::vec3(0.0, -0.2, 0.0), + glm::vec3(0.0, 0.0, 0.2), glm::vec3(0.0, 0.0, -0.2)}) { + verts.push_back(point); + star += Manifold::Hull(verts); + verts.pop_back(); + } + + Manifold sphere = Manifold::Sphere(0.6, 20); + Manifold cube = Manifold::Cube({1.0, 1.0, 1.0}, true); + Manifold nonConvex = cube - sphere; + Manifold sum = Manifold::Minkowski(nonConvex, star); + EXPECT_FLOAT_EQ(sum.GetProperties().volume, 1.643248); +} + +TEST(Manifold, NonConvexNonConvexMinkowskiThreaded) { + Manifold smallCube = Manifold::Cube({0.1, 0.1, 0.1}, true); + std::vector verts = smallCube.GetMesh().vertPos; + Manifold star = Manifold::Manifold(); + for (glm::vec3 point : + {glm::vec3(0.2, 0.0, 0.0), glm::vec3(-0.2, 0.0, 0.0), + glm::vec3(0.0, 0.2, 0.0), glm::vec3(0.0, -0.2, 0.0), + glm::vec3(0.0, 0.0, 0.2), glm::vec3(0.0, 0.0, -0.2)}) { + verts.push_back(point); + star += Manifold::Hull(verts); + verts.pop_back(); + } + + Manifold sphere = Manifold::Sphere(0.6, 20); + Manifold cube = Manifold::Cube({1.0, 1.0, 1.0}, true); + Manifold nonConvex = cube - sphere; + Manifold sum = Manifold::Minkowski(nonConvex, star, true); + EXPECT_FLOAT_EQ(sum.GetProperties().volume, 1.643248); +} From 89a9deab15ca6621318d52f29f60dcc54f764c4f Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Tue, 19 Dec 2023 18:38:14 -0800 Subject: [PATCH 02/47] Fix CI Bellyaching --- bindings/python/manifold3d.cpp | 3 --- src/manifold/include/manifold.h | 2 -- src/manifold/src/manifold.cpp | 29 +++++++++-------------------- test/manifold_test.cpp | 13 ++++++------- 4 files changed, 15 insertions(+), 32 deletions(-) diff --git a/bindings/python/manifold3d.cpp b/bindings/python/manifold3d.cpp index bb49a5b1e..b987911f5 100644 --- a/bindings/python/manifold3d.cpp +++ b/bindings/python/manifold3d.cpp @@ -209,9 +209,6 @@ NB_MODULE(manifold3d, m) { "batch_hull", [](std::vector &ms) { return Manifold::Hull(ms); }, "Compute the convex hull enveloping a set of manifolds.") - .def_static( - "batch_batch_hull", Manifold::BatchHull, - "Compute the convex hulls enveloping multiple sets of manifolds.") .def_static( "hull_points", [](std::vector &pts) { diff --git a/src/manifold/include/manifold.h b/src/manifold/include/manifold.h index 1deef1bfe..84646f47c 100644 --- a/src/manifold/include/manifold.h +++ b/src/manifold/include/manifold.h @@ -249,8 +249,6 @@ class Manifold { Manifold Hull() const; static Manifold Hull(const std::vector& manifolds); static Manifold Hull(const std::vector& pts); - static std::vector BatchHull( - const std::vector>& manifolds); ///@} /** @name Minkowski Functions diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index e64dd907f..141229f2e 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -894,25 +894,6 @@ Manifold Manifold::Hull(const std::vector& manifolds) { return Compose(manifolds).Hull(); } -/** - * Compute the convex hulls enveloping many sets of manifolds. - * - * @param manifolds A vector of vectors of manifolds over which compute hulls. - */ -std::vector Manifold::BatchHull( - const std::vector>& manifolds) { - ZoneScoped; - std::vector output; - output.reserve(manifolds.size()); - output.resize(manifolds.size()); - thrust::for_each_n( - thrust::host, zip(manifolds.begin(), output.begin()), manifolds.size(), - [](thrust::tuple, Manifold&> inOut) { - thrust::get<1>(inOut) = Manifold::Hull(thrust::get<0>(inOut)); - }); - return output; -} - /** * Compute the minkowski sum of two manifolds. * @@ -958,7 +939,15 @@ Manifold Manifold::Minkowski(const Manifold& a, const Manifold& b, bM.Translate(aMesh.vertPos[vertexIndices.y]), bM.Translate(aMesh.vertPos[vertexIndices.z])}); } - auto newHulls = Manifold::BatchHull(composedParts); + std::vector newHulls; + newHulls.reserve(composedParts.size()); + newHulls.resize(composedParts.size()); + thrust::for_each_n( + thrust::host, zip(composedParts.begin(), newHulls.begin()), + composedParts.size(), + [](thrust::tuple, Manifold&> inOut) { + thrust::get<1>(inOut) = Manifold::Hull(thrust::get<0>(inOut)); + }); composedHulls.insert(composedHulls.end(), newHulls.begin(), newHulls.end()); } diff --git a/test/manifold_test.cpp b/test/manifold_test.cpp index 5c6f6c3fa..a0d54709c 100644 --- a/test/manifold_test.cpp +++ b/test/manifold_test.cpp @@ -811,14 +811,14 @@ TEST(Manifold, NonConvexConvexMinkowskiThreaded) { Manifold sphere = Manifold::Sphere(0.6, 20); Manifold cube = Manifold::Cube({1.0, 1.0, 1.0}, true); Manifold nonConvex = cube - sphere; - Manifold sum = Manifold::Minkowski(nonConvex, Manifold::Sphere(0.1, 20), true); + Manifold sum = + Manifold::Minkowski(nonConvex, Manifold::Sphere(0.1, 20), true); EXPECT_FLOAT_EQ(sum.GetProperties().volume, 1.0686193f); } TEST(Manifold, NonConvexNonConvexMinkowski) { - Manifold smallCube = Manifold::Cube({0.1, 0.1, 0.1}, true); - std::vector verts = smallCube.GetMesh().vertPos; - Manifold star = Manifold::Manifold(); + Manifold star = Manifold::Cube({0.1, 0.1, 0.1}, true); + std::vector verts = star.GetMesh().vertPos; for (glm::vec3 point : {glm::vec3(0.2, 0.0, 0.0), glm::vec3(-0.2, 0.0, 0.0), glm::vec3(0.0, 0.2, 0.0), glm::vec3(0.0, -0.2, 0.0), @@ -836,9 +836,8 @@ TEST(Manifold, NonConvexNonConvexMinkowski) { } TEST(Manifold, NonConvexNonConvexMinkowskiThreaded) { - Manifold smallCube = Manifold::Cube({0.1, 0.1, 0.1}, true); - std::vector verts = smallCube.GetMesh().vertPos; - Manifold star = Manifold::Manifold(); + Manifold star = Manifold::Cube({0.1, 0.1, 0.1}, true); + std::vector verts = star.GetMesh().vertPos; for (glm::vec3 point : {glm::vec3(0.2, 0.0, 0.0), glm::vec3(-0.2, 0.0, 0.0), glm::vec3(0.0, 0.2, 0.0), glm::vec3(0.0, -0.2, 0.0), From 30be9a06782d343af7db68ad06dbb8e722369129 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Tue, 19 Dec 2023 23:25:17 -0800 Subject: [PATCH 03/47] Add Python Minkowski Test --- bindings/python/examples/minkowski.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 bindings/python/examples/minkowski.py diff --git a/bindings/python/examples/minkowski.py b/bindings/python/examples/minkowski.py new file mode 100644 index 000000000..e41932374 --- /dev/null +++ b/bindings/python/examples/minkowski.py @@ -0,0 +1,25 @@ +import numpy as np +from manifold3d import Manifold + + +def run(): + small_cube = Manifold.cube(0.1, 0.1, 0.1, True) + star = Manifold.as_original(small_cube) + for offset in [ + [[0.2, 0.0, 0.0]], + [[-0.2, 0.0, 0.0]], + [[0.0, 0.2, 0.0]], + [[0.0, -0.2, 0.0]], + [[0.0, 0.0, 0.2]], + [[0.0, 0.0, -0.2]], + ]: + star += Manifold.hull_points( + np.concatenate( + (small_cube.to_mesh().vert_properties[:, :3], offset), axis=0 + ) + ) + + sphere = Manifold.sphere(0.6, 30) + cube = Manifold.cube(1.0, 1.0, 1.0, True) + sphereless_cube = cube - sphere + return Manifold.minkowski(sphereless_cube, star, False) From f1e245fb47d47ce3ee7b7405985b0608cf6fa5e7 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Tue, 19 Dec 2023 23:28:46 -0800 Subject: [PATCH 04/47] Enable Determinism for the Failing CI Tests --- test/manifold_test.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/manifold_test.cpp b/test/manifold_test.cpp index a0d54709c..0edae57d0 100644 --- a/test/manifold_test.cpp +++ b/test/manifold_test.cpp @@ -817,6 +817,7 @@ TEST(Manifold, NonConvexConvexMinkowskiThreaded) { } TEST(Manifold, NonConvexNonConvexMinkowski) { + ManifoldParams().deterministic = true; Manifold star = Manifold::Cube({0.1, 0.1, 0.1}, true); std::vector verts = star.GetMesh().vertPos; for (glm::vec3 point : @@ -836,6 +837,7 @@ TEST(Manifold, NonConvexNonConvexMinkowski) { } TEST(Manifold, NonConvexNonConvexMinkowskiThreaded) { + ManifoldParams().deterministic = true; Manifold star = Manifold::Cube({0.1, 0.1, 0.1}, true); std::vector verts = star.GetMesh().vertPos; for (glm::vec3 point : From d51976a9da11fc83d5f8b95880bb50fffc9669e1 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Tue, 19 Dec 2023 23:31:05 -0800 Subject: [PATCH 05/47] Fix Python Test --- bindings/python/examples/minkowski.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/bindings/python/examples/minkowski.py b/bindings/python/examples/minkowski.py index e41932374..71eb6dba2 100644 --- a/bindings/python/examples/minkowski.py +++ b/bindings/python/examples/minkowski.py @@ -3,7 +3,8 @@ def run(): - small_cube = Manifold.cube(0.1, 0.1, 0.1, True) + small_cube = Manifold.cube([0.1, 0.1, 0.1], True) + cube_vertices = small_cube.to_mesh().vert_properties[:, :3] star = Manifold.as_original(small_cube) for offset in [ [[0.2, 0.0, 0.0]], @@ -13,11 +14,7 @@ def run(): [[0.0, 0.0, 0.2]], [[0.0, 0.0, -0.2]], ]: - star += Manifold.hull_points( - np.concatenate( - (small_cube.to_mesh().vert_properties[:, :3], offset), axis=0 - ) - ) + star += Manifold.hull_points(np.concatenate((cube_vertices, offset), axis=0)) sphere = Manifold.sphere(0.6, 30) cube = Manifold.cube(1.0, 1.0, 1.0, True) From 12990f8bc355efaa9be3cf0eeaa044d10b1aaa33 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Tue, 19 Dec 2023 23:34:29 -0800 Subject: [PATCH 06/47] Fix Python Test Again --- bindings/python/examples/minkowski.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/python/examples/minkowski.py b/bindings/python/examples/minkowski.py index 71eb6dba2..f05efa8b8 100644 --- a/bindings/python/examples/minkowski.py +++ b/bindings/python/examples/minkowski.py @@ -17,6 +17,6 @@ def run(): star += Manifold.hull_points(np.concatenate((cube_vertices, offset), axis=0)) sphere = Manifold.sphere(0.6, 30) - cube = Manifold.cube(1.0, 1.0, 1.0, True) + cube = Manifold.cube([1.0, 1.0, 1.0], True) sphereless_cube = cube - sphere return Manifold.minkowski(sphereless_cube, star, False) From 5f52540406751f738c5abe768acb87d8e50563a6 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Tue, 19 Dec 2023 23:42:59 -0800 Subject: [PATCH 07/47] Make Python Test Cheaper so it doesn't segfault --- bindings/python/examples/minkowski.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/python/examples/minkowski.py b/bindings/python/examples/minkowski.py index f05efa8b8..1ffa48720 100644 --- a/bindings/python/examples/minkowski.py +++ b/bindings/python/examples/minkowski.py @@ -16,7 +16,7 @@ def run(): ]: star += Manifold.hull_points(np.concatenate((cube_vertices, offset), axis=0)) - sphere = Manifold.sphere(0.6, 30) + sphere = Manifold.sphere(0.6, 20) cube = Manifold.cube([1.0, 1.0, 1.0], True) sphereless_cube = cube - sphere return Manifold.minkowski(sphereless_cube, star, False) From 755a55c8add1a8f9ab600cbb3dccebbef972cc1a Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Wed, 20 Dec 2023 12:14:45 -0800 Subject: [PATCH 08/47] Add Insetting Option --- src/manifold/include/manifold.h | 2 +- src/manifold/src/manifold.cpp | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/manifold/include/manifold.h b/src/manifold/include/manifold.h index 84646f47c..dea71cde9 100644 --- a/src/manifold/include/manifold.h +++ b/src/manifold/include/manifold.h @@ -255,7 +255,7 @@ class Manifold { */ ///@{ static Manifold Minkowski(const Manifold& a, const Manifold& b, - bool useThreading = false); + bool inset = false, bool useThreading = false); ///@} /** @name Testing hooks diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index 141229f2e..11ebce975 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -900,7 +900,7 @@ Manifold Manifold::Hull(const std::vector& manifolds) { * @param a The first manifold in the sum. * @param b The second manifold in the sum. */ -Manifold Manifold::Minkowski(const Manifold& a, const Manifold& b, +Manifold Manifold::Minkowski(const Manifold& a, const Manifold& b, bool inset, bool useThreading) { std::vector composedHulls({a}); bool aConvex = a.GetCsgLeafNode().GetImpl()->ReflexFaces().size() == 0; @@ -983,6 +983,8 @@ Manifold Manifold::Minkowski(const Manifold& a, const Manifold& b, } } } - return Manifold::BatchBoolean(composedHulls, manifold::OpType::Add); + return Manifold::BatchBoolean(composedHulls, inset + ? manifold::OpType::Subtract + : manifold::OpType::Add); } } // namespace manifold From 42b8cf6e2e01b092244582d0ed31f9552eb8acb5 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Wed, 20 Dec 2023 12:21:52 -0800 Subject: [PATCH 09/47] Update Python Binding Too --- bindings/python/manifold3d.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/python/manifold3d.cpp b/bindings/python/manifold3d.cpp index b987911f5..c52ba7068 100644 --- a/bindings/python/manifold3d.cpp +++ b/bindings/python/manifold3d.cpp @@ -221,7 +221,7 @@ NB_MODULE(manifold3d, m) { }, "Compute the convex hull enveloping a set of 3d points.") .def_static("minkowski", Manifold::Minkowski, nb::arg("a"), nb::arg("b"), - nb::arg("useThreading"), + nb::arg("inset"), nb::arg("useThreading"), "Compute the minkowski sum of two manifolds.") .def( "transform", From 00d92de2644a078672955ae54fd2ab098b21d3db Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Thu, 21 Dec 2023 13:13:38 -0800 Subject: [PATCH 10/47] Switch to a Member Function --- bindings/python/manifold3d.cpp | 6 +- src/manifold/include/manifold.h | 9 +- src/manifold/src/manifold.cpp | 162 ++++++++++++++++---------------- test/manifold_test.cpp | 13 ++- 4 files changed, 92 insertions(+), 98 deletions(-) diff --git a/bindings/python/manifold3d.cpp b/bindings/python/manifold3d.cpp index c52ba7068..f2dc2a539 100644 --- a/bindings/python/manifold3d.cpp +++ b/bindings/python/manifold3d.cpp @@ -220,9 +220,6 @@ NB_MODULE(manifold3d, m) { return Manifold::Hull(vec); }, "Compute the convex hull enveloping a set of 3d points.") - .def_static("minkowski", Manifold::Minkowski, nb::arg("a"), nb::arg("b"), - nb::arg("inset"), nb::arg("useThreading"), - "Compute the minkowski sum of two manifolds.") .def( "transform", [](Manifold &self, nb::ndarray> &mat) { @@ -550,6 +547,9 @@ NB_MODULE(manifold3d, m) { "vector from the plane.\n" ":param originOffset: The distance of the plane from the origin in " "the direction of the normal vector.") + .def("minkowski", Manifold::Minkowski, nb::arg("other"), nb::arg("inset"), + nb::arg("useThreading"), + "Compute the minkowski sum of two manifolds.") .def( "slice", [](Manifold &self, float height) { return self.Slice(height); }, diff --git a/src/manifold/include/manifold.h b/src/manifold/include/manifold.h index dea71cde9..04930858e 100644 --- a/src/manifold/include/manifold.h +++ b/src/manifold/include/manifold.h @@ -234,6 +234,8 @@ class Manifold { std::pair SplitByPlane(glm::vec3 normal, float originOffset) const; Manifold TrimByPlane(glm::vec3 normal, float originOffset) const; + Manifold Minkowski(const Manifold&, bool inset = false, + bool useThreading = false); ///@} /** @name 2D from 3D @@ -251,13 +253,6 @@ class Manifold { static Manifold Hull(const std::vector& pts); ///@} - /** @name Minkowski Functions - */ - ///@{ - static Manifold Minkowski(const Manifold& a, const Manifold& b, - bool inset = false, bool useThreading = false); - ///@} - /** @name Testing hooks * These are just for internal testing. */ diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index 11ebce975..f922c997c 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -827,101 +827,34 @@ Manifold Manifold::TrimByPlane(glm::vec3 normal, float originOffset) const { return *this ^ Halfspace(BoundingBox(), normal, originOffset); } -/** - * Returns the cross section of this object parallel to the X-Y plane at the - * specified Z height, defaulting to zero. Using a height equal to the bottom of - * the bounding box will return the bottom faces, while using a height equal to - * the top of the bounding box will return empty. - */ -CrossSection Manifold::Slice(float height) const { - return GetCsgLeafNode().GetImpl()->Slice(height); -} - -/** - * Returns a cross section representing the projected outline of this object - * onto the X-Y plane. - */ -CrossSection Manifold::Project() const { - return GetCsgLeafNode().GetImpl()->Project(); -} - -ExecutionParams& ManifoldParams() { return manifoldParams; } - -/** - * Compute the convex hull of a set of points. If the given points are fewer - * than 4, or they are all coplanar, an empty Manifold will be returned. - * - * @param pts A vector of 3-dimensional points over which to compute a convex - * hull. - */ -Manifold Manifold::Hull(const std::vector& pts) { - ZoneScoped; - const int numVert = pts.size(); - if (numVert < 4) return Manifold(); - - std::vector> vertices(numVert); - for (int i = 0; i < numVert; i++) { - vertices[i] = {pts[i].x, pts[i].y, pts[i].z}; - } - - quickhull::QuickHull qh; - // bools: correct triangle winding, and use original indices - auto hull = qh.getConvexHull(vertices, false, true); - const auto& triangles = hull.getIndexBuffer(); - const int numTris = triangles.size() / 3; - - Mesh mesh; - mesh.vertPos = pts; - mesh.triVerts.reserve(numTris); - for (int i = 0; i < numTris; i++) { - const int j = i * 3; - mesh.triVerts.push_back({triangles[j], triangles[j + 1], triangles[j + 2]}); - } - return Manifold(mesh); -} - -/** - * Compute the convex hull of this manifold. - */ -Manifold Manifold::Hull() const { return Hull(GetMesh().vertPos); } - -/** - * Compute the convex hull enveloping a set of manifolds. - * - * @param manifolds A vector of manifolds over which to compute a convex hull. - */ -Manifold Manifold::Hull(const std::vector& manifolds) { - return Compose(manifolds).Hull(); -} - /** * Compute the minkowski sum of two manifolds. * * @param a The first manifold in the sum. * @param b The second manifold in the sum. */ -Manifold Manifold::Minkowski(const Manifold& a, const Manifold& b, bool inset, +Manifold Manifold::Minkowski(const Manifold& other, bool inset, bool useThreading) { - std::vector composedHulls({a}); - bool aConvex = a.GetCsgLeafNode().GetImpl()->ReflexFaces().size() == 0; - bool bConvex = b.GetCsgLeafNode().GetImpl()->ReflexFaces().size() == 0; + std::vector composedHulls({*this}); + bool aConvex = this->GetCsgLeafNode().GetImpl()->ReflexFaces().size() == 0; + bool bConvex = other.GetCsgLeafNode().GetImpl()->ReflexFaces().size() == 0; // If the convex manifold was supplied first, swap them! - Manifold aM = a, bM = b; + Manifold a = *this, b = other; if (aConvex && !bConvex) { - aM = b; - bM = a; + a = other; + b = *this; aConvex = !aConvex; bConvex = !bConvex; } - manifold::Mesh aMesh = aM.GetMesh(); + manifold::Mesh aMesh = a.GetMesh(); // Convex-Convex Minkowski: Very Fast if (aConvex && bConvex) { std::vector simpleHull; for (glm::vec3 vertex : aMesh.vertPos) { - simpleHull.push_back(bM.Translate(vertex)); + simpleHull.push_back(b.Translate(vertex)); } composedHulls.push_back(Manifold::Hull(simpleHull)); // Convex - Non-Convex Minkowski: Slower @@ -931,13 +864,13 @@ Manifold Manifold::Minkowski(const Manifold& a, const Manifold& b, bool inset, thrust::for_each_n( thrust::host, zip(countAt(1), aMesh.triVerts.begin()), aMesh.triVerts.size(), - ComputeTriangleHull({&bM, &aMesh.vertPos, &composedHulls})); + ComputeTriangleHull({&b, &aMesh.vertPos, &composedHulls})); } else { std::vector> composedParts; for (glm::ivec3 vertexIndices : aMesh.triVerts) { - composedParts.push_back({bM.Translate(aMesh.vertPos[vertexIndices.x]), - bM.Translate(aMesh.vertPos[vertexIndices.y]), - bM.Translate(aMesh.vertPos[vertexIndices.z])}); + composedParts.push_back({b.Translate(aMesh.vertPos[vertexIndices.x]), + b.Translate(aMesh.vertPos[vertexIndices.y]), + b.Translate(aMesh.vertPos[vertexIndices.z])}); } std::vector newHulls; newHulls.reserve(composedParts.size()); @@ -953,7 +886,7 @@ Manifold Manifold::Minkowski(const Manifold& a, const Manifold& b, bool inset, } // Non-Convex - Non-Convex Minkowski: Very Slow } else if (!aConvex && !bConvex) { - manifold::Mesh bMesh = bM.GetMesh(); + manifold::Mesh bMesh = b.GetMesh(); if (useThreading) { std::vector> trianglePairs; for (glm::ivec3 aVertexIndices : aMesh.triVerts) { @@ -987,4 +920,71 @@ Manifold Manifold::Minkowski(const Manifold& a, const Manifold& b, bool inset, ? manifold::OpType::Subtract : manifold::OpType::Add); } + +/** + * Returns the cross section of this object parallel to the X-Y plane at the + * specified Z height, defaulting to zero. Using a height equal to the bottom of + * the bounding box will return the bottom faces, while using a height equal to + * the top of the bounding box will return empty. + */ +CrossSection Manifold::Slice(float height) const { + return GetCsgLeafNode().GetImpl()->Slice(height); +} + +/** + * Returns a cross section representing the projected outline of this object + * onto the X-Y plane. + */ +CrossSection Manifold::Project() const { + return GetCsgLeafNode().GetImpl()->Project(); +} + +ExecutionParams& ManifoldParams() { return manifoldParams; } + +/** + * Compute the convex hull of a set of points. If the given points are fewer + * than 4, or they are all coplanar, an empty Manifold will be returned. + * + * @param pts A vector of 3-dimensional points over which to compute a convex + * hull. + */ +Manifold Manifold::Hull(const std::vector& pts) { + ZoneScoped; + const int numVert = pts.size(); + if (numVert < 4) return Manifold(); + + std::vector> vertices(numVert); + for (int i = 0; i < numVert; i++) { + vertices[i] = {pts[i].x, pts[i].y, pts[i].z}; + } + + quickhull::QuickHull qh; + // bools: correct triangle winding, and use original indices + auto hull = qh.getConvexHull(vertices, false, true); + const auto& triangles = hull.getIndexBuffer(); + const int numTris = triangles.size() / 3; + + Mesh mesh; + mesh.vertPos = pts; + mesh.triVerts.reserve(numTris); + for (int i = 0; i < numTris; i++) { + const int j = i * 3; + mesh.triVerts.push_back({triangles[j], triangles[j + 1], triangles[j + 2]}); + } + return Manifold(mesh); +} + +/** + * Compute the convex hull of this manifold. + */ +Manifold Manifold::Hull() const { return Hull(GetMesh().vertPos); } + +/** + * Compute the convex hull enveloping a set of manifolds. + * + * @param manifolds A vector of manifolds over which to compute a convex hull. + */ +Manifold Manifold::Hull(const std::vector& manifolds) { + return Compose(manifolds).Hull(); +} } // namespace manifold diff --git a/test/manifold_test.cpp b/test/manifold_test.cpp index 0edae57d0..072617521 100644 --- a/test/manifold_test.cpp +++ b/test/manifold_test.cpp @@ -788,14 +788,14 @@ TEST(Manifold, EmptyHull) { TEST(Manifold, ConvexConvexMinkowski) { Manifold sphere = Manifold::Sphere(0.1, 20); Manifold cube = Manifold::Cube(); - Manifold sum = Manifold::Minkowski(cube, sphere); + Manifold sum = cube.Minkowski(sphere); EXPECT_FLOAT_EQ(sum.GetProperties().volume, 1.6966598f); } TEST(Manifold, ConvexConvexMinkowskiThreaded) { Manifold sphere = Manifold::Sphere(0.1, 20); Manifold cube = Manifold::Cube(); - Manifold sum = Manifold::Minkowski(cube, sphere, true); + Manifold sum = cube.Minkowski(sphere, true); EXPECT_FLOAT_EQ(sum.GetProperties().volume, 1.6966598f); } @@ -803,7 +803,7 @@ TEST(Manifold, NonConvexConvexMinkowski) { Manifold sphere = Manifold::Sphere(0.6, 20); Manifold cube = Manifold::Cube({1.0, 1.0, 1.0}, true); Manifold nonConvex = cube - sphere; - Manifold sum = Manifold::Minkowski(nonConvex, Manifold::Sphere(0.1, 20)); + Manifold sum = nonConvex.Minkowski(Manifold::Sphere(0.1, 20)); EXPECT_FLOAT_EQ(sum.GetProperties().volume, 1.0686193f); } @@ -811,8 +811,7 @@ TEST(Manifold, NonConvexConvexMinkowskiThreaded) { Manifold sphere = Manifold::Sphere(0.6, 20); Manifold cube = Manifold::Cube({1.0, 1.0, 1.0}, true); Manifold nonConvex = cube - sphere; - Manifold sum = - Manifold::Minkowski(nonConvex, Manifold::Sphere(0.1, 20), true); + Manifold sum = nonConvex.Minkowski(Manifold::Sphere(0.1, 20), true); EXPECT_FLOAT_EQ(sum.GetProperties().volume, 1.0686193f); } @@ -832,7 +831,7 @@ TEST(Manifold, NonConvexNonConvexMinkowski) { Manifold sphere = Manifold::Sphere(0.6, 20); Manifold cube = Manifold::Cube({1.0, 1.0, 1.0}, true); Manifold nonConvex = cube - sphere; - Manifold sum = Manifold::Minkowski(nonConvex, star); + Manifold sum = nonConvex.Minkowski(star); EXPECT_FLOAT_EQ(sum.GetProperties().volume, 1.643248); } @@ -852,6 +851,6 @@ TEST(Manifold, NonConvexNonConvexMinkowskiThreaded) { Manifold sphere = Manifold::Sphere(0.6, 20); Manifold cube = Manifold::Cube({1.0, 1.0, 1.0}, true); Manifold nonConvex = cube - sphere; - Manifold sum = Manifold::Minkowski(nonConvex, star, true); + Manifold sum = nonConvex.Minkowski(star, true); EXPECT_FLOAT_EQ(sum.GetProperties().volume, 1.643248); } From 93b6f30b6423f39d1a86077236842c8d5f526ad8 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Thu, 21 Dec 2023 13:17:05 -0800 Subject: [PATCH 11/47] Remove the useThreading option --- bindings/python/manifold3d.cpp | 1 - src/manifold/include/manifold.h | 3 +- src/manifold/src/manifold.cpp | 114 ++++++++------------------------ test/manifold_test.cpp | 35 ---------- 4 files changed, 29 insertions(+), 124 deletions(-) diff --git a/bindings/python/manifold3d.cpp b/bindings/python/manifold3d.cpp index f2dc2a539..6d02d0ec9 100644 --- a/bindings/python/manifold3d.cpp +++ b/bindings/python/manifold3d.cpp @@ -548,7 +548,6 @@ NB_MODULE(manifold3d, m) { ":param originOffset: The distance of the plane from the origin in " "the direction of the normal vector.") .def("minkowski", Manifold::Minkowski, nb::arg("other"), nb::arg("inset"), - nb::arg("useThreading"), "Compute the minkowski sum of two manifolds.") .def( "slice", diff --git a/src/manifold/include/manifold.h b/src/manifold/include/manifold.h index 04930858e..9f1a12714 100644 --- a/src/manifold/include/manifold.h +++ b/src/manifold/include/manifold.h @@ -234,8 +234,7 @@ class Manifold { std::pair SplitByPlane(glm::vec3 normal, float originOffset) const; Manifold TrimByPlane(glm::vec3 normal, float originOffset) const; - Manifold Minkowski(const Manifold&, bool inset = false, - bool useThreading = false); + Manifold Minkowski(const Manifold&, bool inset = false); ///@} /** @name 2D from 3D diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index f922c997c..d2dc86547 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -61,40 +61,6 @@ struct UpdateProperties { } }; -struct ComputeTriangleHull { - const Manifold* manifold; - std::vector* vertPos; - std::vector* output; - void operator()(thrust::tuple inOut) { - const int idx = thrust::get<0>(inOut); - glm::ivec3& tri = thrust::get<1>(inOut); - (*output)[idx] = Manifold::Hull({(*manifold).Translate((*vertPos)[tri.x]), - (*manifold).Translate((*vertPos)[tri.y]), - (*manifold).Translate((*vertPos)[tri.z])}); - } -}; - -struct ComputeTriangleTriangleHull { - std::vector* vertPosAPtr; - std::vector* vertPosBPtr; - std::vector* output; - void operator()(thrust::tuple> inOut) { - const int idx = thrust::get<0>(inOut); - std::pair tris = thrust::get<1>(inOut); - std::vector vertPosA = *vertPosAPtr, vertPosB = *vertPosBPtr; - (*output)[idx] = - Manifold::Hull({vertPosA[tris.first.x] + vertPosB[tris.second.x], - vertPosA[tris.first.x] + vertPosB[tris.second.y], - vertPosA[tris.first.x] + vertPosB[tris.second.z], - vertPosA[tris.first.y] + vertPosB[tris.second.x], - vertPosA[tris.first.y] + vertPosB[tris.second.y], - vertPosA[tris.first.y] + vertPosB[tris.second.z], - vertPosA[tris.first.z] + vertPosB[tris.second.x], - vertPosA[tris.first.z] + vertPosB[tris.second.y], - vertPosA[tris.first.z] + vertPosB[tris.second.z]}); - } -}; - Manifold Halfspace(Box bBox, glm::vec3 normal, float originOffset) { normal = glm::normalize(normal); Manifold cutter = @@ -833,8 +799,7 @@ Manifold Manifold::TrimByPlane(glm::vec3 normal, float originOffset) const { * @param a The first manifold in the sum. * @param b The second manifold in the sum. */ -Manifold Manifold::Minkowski(const Manifold& other, bool inset, - bool useThreading) { +Manifold Manifold::Minkowski(const Manifold& other, bool inset) { std::vector composedHulls({*this}); bool aConvex = this->GetCsgLeafNode().GetImpl()->ReflexFaces().size() == 0; bool bConvex = other.GetCsgLeafNode().GetImpl()->ReflexFaces().size() == 0; @@ -859,60 +824,37 @@ Manifold Manifold::Minkowski(const Manifold& other, bool inset, composedHulls.push_back(Manifold::Hull(simpleHull)); // Convex - Non-Convex Minkowski: Slower } else if (!aConvex && bConvex) { - if (useThreading) { - composedHulls.resize(aMesh.triVerts.size() + 1); - thrust::for_each_n( - thrust::host, zip(countAt(1), aMesh.triVerts.begin()), - aMesh.triVerts.size(), - ComputeTriangleHull({&b, &aMesh.vertPos, &composedHulls})); - } else { - std::vector> composedParts; - for (glm::ivec3 vertexIndices : aMesh.triVerts) { - composedParts.push_back({b.Translate(aMesh.vertPos[vertexIndices.x]), - b.Translate(aMesh.vertPos[vertexIndices.y]), - b.Translate(aMesh.vertPos[vertexIndices.z])}); - } - std::vector newHulls; - newHulls.reserve(composedParts.size()); - newHulls.resize(composedParts.size()); - thrust::for_each_n( - thrust::host, zip(composedParts.begin(), newHulls.begin()), - composedParts.size(), - [](thrust::tuple, Manifold&> inOut) { - thrust::get<1>(inOut) = Manifold::Hull(thrust::get<0>(inOut)); - }); - composedHulls.insert(composedHulls.end(), newHulls.begin(), - newHulls.end()); + std::vector> composedParts; + for (glm::ivec3 vertexIndices : aMesh.triVerts) { + composedParts.push_back({b.Translate(aMesh.vertPos[vertexIndices.x]), + b.Translate(aMesh.vertPos[vertexIndices.y]), + b.Translate(aMesh.vertPos[vertexIndices.z])}); } + std::vector newHulls; + newHulls.reserve(composedParts.size()); + newHulls.resize(composedParts.size()); + thrust::for_each_n( + thrust::host, zip(composedParts.begin(), newHulls.begin()), + composedParts.size(), + [](thrust::tuple, Manifold&> inOut) { + thrust::get<1>(inOut) = Manifold::Hull(thrust::get<0>(inOut)); + }); + composedHulls.insert(composedHulls.end(), newHulls.begin(), newHulls.end()); // Non-Convex - Non-Convex Minkowski: Very Slow } else if (!aConvex && !bConvex) { manifold::Mesh bMesh = b.GetMesh(); - if (useThreading) { - std::vector> trianglePairs; - for (glm::ivec3 aVertexIndices : aMesh.triVerts) { - for (glm::ivec3 bVertexIndices : bMesh.triVerts) { - trianglePairs.push_back({aVertexIndices, bVertexIndices}); - } - } - composedHulls.resize(trianglePairs.size() + 1); - thrust::for_each_n(thrust::host, zip(countAt(1), trianglePairs.begin()), - trianglePairs.size(), - ComputeTriangleTriangleHull( - {&aMesh.vertPos, &bMesh.vertPos, &composedHulls})); - } else { - for (glm::ivec3 aIndices : aMesh.triVerts) { - for (glm::ivec3 bIndices : bMesh.triVerts) { - composedHulls.push_back(Manifold::Hull( - {aMesh.vertPos[aIndices.x] + bMesh.vertPos[bIndices.x], - aMesh.vertPos[aIndices.x] + bMesh.vertPos[bIndices.y], - aMesh.vertPos[aIndices.x] + bMesh.vertPos[bIndices.z], - aMesh.vertPos[aIndices.y] + bMesh.vertPos[bIndices.x], - aMesh.vertPos[aIndices.y] + bMesh.vertPos[bIndices.y], - aMesh.vertPos[aIndices.y] + bMesh.vertPos[bIndices.z], - aMesh.vertPos[aIndices.z] + bMesh.vertPos[bIndices.x], - aMesh.vertPos[aIndices.z] + bMesh.vertPos[bIndices.y], - aMesh.vertPos[aIndices.z] + bMesh.vertPos[bIndices.z]})); - } + for (glm::ivec3 aIndices : aMesh.triVerts) { + for (glm::ivec3 bIndices : bMesh.triVerts) { + composedHulls.push_back(Manifold::Hull( + {aMesh.vertPos[aIndices.x] + bMesh.vertPos[bIndices.x], + aMesh.vertPos[aIndices.x] + bMesh.vertPos[bIndices.y], + aMesh.vertPos[aIndices.x] + bMesh.vertPos[bIndices.z], + aMesh.vertPos[aIndices.y] + bMesh.vertPos[bIndices.x], + aMesh.vertPos[aIndices.y] + bMesh.vertPos[bIndices.y], + aMesh.vertPos[aIndices.y] + bMesh.vertPos[bIndices.z], + aMesh.vertPos[aIndices.z] + bMesh.vertPos[bIndices.x], + aMesh.vertPos[aIndices.z] + bMesh.vertPos[bIndices.y], + aMesh.vertPos[aIndices.z] + bMesh.vertPos[bIndices.z]})); } } } diff --git a/test/manifold_test.cpp b/test/manifold_test.cpp index 072617521..ec0a347bc 100644 --- a/test/manifold_test.cpp +++ b/test/manifold_test.cpp @@ -792,13 +792,6 @@ TEST(Manifold, ConvexConvexMinkowski) { EXPECT_FLOAT_EQ(sum.GetProperties().volume, 1.6966598f); } -TEST(Manifold, ConvexConvexMinkowskiThreaded) { - Manifold sphere = Manifold::Sphere(0.1, 20); - Manifold cube = Manifold::Cube(); - Manifold sum = cube.Minkowski(sphere, true); - EXPECT_FLOAT_EQ(sum.GetProperties().volume, 1.6966598f); -} - TEST(Manifold, NonConvexConvexMinkowski) { Manifold sphere = Manifold::Sphere(0.6, 20); Manifold cube = Manifold::Cube({1.0, 1.0, 1.0}, true); @@ -807,14 +800,6 @@ TEST(Manifold, NonConvexConvexMinkowski) { EXPECT_FLOAT_EQ(sum.GetProperties().volume, 1.0686193f); } -TEST(Manifold, NonConvexConvexMinkowskiThreaded) { - Manifold sphere = Manifold::Sphere(0.6, 20); - Manifold cube = Manifold::Cube({1.0, 1.0, 1.0}, true); - Manifold nonConvex = cube - sphere; - Manifold sum = nonConvex.Minkowski(Manifold::Sphere(0.1, 20), true); - EXPECT_FLOAT_EQ(sum.GetProperties().volume, 1.0686193f); -} - TEST(Manifold, NonConvexNonConvexMinkowski) { ManifoldParams().deterministic = true; Manifold star = Manifold::Cube({0.1, 0.1, 0.1}, true); @@ -834,23 +819,3 @@ TEST(Manifold, NonConvexNonConvexMinkowski) { Manifold sum = nonConvex.Minkowski(star); EXPECT_FLOAT_EQ(sum.GetProperties().volume, 1.643248); } - -TEST(Manifold, NonConvexNonConvexMinkowskiThreaded) { - ManifoldParams().deterministic = true; - Manifold star = Manifold::Cube({0.1, 0.1, 0.1}, true); - std::vector verts = star.GetMesh().vertPos; - for (glm::vec3 point : - {glm::vec3(0.2, 0.0, 0.0), glm::vec3(-0.2, 0.0, 0.0), - glm::vec3(0.0, 0.2, 0.0), glm::vec3(0.0, -0.2, 0.0), - glm::vec3(0.0, 0.0, 0.2), glm::vec3(0.0, 0.0, -0.2)}) { - verts.push_back(point); - star += Manifold::Hull(verts); - verts.pop_back(); - } - - Manifold sphere = Manifold::Sphere(0.6, 20); - Manifold cube = Manifold::Cube({1.0, 1.0, 1.0}, true); - Manifold nonConvex = cube - sphere; - Manifold sum = nonConvex.Minkowski(star, true); - EXPECT_FLOAT_EQ(sum.GetProperties().volume, 1.643248); -} From 953eaf3c11d2b668cc2109fad6f2aec6f14c8754 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Thu, 21 Dec 2023 13:20:34 -0800 Subject: [PATCH 12/47] Fix the Python Binding --- bindings/python/manifold3d.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/python/manifold3d.cpp b/bindings/python/manifold3d.cpp index b0f3dbbca..80b7ad4fa 100644 --- a/bindings/python/manifold3d.cpp +++ b/bindings/python/manifold3d.cpp @@ -518,7 +518,7 @@ NB_MODULE(manifold3d, m) { "vector from the plane.\n" ":param origin_offset: The distance of the plane from the origin in " "the direction of the normal vector.") - .def("minkowski", Manifold::Minkowski, nb::arg("other"), nb::arg("inset"), + .def("minkowski", &Manifold::Minkowski, nb::arg("other"), nb::arg("inset"), "Compute the minkowski sum of two manifolds.") .def("slice", &Manifold::Slice, nb::arg("height"), "Returns the cross section of this object parallel to the X-Y plane " From 2d93b1062bc6662a3723506d0cdaa2e48e25a7a5 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Thu, 21 Dec 2023 13:24:19 -0800 Subject: [PATCH 13/47] And the example --- bindings/python/examples/minkowski.py | 2 +- bindings/python/manifold3d.cpp | 7 +++++-- src/manifold/src/manifold.cpp | 4 ++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/bindings/python/examples/minkowski.py b/bindings/python/examples/minkowski.py index 1ffa48720..6a470deab 100644 --- a/bindings/python/examples/minkowski.py +++ b/bindings/python/examples/minkowski.py @@ -19,4 +19,4 @@ def run(): sphere = Manifold.sphere(0.6, 20) cube = Manifold.cube([1.0, 1.0, 1.0], True) sphereless_cube = cube - sphere - return Manifold.minkowski(sphereless_cube, star, False) + return sphereless_cube.minkowski(star, False) diff --git a/bindings/python/manifold3d.cpp b/bindings/python/manifold3d.cpp index 80b7ad4fa..80b0f5f3d 100644 --- a/bindings/python/manifold3d.cpp +++ b/bindings/python/manifold3d.cpp @@ -518,8 +518,11 @@ NB_MODULE(manifold3d, m) { "vector from the plane.\n" ":param origin_offset: The distance of the plane from the origin in " "the direction of the normal vector.") - .def("minkowski", &Manifold::Minkowski, nb::arg("other"), nb::arg("inset"), - "Compute the minkowski sum of two manifolds.") + .def("minkowski", &Manifold::Minkowski, nb::arg("other"),nb::arg("inset"), + "Compute the minkowski sum of two manifolds." + "\n\n" + ":param other: The other manifold to minkowski sum to this one." + ":param inset: Whether it should add or subtract from the manifold.") .def("slice", &Manifold::Slice, nb::arg("height"), "Returns the cross section of this object parallel to the X-Y plane " "at the specified height. Using a height equal to the bottom of the " diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index d2dc86547..4ff7c58bf 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -796,8 +796,8 @@ Manifold Manifold::TrimByPlane(glm::vec3 normal, float originOffset) const { /** * Compute the minkowski sum of two manifolds. * - * @param a The first manifold in the sum. - * @param b The second manifold in the sum. + * @param other The other manifold to minkowski sum to this one. + * @param inset Whether it should add or subtract from the manifold. */ Manifold Manifold::Minkowski(const Manifold& other, bool inset) { std::vector composedHulls({*this}); From b668659d13e95aac21824f948e4ec2bad7dc3d77 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Thu, 21 Dec 2023 13:25:48 -0800 Subject: [PATCH 14/47] Fix Formatting --- bindings/python/manifold3d.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bindings/python/manifold3d.cpp b/bindings/python/manifold3d.cpp index 80b0f5f3d..3fb277316 100644 --- a/bindings/python/manifold3d.cpp +++ b/bindings/python/manifold3d.cpp @@ -518,7 +518,8 @@ NB_MODULE(manifold3d, m) { "vector from the plane.\n" ":param origin_offset: The distance of the plane from the origin in " "the direction of the normal vector.") - .def("minkowski", &Manifold::Minkowski, nb::arg("other"),nb::arg("inset"), + .def("minkowski", &Manifold::Minkowski, nb::arg("other"), + nb::arg("inset"), "Compute the minkowski sum of two manifolds." "\n\n" ":param other: The other manifold to minkowski sum to this one." From 847ae97ae678c5e311cf2ec5266987d1e48f99b9 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Thu, 21 Dec 2023 14:07:07 -0800 Subject: [PATCH 15/47] Split into Add/Subtract Functions, Add C Binding --- bindings/c/include/manifoldc.h | 4 ++++ bindings/c/manifoldc.cpp | 12 ++++++++++++ bindings/python/manifold3d.cpp | 14 +++++++++----- src/manifold/include/manifold.h | 5 ++++- src/manifold/src/manifold.cpp | 18 ++++++++++++++++++ 5 files changed, 47 insertions(+), 6 deletions(-) diff --git a/bindings/c/include/manifoldc.h b/bindings/c/include/manifoldc.h index 8203405ab..49413f263 100644 --- a/bindings/c/include/manifoldc.h +++ b/bindings/c/include/manifoldc.h @@ -110,6 +110,10 @@ ManifoldManifoldPair manifold_split_by_plane(void *mem_first, void *mem_second, ManifoldManifold *manifold_trim_by_plane(void *mem, ManifoldManifold *m, float normal_x, float normal_y, float normal_z, float offset); +ManifoldManifold *manifold_minkowski_add(void *mem, ManifoldManifold *a, + ManifoldManifold *b); +ManifoldManifold *manifold_minkowski_subtract(void *mem, ManifoldManifold *a, + ManifoldManifold *b); // 3D to 2D diff --git a/bindings/c/manifoldc.cpp b/bindings/c/manifoldc.cpp index 5e1ea9652..fbbafff4c 100644 --- a/bindings/c/manifoldc.cpp +++ b/bindings/c/manifoldc.cpp @@ -202,6 +202,18 @@ ManifoldManifold *manifold_trim_by_plane(void *mem, ManifoldManifold *m, return to_c(new (mem) Manifold(trimmed)); } +ManifoldManifold *manifold_minkowski_add(void *mem, ManifoldManifold *a, + ManifoldManifold *b) { + auto m = (*from_c(a)).MinkowskiAdd(*from_c(b)); + return to_c(new (mem) Manifold(m)); +} + +ManifoldManifold *manifold_minkowski_subtract(void *mem, ManifoldManifold *a, + ManifoldManifold *b) { + auto m = (*from_c(a)).MinkowskiSubtract(*from_c(b)); + return to_c(new (mem) Manifold(m)); +} + ManifoldCrossSection *manifold_slice(void *mem, ManifoldManifold *m, float height) { auto poly = from_c(m)->Slice(height); diff --git a/bindings/python/manifold3d.cpp b/bindings/python/manifold3d.cpp index 3fb277316..731c02792 100644 --- a/bindings/python/manifold3d.cpp +++ b/bindings/python/manifold3d.cpp @@ -518,12 +518,16 @@ NB_MODULE(manifold3d, m) { "vector from the plane.\n" ":param origin_offset: The distance of the plane from the origin in " "the direction of the normal vector.") - .def("minkowski", &Manifold::Minkowski, nb::arg("other"), - nb::arg("inset"), - "Compute the minkowski sum of two manifolds." + .def("minkowski_add", &Manifold::MinkowskiAdd, nb::arg("other"), + "Compute the minkowski sum of this manifold with another." "\n\n" - ":param other: The other manifold to minkowski sum to this one." - ":param inset: Whether it should add or subtract from the manifold.") + ":param other: The other manifold to minkowski sum to this one.") + .def("minkowski_subtract", &Manifold::MinkowskiSubtract, nb::arg("other"), + "Subtract the sweep of the other manifold across this manifold's " + "surface." + "\n\n" + ":param other: The other manifold to minkowski subtract from this " + "one.") .def("slice", &Manifold::Slice, nb::arg("height"), "Returns the cross section of this object parallel to the X-Y plane " "at the specified height. Using a height equal to the bottom of the " diff --git a/src/manifold/include/manifold.h b/src/manifold/include/manifold.h index 9f1a12714..16460a934 100644 --- a/src/manifold/include/manifold.h +++ b/src/manifold/include/manifold.h @@ -234,7 +234,8 @@ class Manifold { std::pair SplitByPlane(glm::vec3 normal, float originOffset) const; Manifold TrimByPlane(glm::vec3 normal, float originOffset) const; - Manifold Minkowski(const Manifold&, bool inset = false); + Manifold MinkowskiAdd(const Manifold&); + Manifold MinkowskiSubtract(const Manifold&); ///@} /** @name 2D from 3D @@ -270,6 +271,8 @@ class Manifold { mutable std::shared_ptr pNode_; CsgLeafNode& GetCsgLeafNode() const; + + Manifold Minkowski(const Manifold&, bool inset = false); }; /** @} */ } // namespace manifold diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index 4ff7c58bf..707ef4580 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -863,6 +863,24 @@ Manifold Manifold::Minkowski(const Manifold& other, bool inset) { : manifold::OpType::Add); } +/** + * Compute the minkowski sum of this manifold with another. + * + * @param other The other manifold to minkowski sum to this one. + */ +Manifold Manifold::MinkowskiAdd(const Manifold& other) { + return this->Minkowski(other, false); +} + +/** + * Subtract the sweep of the other manifold across this manifold's surface. + * + * @param other The other manifold to minkowski subtract from this one. + */ +Manifold Manifold::MinkowskiSubtract(const Manifold& other) { + return this->Minkowski(other, true); +} + /** * Returns the cross section of this object parallel to the X-Y plane at the * specified Z height, defaulting to zero. Using a height equal to the bottom of From 15871649188e4a2e56f5e62b0380a29cb9c04dca Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Thu, 21 Dec 2023 14:09:58 -0800 Subject: [PATCH 16/47] Make Methods const --- src/manifold/include/manifold.h | 6 +++--- src/manifold/src/manifold.cpp | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/manifold/include/manifold.h b/src/manifold/include/manifold.h index 16460a934..db83ee9f1 100644 --- a/src/manifold/include/manifold.h +++ b/src/manifold/include/manifold.h @@ -234,8 +234,8 @@ class Manifold { std::pair SplitByPlane(glm::vec3 normal, float originOffset) const; Manifold TrimByPlane(glm::vec3 normal, float originOffset) const; - Manifold MinkowskiAdd(const Manifold&); - Manifold MinkowskiSubtract(const Manifold&); + Manifold MinkowskiAdd(const Manifold&) const; + Manifold MinkowskiSubtract(const Manifold&) const; ///@} /** @name 2D from 3D @@ -272,7 +272,7 @@ class Manifold { CsgLeafNode& GetCsgLeafNode() const; - Manifold Minkowski(const Manifold&, bool inset = false); + Manifold Minkowski(const Manifold&, bool inset = false) const; }; /** @} */ } // namespace manifold diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index 707ef4580..c9ab4e470 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -799,7 +799,7 @@ Manifold Manifold::TrimByPlane(glm::vec3 normal, float originOffset) const { * @param other The other manifold to minkowski sum to this one. * @param inset Whether it should add or subtract from the manifold. */ -Manifold Manifold::Minkowski(const Manifold& other, bool inset) { +Manifold Manifold::Minkowski(const Manifold& other, bool inset) const { std::vector composedHulls({*this}); bool aConvex = this->GetCsgLeafNode().GetImpl()->ReflexFaces().size() == 0; bool bConvex = other.GetCsgLeafNode().GetImpl()->ReflexFaces().size() == 0; @@ -868,7 +868,7 @@ Manifold Manifold::Minkowski(const Manifold& other, bool inset) { * * @param other The other manifold to minkowski sum to this one. */ -Manifold Manifold::MinkowskiAdd(const Manifold& other) { +Manifold Manifold::MinkowskiAdd(const Manifold& other) const { return this->Minkowski(other, false); } @@ -877,7 +877,7 @@ Manifold Manifold::MinkowskiAdd(const Manifold& other) { * * @param other The other manifold to minkowski subtract from this one. */ -Manifold Manifold::MinkowskiSubtract(const Manifold& other) { +Manifold Manifold::MinkowskiSubtract(const Manifold& other) const { return this->Minkowski(other, true); } From ebe2baa3596e0f1a9c46e9c45bf82d781e55fdae Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Thu, 21 Dec 2023 14:34:38 -0800 Subject: [PATCH 17/47] Update the tests; ensure that the inset operation works with convex shapes --- src/manifold/src/manifold.cpp | 4 ++-- test/manifold_test.cpp | 37 ++++++++++++++++++++++++----------- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index c9ab4e470..6e14a98e2 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -816,14 +816,14 @@ Manifold Manifold::Minkowski(const Manifold& other, bool inset) const { manifold::Mesh aMesh = a.GetMesh(); // Convex-Convex Minkowski: Very Fast - if (aConvex && bConvex) { + if (!inset && aConvex && bConvex) { std::vector simpleHull; for (glm::vec3 vertex : aMesh.vertPos) { simpleHull.push_back(b.Translate(vertex)); } composedHulls.push_back(Manifold::Hull(simpleHull)); // Convex - Non-Convex Minkowski: Slower - } else if (!aConvex && bConvex) { + } else if ((inset || !aConvex) && bConvex) { std::vector> composedParts; for (glm::ivec3 vertexIndices : aMesh.triVerts) { composedParts.push_back({b.Translate(aMesh.vertPos[vertexIndices.x]), diff --git a/test/manifold_test.cpp b/test/manifold_test.cpp index ec0a347bc..cab244f22 100644 --- a/test/manifold_test.cpp +++ b/test/manifold_test.cpp @@ -786,21 +786,33 @@ TEST(Manifold, EmptyHull) { } TEST(Manifold, ConvexConvexMinkowski) { + bool oldDeterministic = ManifoldParams().deterministic; + ManifoldParams().deterministic = true; Manifold sphere = Manifold::Sphere(0.1, 20); - Manifold cube = Manifold::Cube(); - Manifold sum = cube.Minkowski(sphere); - EXPECT_FLOAT_EQ(sum.GetProperties().volume, 1.6966598f); + Manifold cube = Manifold::Cube({2.0, 2.0, 2.0}); + Manifold sum = cube.MinkowskiAdd(sphere); + EXPECT_FLOAT_EQ(sum.GetProperties().volume, 10.589364f); + Manifold difference = + Manifold::Cube({2.0, 2.0, 2.0}).MinkowskiSubtract(sphere); + EXPECT_FLOAT_EQ(difference.GetProperties().volume, 5.8319983f); + ManifoldParams().deterministic = oldDeterministic; } TEST(Manifold, NonConvexConvexMinkowski) { - Manifold sphere = Manifold::Sphere(0.6, 20); - Manifold cube = Manifold::Cube({1.0, 1.0, 1.0}, true); + bool oldDeterministic = ManifoldParams().deterministic; + ManifoldParams().deterministic = true; + Manifold sphere = Manifold::Sphere(1.2, 20); + Manifold cube = Manifold::Cube({2.0, 2.0, 2.0}, true); Manifold nonConvex = cube - sphere; - Manifold sum = nonConvex.Minkowski(Manifold::Sphere(0.1, 20)); - EXPECT_FLOAT_EQ(sum.GetProperties().volume, 1.0686193f); + Manifold sum = nonConvex.MinkowskiAdd(Manifold::Sphere(0.1, 20)); + EXPECT_FLOAT_EQ(sum.GetProperties().volume, 4.8406339f); + Manifold difference = nonConvex.MinkowskiSubtract(Manifold::Sphere(0.1, 20)); + EXPECT_FLOAT_EQ(difference.GetProperties().volume, 0.2029168f); + ManifoldParams().deterministic = oldDeterministic; } TEST(Manifold, NonConvexNonConvexMinkowski) { + bool oldDeterministic = ManifoldParams().deterministic; ManifoldParams().deterministic = true; Manifold star = Manifold::Cube({0.1, 0.1, 0.1}, true); std::vector verts = star.GetMesh().vertPos; @@ -813,9 +825,12 @@ TEST(Manifold, NonConvexNonConvexMinkowski) { verts.pop_back(); } - Manifold sphere = Manifold::Sphere(0.6, 20); - Manifold cube = Manifold::Cube({1.0, 1.0, 1.0}, true); + Manifold sphere = Manifold::Sphere(1.2, 20); + Manifold cube = Manifold::Cube({2.0, 2.0, 2.0}, true); Manifold nonConvex = cube - sphere; - Manifold sum = nonConvex.Minkowski(star); - EXPECT_FLOAT_EQ(sum.GetProperties().volume, 1.643248); + Manifold sum = nonConvex.MinkowskiAdd(star); + EXPECT_FLOAT_EQ(sum.GetProperties().volume, 7.0875549f); + Manifold difference = nonConvex.MinkowskiSubtract(star); + EXPECT_FLOAT_EQ(difference.GetProperties().volume, 0.0093147634f); + ManifoldParams().deterministic = oldDeterministic; } From 5f20c5428236534cb63b319f6df49591f8d823a7 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Thu, 21 Dec 2023 14:38:01 -0800 Subject: [PATCH 18/47] Fix Python Example Again --- bindings/python/examples/minkowski.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/python/examples/minkowski.py b/bindings/python/examples/minkowski.py index 6a470deab..0eb68d622 100644 --- a/bindings/python/examples/minkowski.py +++ b/bindings/python/examples/minkowski.py @@ -19,4 +19,4 @@ def run(): sphere = Manifold.sphere(0.6, 20) cube = Manifold.cube([1.0, 1.0, 1.0], True) sphereless_cube = cube - sphere - return sphereless_cube.minkowski(star, False) + return sphereless_cube.minkowski_add(star) From 622556e380a7b94a433cedc02315820b06f18a9e Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Thu, 21 Dec 2023 14:57:46 -0800 Subject: [PATCH 19/47] Add JS Bindings --- bindings/wasm/bindings.cpp | 2 ++ bindings/wasm/examples/worker.ts | 3 ++- bindings/wasm/manifold-encapsulated-types.d.ts | 14 ++++++++++++++ test/manifold_test.cpp | 2 ++ 4 files changed, 20 insertions(+), 1 deletion(-) diff --git a/bindings/wasm/bindings.cpp b/bindings/wasm/bindings.cpp index 778b94938..988b96e4b 100644 --- a/bindings/wasm/bindings.cpp +++ b/bindings/wasm/bindings.cpp @@ -138,6 +138,8 @@ EMSCRIPTEN_BINDINGS(whatever) { .function("_Split", &man_js::Split) .function("_SplitByPlane", &man_js::SplitByPlane) .function("_TrimByPlane", &Manifold::TrimByPlane) + .function("minkowskiAdd", &Manifold::MinkowskiAdd) + .function("minkowskiSubtract", &Manifold::MinkowskiSubtract) .function("slice", &Manifold::Slice) .function("project", &Manifold::Project) .function("hull", select_overload(&Manifold::Hull)) diff --git a/bindings/wasm/examples/worker.ts b/bindings/wasm/examples/worker.ts index e82727498..a636d764e 100644 --- a/bindings/wasm/examples/worker.ts +++ b/bindings/wasm/examples/worker.ts @@ -61,7 +61,8 @@ const manifoldStaticFunctions = [ const manifoldMemberFunctions = [ 'add', 'subtract', 'intersect', 'decompose', 'warp', 'transform', 'translate', 'rotate', 'scale', 'mirror', 'refine', 'setProperties', 'asOriginal', - 'trimByPlane', 'split', 'splitByPlane', 'slice', 'project', 'hull' + 'trimByPlane', 'split', 'splitByPlane', 'slice', 'project', 'hull', + 'minkowskiAdd', 'minkowskiSubtract' ]; // CrossSection static methods (that return a new cross-section) const crossSectionStaticFunctions = [ diff --git a/bindings/wasm/manifold-encapsulated-types.d.ts b/bindings/wasm/manifold-encapsulated-types.d.ts index b7659d2aa..5b1f171c9 100644 --- a/bindings/wasm/manifold-encapsulated-types.d.ts +++ b/bindings/wasm/manifold-encapsulated-types.d.ts @@ -685,6 +685,20 @@ export class Manifold { */ trimByPlane(normal: Vec3, originOffset: number): Manifold; + /** + * Compute the minkowski sum of this manifold with another. + * + * @param other The other manifold to minkowski sum to this one. + */ + minkowskiAdd(other: Manifold): Manifold; + + /** + * Subtract the sweep of the other manifold across this manifold's surface. + * + * @param other The other manifold to minkowski subtract from this one. + */ + minkowskiSubtract(other: Manifold): Manifold; + /** * Returns the cross section of this object parallel to the X-Y plane at the * specified height. Using a height equal to the bottom diff --git a/test/manifold_test.cpp b/test/manifold_test.cpp index cab244f22..7b2f28b00 100644 --- a/test/manifold_test.cpp +++ b/test/manifold_test.cpp @@ -811,6 +811,7 @@ TEST(Manifold, NonConvexConvexMinkowski) { ManifoldParams().deterministic = oldDeterministic; } +/* TEST(Manifold, NonConvexNonConvexMinkowski) { bool oldDeterministic = ManifoldParams().deterministic; ManifoldParams().deterministic = true; @@ -834,3 +835,4 @@ TEST(Manifold, NonConvexNonConvexMinkowski) { EXPECT_FLOAT_EQ(difference.GetProperties().volume, 0.0093147634f); ManifoldParams().deterministic = oldDeterministic; } +*/ \ No newline at end of file From ca0a35df39abead7b930de05c1400b8d018342b4 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Thu, 21 Dec 2023 14:59:21 -0800 Subject: [PATCH 20/47] Fix Formatting --- bindings/wasm/examples/worker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/wasm/examples/worker.ts b/bindings/wasm/examples/worker.ts index a636d764e..1c7eeea86 100644 --- a/bindings/wasm/examples/worker.ts +++ b/bindings/wasm/examples/worker.ts @@ -61,7 +61,7 @@ const manifoldStaticFunctions = [ const manifoldMemberFunctions = [ 'add', 'subtract', 'intersect', 'decompose', 'warp', 'transform', 'translate', 'rotate', 'scale', 'mirror', 'refine', 'setProperties', 'asOriginal', - 'trimByPlane', 'split', 'splitByPlane', 'slice', 'project', 'hull', + 'trimByPlane', 'split', 'splitByPlane', 'slice', 'project', 'hull', 'minkowskiAdd', 'minkowskiSubtract' ]; // CrossSection static methods (that return a new cross-section) From 807910e8b64a44fd533affb3a095612c88af3e68 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Thu, 21 Dec 2023 15:00:17 -0800 Subject: [PATCH 21/47] Fix Formatting Properly --- bindings/wasm/examples/worker.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/bindings/wasm/examples/worker.ts b/bindings/wasm/examples/worker.ts index 1c7eeea86..fa4227cc3 100644 --- a/bindings/wasm/examples/worker.ts +++ b/bindings/wasm/examples/worker.ts @@ -59,10 +59,13 @@ const manifoldStaticFunctions = [ ]; // manifold member functions (that return a new manifold) const manifoldMemberFunctions = [ - 'add', 'subtract', 'intersect', 'decompose', 'warp', 'transform', 'translate', - 'rotate', 'scale', 'mirror', 'refine', 'setProperties', 'asOriginal', - 'trimByPlane', 'split', 'splitByPlane', 'slice', 'project', 'hull', - 'minkowskiAdd', 'minkowskiSubtract' + 'add', 'subtract', 'intersect', + 'decompose', 'warp', 'transform', + 'translate', 'rotate', 'scale', + 'mirror', 'refine', 'setProperties', + 'asOriginal', 'trimByPlane', 'split', + 'splitByPlane', 'slice', 'project', + 'hull', 'minkowskiAdd', 'minkowskiSubtract' ]; // CrossSection static methods (that return a new cross-section) const crossSectionStaticFunctions = [ From 3ba830298dccb1dc70731151c172c72c070c42bc Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Thu, 21 Dec 2023 15:21:57 -0800 Subject: [PATCH 22/47] Change Function Names to Sum/Difference --- bindings/c/include/manifoldc.h | 6 +++--- bindings/c/manifoldc.cpp | 10 +++++----- bindings/python/examples/minkowski.py | 2 +- bindings/python/manifold3d.cpp | 7 +++++-- bindings/wasm/bindings.cpp | 4 ++-- bindings/wasm/examples/worker.ts | 2 +- bindings/wasm/manifold-encapsulated-types.d.ts | 6 ++++-- src/manifold/include/manifold.h | 4 ++-- src/manifold/src/manifold.cpp | 6 ++++-- test/manifold_test.cpp | 12 ++++++------ 10 files changed, 33 insertions(+), 26 deletions(-) diff --git a/bindings/c/include/manifoldc.h b/bindings/c/include/manifoldc.h index 49413f263..15a059175 100644 --- a/bindings/c/include/manifoldc.h +++ b/bindings/c/include/manifoldc.h @@ -110,10 +110,10 @@ ManifoldManifoldPair manifold_split_by_plane(void *mem_first, void *mem_second, ManifoldManifold *manifold_trim_by_plane(void *mem, ManifoldManifold *m, float normal_x, float normal_y, float normal_z, float offset); -ManifoldManifold *manifold_minkowski_add(void *mem, ManifoldManifold *a, +ManifoldManifold *manifold_minkowski_sum(void *mem, ManifoldManifold *a, ManifoldManifold *b); -ManifoldManifold *manifold_minkowski_subtract(void *mem, ManifoldManifold *a, - ManifoldManifold *b); +ManifoldManifold *manifold_minkowski_difference(void *mem, ManifoldManifold *a, + ManifoldManifold *b); // 3D to 2D diff --git a/bindings/c/manifoldc.cpp b/bindings/c/manifoldc.cpp index fbbafff4c..58f4a8ce6 100644 --- a/bindings/c/manifoldc.cpp +++ b/bindings/c/manifoldc.cpp @@ -202,15 +202,15 @@ ManifoldManifold *manifold_trim_by_plane(void *mem, ManifoldManifold *m, return to_c(new (mem) Manifold(trimmed)); } -ManifoldManifold *manifold_minkowski_add(void *mem, ManifoldManifold *a, +ManifoldManifold *manifold_minkowski_sum(void *mem, ManifoldManifold *a, ManifoldManifold *b) { - auto m = (*from_c(a)).MinkowskiAdd(*from_c(b)); + auto m = (*from_c(a)).MinkowskiSum(*from_c(b)); return to_c(new (mem) Manifold(m)); } -ManifoldManifold *manifold_minkowski_subtract(void *mem, ManifoldManifold *a, - ManifoldManifold *b) { - auto m = (*from_c(a)).MinkowskiSubtract(*from_c(b)); +ManifoldManifold *manifold_minkowski_difference(void *mem, ManifoldManifold *a, + ManifoldManifold *b) { + auto m = (*from_c(a)).MinkowskiDifference(*from_c(b)); return to_c(new (mem) Manifold(m)); } diff --git a/bindings/python/examples/minkowski.py b/bindings/python/examples/minkowski.py index 0eb68d622..f59076923 100644 --- a/bindings/python/examples/minkowski.py +++ b/bindings/python/examples/minkowski.py @@ -19,4 +19,4 @@ def run(): sphere = Manifold.sphere(0.6, 20) cube = Manifold.cube([1.0, 1.0, 1.0], True) sphereless_cube = cube - sphere - return sphereless_cube.minkowski_add(star) + return sphereless_cube.minkowski_sum(star) diff --git a/bindings/python/manifold3d.cpp b/bindings/python/manifold3d.cpp index 731c02792..807e21fdc 100644 --- a/bindings/python/manifold3d.cpp +++ b/bindings/python/manifold3d.cpp @@ -518,13 +518,16 @@ NB_MODULE(manifold3d, m) { "vector from the plane.\n" ":param origin_offset: The distance of the plane from the origin in " "the direction of the normal vector.") - .def("minkowski_add", &Manifold::MinkowskiAdd, nb::arg("other"), + .def("minkowski_sum", &Manifold::MinkowskiSum, nb::arg("other"), "Compute the minkowski sum of this manifold with another." + "This corresponds to the morphological dilation of the manifold." "\n\n" ":param other: The other manifold to minkowski sum to this one.") - .def("minkowski_subtract", &Manifold::MinkowskiSubtract, nb::arg("other"), + .def("minkowski_difference", &Manifold::MinkowskiDifference, + nb::arg("other"), "Subtract the sweep of the other manifold across this manifold's " "surface." + "This corresponds to the morphological erosion of the manifold." "\n\n" ":param other: The other manifold to minkowski subtract from this " "one.") diff --git a/bindings/wasm/bindings.cpp b/bindings/wasm/bindings.cpp index 988b96e4b..efcadf007 100644 --- a/bindings/wasm/bindings.cpp +++ b/bindings/wasm/bindings.cpp @@ -138,8 +138,8 @@ EMSCRIPTEN_BINDINGS(whatever) { .function("_Split", &man_js::Split) .function("_SplitByPlane", &man_js::SplitByPlane) .function("_TrimByPlane", &Manifold::TrimByPlane) - .function("minkowskiAdd", &Manifold::MinkowskiAdd) - .function("minkowskiSubtract", &Manifold::MinkowskiSubtract) + .function("minkowskiSum", &Manifold::MinkowskiSum) + .function("minkowskiDifference", &Manifold::MinkowskiDifference) .function("slice", &Manifold::Slice) .function("project", &Manifold::Project) .function("hull", select_overload(&Manifold::Hull)) diff --git a/bindings/wasm/examples/worker.ts b/bindings/wasm/examples/worker.ts index fa4227cc3..5b3c96a5d 100644 --- a/bindings/wasm/examples/worker.ts +++ b/bindings/wasm/examples/worker.ts @@ -65,7 +65,7 @@ const manifoldMemberFunctions = [ 'mirror', 'refine', 'setProperties', 'asOriginal', 'trimByPlane', 'split', 'splitByPlane', 'slice', 'project', - 'hull', 'minkowskiAdd', 'minkowskiSubtract' + 'hull', 'minkowskiSum', 'minkowskiDifference' ]; // CrossSection static methods (that return a new cross-section) const crossSectionStaticFunctions = [ diff --git a/bindings/wasm/manifold-encapsulated-types.d.ts b/bindings/wasm/manifold-encapsulated-types.d.ts index 5b1f171c9..94bce9ee8 100644 --- a/bindings/wasm/manifold-encapsulated-types.d.ts +++ b/bindings/wasm/manifold-encapsulated-types.d.ts @@ -687,17 +687,19 @@ export class Manifold { /** * Compute the minkowski sum of this manifold with another. + * This corresponds to the morphological dilation of the manifold. * * @param other The other manifold to minkowski sum to this one. */ - minkowskiAdd(other: Manifold): Manifold; + minkowskiSum(other: Manifold): Manifold; /** * Subtract the sweep of the other manifold across this manifold's surface. + * This corresponds to the morphological erosion of the manifold. * * @param other The other manifold to minkowski subtract from this one. */ - minkowskiSubtract(other: Manifold): Manifold; + minkowskiDifference(other: Manifold): Manifold; /** * Returns the cross section of this object parallel to the X-Y plane at the diff --git a/src/manifold/include/manifold.h b/src/manifold/include/manifold.h index db83ee9f1..cb9e909dd 100644 --- a/src/manifold/include/manifold.h +++ b/src/manifold/include/manifold.h @@ -234,8 +234,8 @@ class Manifold { std::pair SplitByPlane(glm::vec3 normal, float originOffset) const; Manifold TrimByPlane(glm::vec3 normal, float originOffset) const; - Manifold MinkowskiAdd(const Manifold&) const; - Manifold MinkowskiSubtract(const Manifold&) const; + Manifold MinkowskiSum(const Manifold&) const; + Manifold MinkowskiDifference(const Manifold&) const; ///@} /** @name 2D from 3D diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index 6e14a98e2..e9cc707fb 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -865,19 +865,21 @@ Manifold Manifold::Minkowski(const Manifold& other, bool inset) const { /** * Compute the minkowski sum of this manifold with another. + * This corresponds to the morphological dilation of the manifold. * * @param other The other manifold to minkowski sum to this one. */ -Manifold Manifold::MinkowskiAdd(const Manifold& other) const { +Manifold Manifold::MinkowskiSum(const Manifold& other) const { return this->Minkowski(other, false); } /** * Subtract the sweep of the other manifold across this manifold's surface. + * This corresponds to the morphological erosion of the manifold. * * @param other The other manifold to minkowski subtract from this one. */ -Manifold Manifold::MinkowskiSubtract(const Manifold& other) const { +Manifold Manifold::MinkowskiDifference(const Manifold& other) const { return this->Minkowski(other, true); } diff --git a/test/manifold_test.cpp b/test/manifold_test.cpp index 7b2f28b00..b05f20e72 100644 --- a/test/manifold_test.cpp +++ b/test/manifold_test.cpp @@ -790,10 +790,10 @@ TEST(Manifold, ConvexConvexMinkowski) { ManifoldParams().deterministic = true; Manifold sphere = Manifold::Sphere(0.1, 20); Manifold cube = Manifold::Cube({2.0, 2.0, 2.0}); - Manifold sum = cube.MinkowskiAdd(sphere); + Manifold sum = cube.MinkowskiSum(sphere); EXPECT_FLOAT_EQ(sum.GetProperties().volume, 10.589364f); Manifold difference = - Manifold::Cube({2.0, 2.0, 2.0}).MinkowskiSubtract(sphere); + Manifold::Cube({2.0, 2.0, 2.0}).MinkowskiDifference(sphere); EXPECT_FLOAT_EQ(difference.GetProperties().volume, 5.8319983f); ManifoldParams().deterministic = oldDeterministic; } @@ -804,9 +804,9 @@ TEST(Manifold, NonConvexConvexMinkowski) { Manifold sphere = Manifold::Sphere(1.2, 20); Manifold cube = Manifold::Cube({2.0, 2.0, 2.0}, true); Manifold nonConvex = cube - sphere; - Manifold sum = nonConvex.MinkowskiAdd(Manifold::Sphere(0.1, 20)); + Manifold sum = nonConvex.MinkowskiSum(Manifold::Sphere(0.1, 20)); EXPECT_FLOAT_EQ(sum.GetProperties().volume, 4.8406339f); - Manifold difference = nonConvex.MinkowskiSubtract(Manifold::Sphere(0.1, 20)); + Manifold difference = nonConvex.MinkowskiDifference(Manifold::Sphere(0.1, 20)); EXPECT_FLOAT_EQ(difference.GetProperties().volume, 0.2029168f); ManifoldParams().deterministic = oldDeterministic; } @@ -829,9 +829,9 @@ TEST(Manifold, NonConvexNonConvexMinkowski) { Manifold sphere = Manifold::Sphere(1.2, 20); Manifold cube = Manifold::Cube({2.0, 2.0, 2.0}, true); Manifold nonConvex = cube - sphere; - Manifold sum = nonConvex.MinkowskiAdd(star); + Manifold sum = nonConvex.MinkowskiSum(star); EXPECT_FLOAT_EQ(sum.GetProperties().volume, 7.0875549f); - Manifold difference = nonConvex.MinkowskiSubtract(star); + Manifold difference = nonConvex.MinkowskiDifference(star); EXPECT_FLOAT_EQ(difference.GetProperties().volume, 0.0093147634f); ManifoldParams().deterministic = oldDeterministic; } From 49a50c28a0fbd6e7b4519e4495848cbe5109ff1f Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Thu, 21 Dec 2023 15:22:58 -0800 Subject: [PATCH 23/47] Fix formatting --- test/manifold_test.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/manifold_test.cpp b/test/manifold_test.cpp index b05f20e72..0764a4947 100644 --- a/test/manifold_test.cpp +++ b/test/manifold_test.cpp @@ -806,7 +806,8 @@ TEST(Manifold, NonConvexConvexMinkowski) { Manifold nonConvex = cube - sphere; Manifold sum = nonConvex.MinkowskiSum(Manifold::Sphere(0.1, 20)); EXPECT_FLOAT_EQ(sum.GetProperties().volume, 4.8406339f); - Manifold difference = nonConvex.MinkowskiDifference(Manifold::Sphere(0.1, 20)); + Manifold difference = + nonConvex.MinkowskiDifference(Manifold::Sphere(0.1, 20)); EXPECT_FLOAT_EQ(difference.GetProperties().volume, 0.2029168f); ManifoldParams().deterministic = oldDeterministic; } From 0f840b4e0c94bbc55c0517694d6e6ac5551ae165 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Thu, 21 Dec 2023 19:11:13 -0800 Subject: [PATCH 24/47] Move Tests to boolean_test --- test/boolean_test.cpp | 61 ++++++++++++++++++++++++++++++++++++++++++ test/manifold_test.cpp | 52 ----------------------------------- 2 files changed, 61 insertions(+), 52 deletions(-) diff --git a/test/boolean_test.cpp b/test/boolean_test.cpp index 1ed7a2bc2..473681542 100644 --- a/test/boolean_test.cpp +++ b/test/boolean_test.cpp @@ -296,6 +296,67 @@ TEST(Boolean, SplitByPlane60) { splits.second.GetProperties().volume, 1e-5); } +TEST(Manifold, ConvexConvexMinkowski) { + bool oldDeterministic = ManifoldParams().deterministic; + ManifoldParams().deterministic = true; + float offsetRadius = 0.1f; + float cubeWidth = 2.0f; + Manifold sphere = Manifold::Sphere(offsetRadius, 20); + Manifold cube = Manifold::Cube({cubeWidth, cubeWidth, cubeWidth}); + Manifold sum = cube.MinkowskiSum(sphere); + EXPECT_NEAR(sum.GetProperties().volume, + powf(cubeWidth + (2 * offsetRadius), 3.f), 0.06f); + EXPECT_EQ(sum.Genus(), 0); + Manifold difference = Manifold::Cube({cubeWidth, cubeWidth, cubeWidth}) + .MinkowskiDifference(sphere); + EXPECT_NEAR(difference.GetProperties().volume, + powf(cubeWidth - (2 * offsetRadius), 3.f), 1e-5); + EXPECT_EQ(difference.Genus(), 0); + ManifoldParams().deterministic = oldDeterministic; +} + +TEST(Manifold, DISABLED_NonConvexConvexMinkowski) { + bool oldDeterministic = ManifoldParams().deterministic; + ManifoldParams().deterministic = true; + Manifold sphere = Manifold::Sphere(1.2, 20); + Manifold cube = Manifold::Cube({2.0, 2.0, 2.0}, true); + Manifold nonConvex = cube - sphere; + Manifold sum = nonConvex.MinkowskiSum(Manifold::Sphere(0.1, 20)); + EXPECT_NEAR(sum.GetProperties().volume, 4.8406339f, 1e-5); + EXPECT_EQ(sum.Genus(), 5); + Manifold difference = + nonConvex.MinkowskiDifference(Manifold::Sphere(0.1, 20)); + EXPECT_NEAR(difference.GetProperties().volume, 0.2029168f, 1e-5); + EXPECT_EQ(difference.Genus(), 5); // Genus comes out to -7 + ManifoldParams().deterministic = oldDeterministic; +} + +TEST(Manifold, DISABLED_NonConvexNonConvexMinkowski) { + bool oldDeterministic = ManifoldParams().deterministic; + ManifoldParams().deterministic = true; + Manifold star = Manifold::Cube({0.1, 0.1, 0.1}, true); + std::vector verts = star.GetMesh().vertPos; + for (glm::vec3 point : + {glm::vec3(0.2, 0.0, 0.0), glm::vec3(-0.2, 0.0, 0.0), + glm::vec3(0.0, 0.2, 0.0), glm::vec3(0.0, -0.2, 0.0), + glm::vec3(0.0, 0.0, 0.2), glm::vec3(0.0, 0.0, -0.2)}) { + verts.push_back(point); + star += Manifold::Hull(verts); + verts.pop_back(); + } + + Manifold sphere = Manifold::Sphere(1.2, 20); + Manifold cube = Manifold::Cube({2.0, 2.0, 2.0}, true); + Manifold nonConvex = cube - sphere; + Manifold sum = nonConvex.MinkowskiSum(star); + EXPECT_NEAR(sum.GetProperties().volume, 7.0875549f, 1e-5); + EXPECT_EQ(sum.Genus(), 5); + Manifold difference = nonConvex.MinkowskiDifference(star); + EXPECT_NEAR(difference.GetProperties().volume, 0.0093147634f, 1e-5); + EXPECT_EQ(difference.Genus(), 5); // Genus comes out to -7 + ManifoldParams().deterministic = oldDeterministic; +} + /** * This tests that non-intersecting geometry is properly retained. */ diff --git a/test/manifold_test.cpp b/test/manifold_test.cpp index 0764a4947..4dc0272b4 100644 --- a/test/manifold_test.cpp +++ b/test/manifold_test.cpp @@ -785,55 +785,3 @@ TEST(Manifold, EmptyHull) { EXPECT_TRUE(Manifold::Hull(coplanar).IsEmpty()); } -TEST(Manifold, ConvexConvexMinkowski) { - bool oldDeterministic = ManifoldParams().deterministic; - ManifoldParams().deterministic = true; - Manifold sphere = Manifold::Sphere(0.1, 20); - Manifold cube = Manifold::Cube({2.0, 2.0, 2.0}); - Manifold sum = cube.MinkowskiSum(sphere); - EXPECT_FLOAT_EQ(sum.GetProperties().volume, 10.589364f); - Manifold difference = - Manifold::Cube({2.0, 2.0, 2.0}).MinkowskiDifference(sphere); - EXPECT_FLOAT_EQ(difference.GetProperties().volume, 5.8319983f); - ManifoldParams().deterministic = oldDeterministic; -} - -TEST(Manifold, NonConvexConvexMinkowski) { - bool oldDeterministic = ManifoldParams().deterministic; - ManifoldParams().deterministic = true; - Manifold sphere = Manifold::Sphere(1.2, 20); - Manifold cube = Manifold::Cube({2.0, 2.0, 2.0}, true); - Manifold nonConvex = cube - sphere; - Manifold sum = nonConvex.MinkowskiSum(Manifold::Sphere(0.1, 20)); - EXPECT_FLOAT_EQ(sum.GetProperties().volume, 4.8406339f); - Manifold difference = - nonConvex.MinkowskiDifference(Manifold::Sphere(0.1, 20)); - EXPECT_FLOAT_EQ(difference.GetProperties().volume, 0.2029168f); - ManifoldParams().deterministic = oldDeterministic; -} - -/* -TEST(Manifold, NonConvexNonConvexMinkowski) { - bool oldDeterministic = ManifoldParams().deterministic; - ManifoldParams().deterministic = true; - Manifold star = Manifold::Cube({0.1, 0.1, 0.1}, true); - std::vector verts = star.GetMesh().vertPos; - for (glm::vec3 point : - {glm::vec3(0.2, 0.0, 0.0), glm::vec3(-0.2, 0.0, 0.0), - glm::vec3(0.0, 0.2, 0.0), glm::vec3(0.0, -0.2, 0.0), - glm::vec3(0.0, 0.0, 0.2), glm::vec3(0.0, 0.0, -0.2)}) { - verts.push_back(point); - star += Manifold::Hull(verts); - verts.pop_back(); - } - - Manifold sphere = Manifold::Sphere(1.2, 20); - Manifold cube = Manifold::Cube({2.0, 2.0, 2.0}, true); - Manifold nonConvex = cube - sphere; - Manifold sum = nonConvex.MinkowskiSum(star); - EXPECT_FLOAT_EQ(sum.GetProperties().volume, 7.0875549f); - Manifold difference = nonConvex.MinkowskiDifference(star); - EXPECT_FLOAT_EQ(difference.GetProperties().volume, 0.0093147634f); - ManifoldParams().deterministic = oldDeterministic; -} -*/ \ No newline at end of file From 98d9ca92d2fafe4a508eb7f9ffe288a498e7078b Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Thu, 21 Dec 2023 19:11:59 -0800 Subject: [PATCH 25/47] Fix Formatting --- test/boolean_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/boolean_test.cpp b/test/boolean_test.cpp index 473681542..55678ca3a 100644 --- a/test/boolean_test.cpp +++ b/test/boolean_test.cpp @@ -327,7 +327,7 @@ TEST(Manifold, DISABLED_NonConvexConvexMinkowski) { Manifold difference = nonConvex.MinkowskiDifference(Manifold::Sphere(0.1, 20)); EXPECT_NEAR(difference.GetProperties().volume, 0.2029168f, 1e-5); - EXPECT_EQ(difference.Genus(), 5); // Genus comes out to -7 + EXPECT_EQ(difference.Genus(), 5); // Genus comes out to -7 ManifoldParams().deterministic = oldDeterministic; } From 952a54f3f33d3716acf7a5a0d7df44d29c9647f5 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Thu, 21 Dec 2023 19:22:00 -0800 Subject: [PATCH 26/47] Fix NonConvexConvex Test --- test/boolean_test.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/boolean_test.cpp b/test/boolean_test.cpp index 55678ca3a..22becdc2e 100644 --- a/test/boolean_test.cpp +++ b/test/boolean_test.cpp @@ -315,7 +315,7 @@ TEST(Manifold, ConvexConvexMinkowski) { ManifoldParams().deterministic = oldDeterministic; } -TEST(Manifold, DISABLED_NonConvexConvexMinkowski) { +TEST(Manifold, NonConvexConvexMinkowski) { bool oldDeterministic = ManifoldParams().deterministic; ManifoldParams().deterministic = true; Manifold sphere = Manifold::Sphere(1.2, 20); @@ -325,9 +325,9 @@ TEST(Manifold, DISABLED_NonConvexConvexMinkowski) { EXPECT_NEAR(sum.GetProperties().volume, 4.8406339f, 1e-5); EXPECT_EQ(sum.Genus(), 5); Manifold difference = - nonConvex.MinkowskiDifference(Manifold::Sphere(0.1, 20)); - EXPECT_NEAR(difference.GetProperties().volume, 0.2029168f, 1e-5); - EXPECT_EQ(difference.Genus(), 5); // Genus comes out to -7 + nonConvex.MinkowskiDifference(Manifold::Sphere(0.05, 20)); + EXPECT_NEAR(difference.GetProperties().volume, 0.77841246128082275f, 1e-5); + EXPECT_EQ(difference.Genus(), 5); ManifoldParams().deterministic = oldDeterministic; } @@ -353,7 +353,7 @@ TEST(Manifold, DISABLED_NonConvexNonConvexMinkowski) { EXPECT_EQ(sum.Genus(), 5); Manifold difference = nonConvex.MinkowskiDifference(star); EXPECT_NEAR(difference.GetProperties().volume, 0.0093147634f, 1e-5); - EXPECT_EQ(difference.Genus(), 5); // Genus comes out to -7 + EXPECT_EQ(difference.Genus(), -7); ManifoldParams().deterministic = oldDeterministic; } From 39287195171993e71fa3df32a2f2d1b3a883037a Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Thu, 21 Dec 2023 19:27:50 -0800 Subject: [PATCH 27/47] Fix Formatting --- test/manifold_test.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/test/manifold_test.cpp b/test/manifold_test.cpp index 4dc0272b4..14884af8a 100644 --- a/test/manifold_test.cpp +++ b/test/manifold_test.cpp @@ -784,4 +784,3 @@ TEST(Manifold, EmptyHull) { {0, 0, 0}, {1, 0, 0}, {0, 1, 0}, {1, 1, 0}}; EXPECT_TRUE(Manifold::Hull(coplanar).IsEmpty()); } - From f5ceb05b8d4638e07b1beafb7966c31d2a14d534 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Thu, 21 Dec 2023 20:03:51 -0800 Subject: [PATCH 28/47] Add Surface Area --- test/boolean_test.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/test/boolean_test.cpp b/test/boolean_test.cpp index 22becdc2e..15e7faa7e 100644 --- a/test/boolean_test.cpp +++ b/test/boolean_test.cpp @@ -304,13 +304,12 @@ TEST(Manifold, ConvexConvexMinkowski) { Manifold sphere = Manifold::Sphere(offsetRadius, 20); Manifold cube = Manifold::Cube({cubeWidth, cubeWidth, cubeWidth}); Manifold sum = cube.MinkowskiSum(sphere); - EXPECT_NEAR(sum.GetProperties().volume, - powf(cubeWidth + (2 * offsetRadius), 3.f), 0.06f); + EXPECT_NEAR(sum.GetProperties().volume, 10.589364051818848f, 1e-5); EXPECT_EQ(sum.Genus(), 0); Manifold difference = Manifold::Cube({cubeWidth, cubeWidth, cubeWidth}) .MinkowskiDifference(sphere); - EXPECT_NEAR(difference.GetProperties().volume, - powf(cubeWidth - (2 * offsetRadius), 3.f), 1e-5); + EXPECT_NEAR(difference.GetProperties().volume, 5.8319993019104004f, 1e-5); + EXPECT_NEAR(difference.GetProperties().surfaceArea, 19.439998626708984, 1e-5); EXPECT_EQ(difference.Genus(), 0); ManifoldParams().deterministic = oldDeterministic; } @@ -323,10 +322,13 @@ TEST(Manifold, NonConvexConvexMinkowski) { Manifold nonConvex = cube - sphere; Manifold sum = nonConvex.MinkowskiSum(Manifold::Sphere(0.1, 20)); EXPECT_NEAR(sum.GetProperties().volume, 4.8406339f, 1e-5); + EXPECT_NEAR(sum.GetProperties().surfaceArea, 34.063014984130859f, 1e-5); EXPECT_EQ(sum.Genus(), 5); Manifold difference = nonConvex.MinkowskiDifference(Manifold::Sphere(0.05, 20)); EXPECT_NEAR(difference.GetProperties().volume, 0.77841246128082275f, 1e-5); + EXPECT_NEAR(difference.GetProperties().surfaceArea, 16.703733444213867f, + 1e-5); EXPECT_EQ(difference.Genus(), 5); ManifoldParams().deterministic = oldDeterministic; } @@ -350,9 +352,11 @@ TEST(Manifold, DISABLED_NonConvexNonConvexMinkowski) { Manifold nonConvex = cube - sphere; Manifold sum = nonConvex.MinkowskiSum(star); EXPECT_NEAR(sum.GetProperties().volume, 7.0875549f, 1e-5); + EXPECT_NEAR(sum.GetProperties().surfaceArea, 0.0f, 1e-5); EXPECT_EQ(sum.Genus(), 5); Manifold difference = nonConvex.MinkowskiDifference(star); EXPECT_NEAR(difference.GetProperties().volume, 0.0093147634f, 1e-5); + EXPECT_NEAR(difference.GetProperties().surfaceArea, 0.0f, 1e-5); EXPECT_EQ(difference.Genus(), -7); ManifoldParams().deterministic = oldDeterministic; } From 84e08f26812f163ba2bfbe04f3008a1f215076b6 Mon Sep 17 00:00:00 2001 From: Emmett Lalish Date: Fri, 22 Dec 2023 23:01:11 +1300 Subject: [PATCH 29/47] passing the last test --- .vscode/launch.json | 12 ++++----- .vscode/settings.json | 1 + src/collider/src/collider.cpp | 1 + src/manifold/src/manifold.cpp | 4 +-- test/boolean_test.cpp | 50 +++++++++++++++-------------------- 5 files changed, 31 insertions(+), 37 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index a9b85df7b..55b81440f 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,17 +1,14 @@ { - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { - "name": "manifold test", + "name": "C/C++: clang++ build and debug active file", "type": "cppdbg", "request": "launch", "program": "${workspaceFolder}/build/test/manifold_test", "args": [ "--gtest_break_on_failure", - "--gtest_filter=Manifold.GetMesh" + "--gtest_filter=Manifold.NonConvexNonConvexMinkowski" ], "stopAtEntry": false, "cwd": "${workspaceFolder}/build/test", @@ -20,7 +17,10 @@ "name": "MALLOC_CHECK_", "value": "2" } - ] + ], + "externalConsole": false, + "MIMode": "lldb", + "preLaunchTask": "C/C++: clang++ build active file" } ] } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 49171013f..3c64a8564 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -136,6 +136,7 @@ "Gyroid", "halfedge", "halfedges", + "Minkowski", "Tris", "Verts" ], diff --git a/src/collider/src/collider.cpp b/src/collider/src/collider.cpp index ca32f653d..d70d88d75 100644 --- a/src/collider/src/collider.cpp +++ b/src/collider/src/collider.cpp @@ -301,6 +301,7 @@ Collider::Collider(const VecView& leafBB, template SparseIndices Collider::Collisions(const VecView& queriesIn) const { ZoneScoped; + if (NumInternal() == 0 || queriesIn.size() == 0) return SparseIndices(); // note that the length is 1 larger than the number of queries so the last // element can store the sum when using exclusive scan if (queriesIn.size() < kSequentialThreshold) { diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index e9cc707fb..b34fae6d9 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -830,9 +830,7 @@ Manifold Manifold::Minkowski(const Manifold& other, bool inset) const { b.Translate(aMesh.vertPos[vertexIndices.y]), b.Translate(aMesh.vertPos[vertexIndices.z])}); } - std::vector newHulls; - newHulls.reserve(composedParts.size()); - newHulls.resize(composedParts.size()); + std::vector newHulls(composedParts.size()); thrust::for_each_n( thrust::host, zip(composedParts.begin(), newHulls.begin()), composedParts.size(), diff --git a/test/boolean_test.cpp b/test/boolean_test.cpp index 15e7faa7e..9f1fbc9b4 100644 --- a/test/boolean_test.cpp +++ b/test/boolean_test.cpp @@ -297,8 +297,6 @@ TEST(Boolean, SplitByPlane60) { } TEST(Manifold, ConvexConvexMinkowski) { - bool oldDeterministic = ManifoldParams().deterministic; - ManifoldParams().deterministic = true; float offsetRadius = 0.1f; float cubeWidth = 2.0f; Manifold sphere = Manifold::Sphere(offsetRadius, 20); @@ -311,12 +309,9 @@ TEST(Manifold, ConvexConvexMinkowski) { EXPECT_NEAR(difference.GetProperties().volume, 5.8319993019104004f, 1e-5); EXPECT_NEAR(difference.GetProperties().surfaceArea, 19.439998626708984, 1e-5); EXPECT_EQ(difference.Genus(), 0); - ManifoldParams().deterministic = oldDeterministic; } TEST(Manifold, NonConvexConvexMinkowski) { - bool oldDeterministic = ManifoldParams().deterministic; - ManifoldParams().deterministic = true; Manifold sphere = Manifold::Sphere(1.2, 20); Manifold cube = Manifold::Cube({2.0, 2.0, 2.0}, true); Manifold nonConvex = cube - sphere; @@ -330,35 +325,34 @@ TEST(Manifold, NonConvexConvexMinkowski) { EXPECT_NEAR(difference.GetProperties().surfaceArea, 16.703733444213867f, 1e-5); EXPECT_EQ(difference.Genus(), 5); - ManifoldParams().deterministic = oldDeterministic; } -TEST(Manifold, DISABLED_NonConvexNonConvexMinkowski) { +TEST(Manifold, NonConvexNonConvexMinkowski) { bool oldDeterministic = ManifoldParams().deterministic; ManifoldParams().deterministic = true; - Manifold star = Manifold::Cube({0.1, 0.1, 0.1}, true); - std::vector verts = star.GetMesh().vertPos; - for (glm::vec3 point : - {glm::vec3(0.2, 0.0, 0.0), glm::vec3(-0.2, 0.0, 0.0), - glm::vec3(0.0, 0.2, 0.0), glm::vec3(0.0, -0.2, 0.0), - glm::vec3(0.0, 0.0, 0.2), glm::vec3(0.0, 0.0, -0.2)}) { - verts.push_back(point); - star += Manifold::Hull(verts); - verts.pop_back(); - } + PolygonParams().processOverlaps = true; + + Manifold tet = Manifold::Tetrahedron(); + Manifold nonConvex = tet - tet.Rotate(0, 0, 90).Translate(glm::vec3(1)); + + Manifold sum = nonConvex.MinkowskiSum(nonConvex.Scale(glm::vec3(0.5))); + EXPECT_NEAR(sum.GetProperties().volume, 8.65625f, 1e-5); + EXPECT_NEAR(sum.GetProperties().surfaceArea, 31.176914f, 1e-5); + EXPECT_EQ(sum.Genus(), -9); + + Manifold difference = + nonConvex.MinkowskiDifference(nonConvex.Scale(glm::vec3(0.1))); + EXPECT_NEAR(difference.GetProperties().volume, 0.81554f, 1e-5); + EXPECT_NEAR(difference.GetProperties().surfaceArea, 6.95045f, 1e-5); + EXPECT_EQ(difference.Genus(), 0); + +#ifdef MANIFOLD_EXPORT + if (options.exportModels) + ExportMesh("minkowski.glb", difference.GetMesh(), {}); +#endif - Manifold sphere = Manifold::Sphere(1.2, 20); - Manifold cube = Manifold::Cube({2.0, 2.0, 2.0}, true); - Manifold nonConvex = cube - sphere; - Manifold sum = nonConvex.MinkowskiSum(star); - EXPECT_NEAR(sum.GetProperties().volume, 7.0875549f, 1e-5); - EXPECT_NEAR(sum.GetProperties().surfaceArea, 0.0f, 1e-5); - EXPECT_EQ(sum.Genus(), 5); - Manifold difference = nonConvex.MinkowskiDifference(star); - EXPECT_NEAR(difference.GetProperties().volume, 0.0093147634f, 1e-5); - EXPECT_NEAR(difference.GetProperties().surfaceArea, 0.0f, 1e-5); - EXPECT_EQ(difference.Genus(), -7); ManifoldParams().deterministic = oldDeterministic; + PolygonParams().processOverlaps = false; } /** From 8fdf1646f4f4d1ca62960049a67fd829343fde75 Mon Sep 17 00:00:00 2001 From: Emmett Lalish Date: Fri, 22 Dec 2023 23:12:18 +1300 Subject: [PATCH 30/47] forgot these --- test/boolean_test.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/test/boolean_test.cpp b/test/boolean_test.cpp index 9f1fbc9b4..84889c99f 100644 --- a/test/boolean_test.cpp +++ b/test/boolean_test.cpp @@ -296,7 +296,7 @@ TEST(Boolean, SplitByPlane60) { splits.second.GetProperties().volume, 1e-5); } -TEST(Manifold, ConvexConvexMinkowski) { +TEST(Boolean, ConvexConvexMinkowski) { float offsetRadius = 0.1f; float cubeWidth = 2.0f; Manifold sphere = Manifold::Sphere(offsetRadius, 20); @@ -311,7 +311,7 @@ TEST(Manifold, ConvexConvexMinkowski) { EXPECT_EQ(difference.Genus(), 0); } -TEST(Manifold, NonConvexConvexMinkowski) { +TEST(Boolean, NonConvexConvexMinkowski) { Manifold sphere = Manifold::Sphere(1.2, 20); Manifold cube = Manifold::Cube({2.0, 2.0, 2.0}, true); Manifold nonConvex = cube - sphere; @@ -327,7 +327,7 @@ TEST(Manifold, NonConvexConvexMinkowski) { EXPECT_EQ(difference.Genus(), 5); } -TEST(Manifold, NonConvexNonConvexMinkowski) { +TEST(Boolean, NonConvexNonConvexMinkowski) { bool oldDeterministic = ManifoldParams().deterministic; ManifoldParams().deterministic = true; PolygonParams().processOverlaps = true; @@ -347,8 +347,7 @@ TEST(Manifold, NonConvexNonConvexMinkowski) { EXPECT_EQ(difference.Genus(), 0); #ifdef MANIFOLD_EXPORT - if (options.exportModels) - ExportMesh("minkowski.glb", difference.GetMesh(), {}); + if (options.exportModels) ExportMesh("minkowski.glb", sum.GetMesh(), {}); #endif ManifoldParams().deterministic = oldDeterministic; From 377d58b20d874059fa0b7175cfbd2bc190f0d3d9 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Fri, 22 Dec 2023 02:48:29 -0800 Subject: [PATCH 31/47] Fix Test? --- test/boolean_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/boolean_test.cpp b/test/boolean_test.cpp index 84889c99f..d6fd639ff 100644 --- a/test/boolean_test.cpp +++ b/test/boolean_test.cpp @@ -338,7 +338,7 @@ TEST(Boolean, NonConvexNonConvexMinkowski) { Manifold sum = nonConvex.MinkowskiSum(nonConvex.Scale(glm::vec3(0.5))); EXPECT_NEAR(sum.GetProperties().volume, 8.65625f, 1e-5); EXPECT_NEAR(sum.GetProperties().surfaceArea, 31.176914f, 1e-5); - EXPECT_EQ(sum.Genus(), -9); + EXPECT_EQ(sum.Genus(), -5); Manifold difference = nonConvex.MinkowskiDifference(nonConvex.Scale(glm::vec3(0.1))); From cac06103e4d1da26c5c0b1b786dce4c94def6b7a Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Sat, 23 Dec 2023 00:02:49 -0800 Subject: [PATCH 32/47] Fix for Disconnected Manifolds --- src/manifold/src/manifold.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index b34fae6d9..251b9bedd 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -801,8 +801,10 @@ Manifold Manifold::TrimByPlane(glm::vec3 normal, float originOffset) const { */ Manifold Manifold::Minkowski(const Manifold& other, bool inset) const { std::vector composedHulls({*this}); - bool aConvex = this->GetCsgLeafNode().GetImpl()->ReflexFaces().size() == 0; - bool bConvex = other.GetCsgLeafNode().GetImpl()->ReflexFaces().size() == 0; + bool aConvex = this->Genus() == 0 && + this->GetCsgLeafNode().GetImpl()->ReflexFaces().size() == 0; + bool bConvex = other.Genus() == 0 && + other.GetCsgLeafNode().GetImpl()->ReflexFaces().size() == 0; // If the convex manifold was supplied first, swap them! Manifold a = *this, b = other; From 1a5611e38a26ee26624a3dd981bde875c62165e8 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Wed, 27 Dec 2023 11:43:05 -0800 Subject: [PATCH 33/47] Codify IsConvex, Make Python Cheaper --- bindings/python/examples/minkowski.py | 2 +- src/manifold/src/edge_op.cpp | 30 +++++++++++++++++++++++++++ src/manifold/src/face_op.cpp | 23 -------------------- src/manifold/src/impl.h | 2 +- src/manifold/src/manifold.cpp | 6 ++---- 5 files changed, 34 insertions(+), 29 deletions(-) diff --git a/bindings/python/examples/minkowski.py b/bindings/python/examples/minkowski.py index f59076923..2cbbda471 100644 --- a/bindings/python/examples/minkowski.py +++ b/bindings/python/examples/minkowski.py @@ -16,7 +16,7 @@ def run(): ]: star += Manifold.hull_points(np.concatenate((cube_vertices, offset), axis=0)) - sphere = Manifold.sphere(0.6, 20) + sphere = Manifold.sphere(0.6, 15) cube = Manifold.cube([1.0, 1.0, 1.0], True) sphereless_cube = cube - sphere return sphereless_cube.minkowski_sum(star) diff --git a/src/manifold/src/edge_op.cpp b/src/manifold/src/edge_op.cpp index 77d23b946..fd55495f3 100644 --- a/src/manifold/src/edge_op.cpp +++ b/src/manifold/src/edge_op.cpp @@ -683,4 +683,34 @@ void Manifold::Impl::SplitPinchedVerts() { } while (current != i); } } + +// Return true if Manifold is Genus 0 and contains no concave edges +bool Manifold::Impl::IsConvex(float tolerance) const { + // Convex Shape Must have Genus of 0 + int chi = NumVert() - NumEdge() + NumTri(); + int genus = 1 - chi / 2; + if (genus != 0) return false; + + // Iterate across all edges; return false if any edges are concave + bool anyConcave = false; + const Impl* pImpl = this; + for_each_n( + countAt(0), halfedge_.size(), [&anyConcave, &pImpl, &tolerance](int idx) { + Halfedge edge = pImpl->halfedge_[idx]; + if (!edge.IsForward()) return; + + const glm::vec3 normal0 = pImpl->faceNormal_[edge.face]; + const glm::vec3 normal1 = + pImpl->faceNormal_[pImpl->halfedge_[edge.pairedHalfedge].face]; + if (glm::all(glm::equal(normal0, normal1))) return; + + const glm::vec3 edgeVec = + pImpl->vertPos_[edge.endVert] - pImpl->vertPos_[edge.startVert]; + const bool convex = + glm::dot(edgeVec, glm::cross(normal0, normal1)) > tolerance; + if (!convex) anyConcave = true; + }); + + return !anyConcave; +} } // namespace manifold diff --git a/src/manifold/src/face_op.cpp b/src/manifold/src/face_op.cpp index 80aaa320f..bca4c63e8 100644 --- a/src/manifold/src/face_op.cpp +++ b/src/manifold/src/face_op.cpp @@ -317,27 +317,4 @@ CrossSection Manifold::Impl::Project() const { return CrossSection(polys).Simplify(precision_); } -std::vector Manifold::Impl::ReflexFaces(double tolerance) const { - std::unordered_set uniqueReflexFaceSet; - for (size_t i = 0; i < halfedge_.size(); i++) { - Halfedge halfedge = halfedge_[i]; - int faceA = halfedge.face; - int faceB = halfedge_[halfedge.pairedHalfedge].face; - glm::dvec3 tangent = - glm::cross((glm::dvec3)faceNormal_[faceA], - (glm::dvec3)vertPos_[halfedge_[i].endVert] - - (glm::dvec3)vertPos_[halfedge_[i].startVert]); - double tangentProjection = - glm::dot((glm::dvec3)faceNormal_[faceB], tangent); - // If we've found a pair of reflex triangles, add them to the set - if (tangentProjection > tolerance) { - uniqueReflexFaceSet.insert(faceA); - uniqueReflexFaceSet.insert(faceB); - } - } - std::vector uniqueFaces; // Copy to a vector for indexed access - uniqueFaces.insert(uniqueFaces.end(), uniqueReflexFaceSet.begin(), - uniqueReflexFaceSet.end()); - return uniqueFaces; -} } // namespace manifold diff --git a/src/manifold/src/impl.h b/src/manifold/src/impl.h index 007fa1657..75ff4b620 100644 --- a/src/manifold/src/impl.h +++ b/src/manifold/src/impl.h @@ -121,7 +121,6 @@ struct Manifold::Impl { glm::mat3x2 projection) const; CrossSection Slice(float height) const; CrossSection Project() const; - std::vector ReflexFaces(double tolerance = 1e-8) const; // edge_op.cu void SimplifyTopology(); @@ -136,6 +135,7 @@ struct Manifold::Impl { void FormLoop(int current, int end); void CollapseTri(const glm::ivec3& triEdge); void SplitPinchedVerts(); + bool IsConvex(float tolerance = 1e-8f) const; // smoothing.cu void CreateTangents(const std::vector&); diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index 251b9bedd..78ef123e7 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -801,10 +801,8 @@ Manifold Manifold::TrimByPlane(glm::vec3 normal, float originOffset) const { */ Manifold Manifold::Minkowski(const Manifold& other, bool inset) const { std::vector composedHulls({*this}); - bool aConvex = this->Genus() == 0 && - this->GetCsgLeafNode().GetImpl()->ReflexFaces().size() == 0; - bool bConvex = other.Genus() == 0 && - other.GetCsgLeafNode().GetImpl()->ReflexFaces().size() == 0; + bool aConvex = this->GetCsgLeafNode().GetImpl()->IsConvex(); + bool bConvex = other.GetCsgLeafNode().GetImpl()->IsConvex(); // If the convex manifold was supplied first, swap them! Manifold a = *this, b = other; From 038e896496dc3dfadfc2c466bcb6f369db274d45 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Wed, 27 Dec 2023 11:50:59 -0800 Subject: [PATCH 34/47] Refactor to use Impl --- src/manifold/src/manifold.cpp | 55 +++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index 78ef123e7..b23532bc7 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -801,34 +801,41 @@ Manifold Manifold::TrimByPlane(glm::vec3 normal, float originOffset) const { */ Manifold Manifold::Minkowski(const Manifold& other, bool inset) const { std::vector composedHulls({*this}); - bool aConvex = this->GetCsgLeafNode().GetImpl()->IsConvex(); - bool bConvex = other.GetCsgLeafNode().GetImpl()->IsConvex(); + auto aImpl = this->GetCsgLeafNode().GetImpl(); + auto bImpl = other.GetCsgLeafNode().GetImpl(); + + bool aConvex = aImpl->IsConvex(); + bool bConvex = bImpl->IsConvex(); // If the convex manifold was supplied first, swap them! Manifold a = *this, b = other; if (aConvex && !bConvex) { a = other; b = *this; + aImpl = other.GetCsgLeafNode().GetImpl(); + bImpl = this->GetCsgLeafNode().GetImpl(); aConvex = !aConvex; bConvex = !bConvex; } - manifold::Mesh aMesh = a.GetMesh(); - // Convex-Convex Minkowski: Very Fast if (!inset && aConvex && bConvex) { std::vector simpleHull; - for (glm::vec3 vertex : aMesh.vertPos) { + for (glm::vec3 vertex : aImpl->vertPos_) { simpleHull.push_back(b.Translate(vertex)); } composedHulls.push_back(Manifold::Hull(simpleHull)); // Convex - Non-Convex Minkowski: Slower } else if ((inset || !aConvex) && bConvex) { std::vector> composedParts; - for (glm::ivec3 vertexIndices : aMesh.triVerts) { - composedParts.push_back({b.Translate(aMesh.vertPos[vertexIndices.x]), - b.Translate(aMesh.vertPos[vertexIndices.y]), - b.Translate(aMesh.vertPos[vertexIndices.z])}); + for (size_t face = 0; face < aImpl->NumTri(); face++) { + composedParts.push_back( + {b.Translate( + aImpl->vertPos_[aImpl->halfedge_[(face * 3) + 0].startVert]), + b.Translate( + aImpl->vertPos_[aImpl->halfedge_[(face * 3) + 1].startVert]), + b.Translate( + aImpl->vertPos_[aImpl->halfedge_[(face * 3) + 2].startVert])}); } std::vector newHulls(composedParts.size()); thrust::for_each_n( @@ -840,19 +847,23 @@ Manifold Manifold::Minkowski(const Manifold& other, bool inset) const { composedHulls.insert(composedHulls.end(), newHulls.begin(), newHulls.end()); // Non-Convex - Non-Convex Minkowski: Very Slow } else if (!aConvex && !bConvex) { - manifold::Mesh bMesh = b.GetMesh(); - for (glm::ivec3 aIndices : aMesh.triVerts) { - for (glm::ivec3 bIndices : bMesh.triVerts) { - composedHulls.push_back(Manifold::Hull( - {aMesh.vertPos[aIndices.x] + bMesh.vertPos[bIndices.x], - aMesh.vertPos[aIndices.x] + bMesh.vertPos[bIndices.y], - aMesh.vertPos[aIndices.x] + bMesh.vertPos[bIndices.z], - aMesh.vertPos[aIndices.y] + bMesh.vertPos[bIndices.x], - aMesh.vertPos[aIndices.y] + bMesh.vertPos[bIndices.y], - aMesh.vertPos[aIndices.y] + bMesh.vertPos[bIndices.z], - aMesh.vertPos[aIndices.z] + bMesh.vertPos[bIndices.x], - aMesh.vertPos[aIndices.z] + bMesh.vertPos[bIndices.y], - aMesh.vertPos[aIndices.z] + bMesh.vertPos[bIndices.z]})); + for (size_t aFace = 0; aFace < aImpl->NumTri(); aFace++) { + for (size_t bFace = 0; bFace < bImpl->NumTri(); bFace++) { + const bool coplanar = glm::all(glm::equal(aImpl->faceNormal_[aFace], + bImpl->faceNormal_[bFace])) || + glm::all(glm::equal(aImpl->faceNormal_[aFace], + -bImpl->faceNormal_[bFace])); + if (coplanar) continue; // Skip Coplanar Triangles + + auto a1 = aImpl->vertPos_[aImpl->halfedge_[(aFace * 3) + 0].startVert]; + auto a2 = aImpl->vertPos_[aImpl->halfedge_[(aFace * 3) + 1].startVert]; + auto a3 = aImpl->vertPos_[aImpl->halfedge_[(aFace * 3) + 2].startVert]; + auto b1 = bImpl->vertPos_[bImpl->halfedge_[(bFace * 3) + 0].startVert]; + auto b2 = bImpl->vertPos_[bImpl->halfedge_[(bFace * 3) + 1].startVert]; + auto b3 = bImpl->vertPos_[bImpl->halfedge_[(bFace * 3) + 2].startVert]; + composedHulls.push_back( + Manifold::Hull({a1 + b1, a1 + b2, a1 + b3, a2 + b1, a2 + b2, + a2 + b3, a3 + b1, a3 + b2, a3 + b3})); } } } From 6ca9645ccd1508d202247f1ff1ea2b7b4e4bf68b Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Wed, 27 Dec 2023 12:00:09 -0800 Subject: [PATCH 35/47] Remove Dangling Line Return --- src/manifold/src/face_op.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/manifold/src/face_op.cpp b/src/manifold/src/face_op.cpp index bca4c63e8..331287655 100644 --- a/src/manifold/src/face_op.cpp +++ b/src/manifold/src/face_op.cpp @@ -316,5 +316,4 @@ CrossSection Manifold::Impl::Project() const { return CrossSection(polys).Simplify(precision_); } - } // namespace manifold From 8599a8238fa0fd3e504cfa1b3c18eb41e92baf5c Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Wed, 3 Jan 2024 17:13:00 -0800 Subject: [PATCH 36/47] Add another early exit --- src/manifold/src/manifold.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index b23532bc7..22dff4dd7 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -818,6 +818,10 @@ Manifold Manifold::Minkowski(const Manifold& other, bool inset) const { bConvex = !bConvex; } + // Early-exit if either input is empty + if (b.IsEmpty()) return a; + if (a.IsEmpty()) return b; + // Convex-Convex Minkowski: Very Fast if (!inset && aConvex && bConvex) { std::vector simpleHull; From c530b7ac866cea39acc86324512b5507a9fb20a2 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Mon, 16 Sep 2024 14:57:36 -0700 Subject: [PATCH 37/47] Fix Glaring Bugs... --- src/manifold/src/edge_op.cpp | 12 +++++++----- src/manifold/src/manifold.cpp | 20 +++++++++++++++----- test/boolean_test.cpp | 10 +++++----- 3 files changed, 27 insertions(+), 15 deletions(-) diff --git a/src/manifold/src/edge_op.cpp b/src/manifold/src/edge_op.cpp index 780939cf3..2569d769a 100644 --- a/src/manifold/src/edge_op.cpp +++ b/src/manifold/src/edge_op.cpp @@ -703,19 +703,21 @@ bool Manifold::Impl::IsConvex(float tolerance) const { if (genus != 0) return false; // Iterate across all edges; return false if any edges are concave - bool anyConcave = false; const Impl* pImpl = this; + const size_t nbEdges = halfedge_.size(); + auto policy = autoPolicy(nbEdges, 1e5); + bool anyConcave = false; for_each_n( - countAt(0), halfedge_.size(), [&anyConcave, &pImpl, &tolerance](int idx) { + policy, countAt(0), nbEdges, [&anyConcave, &pImpl, &tolerance](int idx) { Halfedge edge = pImpl->halfedge_[idx]; if (!edge.IsForward()) return; - const glm::vec3 normal0 = pImpl->faceNormal_[edge.face]; - const glm::vec3 normal1 = + const vec3 normal0 = pImpl->faceNormal_[edge.face]; + const vec3 normal1 = pImpl->faceNormal_[pImpl->halfedge_[edge.pairedHalfedge].face]; if (glm::all(glm::equal(normal0, normal1))) return; - const glm::vec3 edgeVec = + const vec3 edgeVec = pImpl->vertPos_[edge.endVert] - pImpl->vertPos_[edge.startVert]; const bool convex = glm::dot(edgeVec, glm::cross(normal0, normal1)) > tolerance; diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index e51b8f236..efcceacd4 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -967,13 +967,13 @@ Manifold Manifold::Minkowski(const Manifold& other, bool inset) const { // Convex-Convex Minkowski: Very Fast if (!inset && aConvex && bConvex) { std::vector simpleHull; - for (glm::vec3 vertex : aImpl->vertPos_) { + for (vec3 vertex : aImpl->vertPos_) { simpleHull.push_back(b.Translate(vertex)); } composedHulls.push_back(Manifold::Hull(simpleHull)); // Convex - Non-Convex Minkowski: Slower } else if ((inset || !aConvex) && bConvex) { - std::vector> composedParts; + /*std::vector> composedParts; for (size_t face = 0; face < aImpl->NumTri(); face++) { composedParts.push_back( {b.Translate( @@ -984,12 +984,22 @@ Manifold Manifold::Minkowski(const Manifold& other, bool inset) const { aImpl->vertPos_[aImpl->halfedge_[(face * 3) + 2].startVert])}); } std::vector newHulls(composedParts.size()); - thrust::for_each_n( - thrust::host, zip(composedParts.begin(), newHulls.begin()), + auto policy = autoPolicy(composedParts.size(), 100); + for_each_n(policy, std::zip(composedParts.begin(), newHulls.begin()), composedParts.size(), [](thrust::tuple, Manifold&> inOut) { thrust::get<1>(inOut) = Manifold::Hull(thrust::get<0>(inOut)); - }); + });*/ + std::vector < Manifold > newHulls; + for (size_t face = 0; face < aImpl->NumTri(); face++) { + newHulls.push_back(Manifold::Hull( + {b.Translate( + aImpl->vertPos_[aImpl->halfedge_[(face * 3) + 0].startVert]), + b.Translate( + aImpl->vertPos_[aImpl->halfedge_[(face * 3) + 1].startVert]), + b.Translate( + aImpl->vertPos_[aImpl->halfedge_[(face * 3) + 2].startVert])})); + } composedHulls.insert(composedHulls.end(), newHulls.begin(), newHulls.end()); // Non-Convex - Non-Convex Minkowski: Very Slow } else if (!aConvex && !bConvex) { diff --git a/test/boolean_test.cpp b/test/boolean_test.cpp index 98f79c0ac..2a84ef88c 100644 --- a/test/boolean_test.cpp +++ b/test/boolean_test.cpp @@ -403,18 +403,18 @@ TEST(Boolean, NonConvexConvexMinkowski) { TEST(Boolean, NonConvexNonConvexMinkowski) { bool oldDeterministic = ManifoldParams().deterministic; ManifoldParams().deterministic = true; - PolygonParams().processOverlaps = true; + ManifoldParams().processOverlaps = true; Manifold tet = Manifold::Tetrahedron(); - Manifold nonConvex = tet - tet.Rotate(0, 0, 90).Translate(glm::vec3(1)); + Manifold nonConvex = tet - tet.Rotate(0, 0, 90).Translate(vec3(1)); - Manifold sum = nonConvex.MinkowskiSum(nonConvex.Scale(glm::vec3(0.5))); + Manifold sum = nonConvex.MinkowskiSum(nonConvex.Scale(vec3(0.5))); EXPECT_NEAR(sum.GetProperties().volume, 8.65625f, 1e-5); EXPECT_NEAR(sum.GetProperties().surfaceArea, 31.176914f, 1e-5); EXPECT_EQ(sum.Genus(), -5); Manifold difference = - nonConvex.MinkowskiDifference(nonConvex.Scale(glm::vec3(0.1))); + nonConvex.MinkowskiDifference(nonConvex.Scale(vec3(0.1))); EXPECT_NEAR(difference.GetProperties().volume, 0.81554f, 1e-5); EXPECT_NEAR(difference.GetProperties().surfaceArea, 6.95045f, 1e-5); EXPECT_EQ(difference.Genus(), 0); @@ -424,7 +424,7 @@ TEST(Boolean, NonConvexNonConvexMinkowski) { #endif ManifoldParams().deterministic = oldDeterministic; - PolygonParams().processOverlaps = false; + ManifoldParams().processOverlaps = false; } /** From 114d2c180732d6ec27244931210c1f3f04ad409b Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Mon, 16 Sep 2024 14:59:14 -0700 Subject: [PATCH 38/47] Fix Formatting --- src/manifold/src/manifold.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index efcceacd4..269fcb4b1 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -990,7 +990,7 @@ Manifold Manifold::Minkowski(const Manifold& other, bool inset) const { [](thrust::tuple, Manifold&> inOut) { thrust::get<1>(inOut) = Manifold::Hull(thrust::get<0>(inOut)); });*/ - std::vector < Manifold > newHulls; + std::vector newHulls; for (size_t face = 0; face < aImpl->NumTri(); face++) { newHulls.push_back(Manifold::Hull( {b.Translate( From adc1163b1171e7d46e3e8bdd056c2ce6850ab765 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Mon, 16 Sep 2024 15:01:08 -0700 Subject: [PATCH 39/47] Again --- src/manifold/src/manifold.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index 269fcb4b1..26692a484 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -990,7 +990,7 @@ Manifold Manifold::Minkowski(const Manifold& other, bool inset) const { [](thrust::tuple, Manifold&> inOut) { thrust::get<1>(inOut) = Manifold::Hull(thrust::get<0>(inOut)); });*/ - std::vector newHulls; + std::vector newHulls; for (size_t face = 0; face < aImpl->NumTri(); face++) { newHulls.push_back(Manifold::Hull( {b.Translate( From 922e6b4cb005e9482231e2bbda482a32600080fe Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Mon, 16 Sep 2024 15:53:30 -0700 Subject: [PATCH 40/47] Fix Mesh Export --- test/boolean_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/boolean_test.cpp b/test/boolean_test.cpp index 2a84ef88c..3892e9aee 100644 --- a/test/boolean_test.cpp +++ b/test/boolean_test.cpp @@ -420,7 +420,7 @@ TEST(Boolean, NonConvexNonConvexMinkowski) { EXPECT_EQ(difference.Genus(), 0); #ifdef MANIFOLD_EXPORT - if (options.exportModels) ExportMesh("minkowski.glb", sum.GetMesh(), {}); + if (options.exportModels) ExportMesh("minkowski.glb", sum.GetMeshGL(), {}); #endif ManifoldParams().deterministic = oldDeterministic; From 3ee19b88466e270117b40c7879078f34bc4baf58 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Mon, 16 Sep 2024 16:10:39 -0700 Subject: [PATCH 41/47] Run SimplifyTopology on the output of Minkowski --- src/manifold/src/manifold.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index 26692a484..72e4188ba 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -1025,7 +1025,8 @@ Manifold Manifold::Minkowski(const Manifold& other, bool inset) const { } return Manifold::BatchBoolean(composedHulls, inset ? manifold::OpType::Subtract - : manifold::OpType::Add); + : manifold::OpType::Add) + .AsOriginal(); } /** From 31700fc0db922000b97398b0df19f49fb94d01a9 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Mon, 16 Sep 2024 22:46:05 -0700 Subject: [PATCH 42/47] Update Tests --- src/manifold/src/manifold.cpp | 23 ++++++----------------- test/boolean_test.cpp | 22 +++++++++++++++++++--- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index 72e4188ba..883f6a22a 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -967,13 +967,13 @@ Manifold Manifold::Minkowski(const Manifold& other, bool inset) const { // Convex-Convex Minkowski: Very Fast if (!inset && aConvex && bConvex) { std::vector simpleHull; - for (vec3 vertex : aImpl->vertPos_) { + for (const vec3& vertex : aImpl->vertPos_) { simpleHull.push_back(b.Translate(vertex)); } composedHulls.push_back(Manifold::Hull(simpleHull)); // Convex - Non-Convex Minkowski: Slower } else if ((inset || !aConvex) && bConvex) { - /*std::vector> composedParts; + std::vector> composedParts; for (size_t face = 0; face < aImpl->NumTri(); face++) { composedParts.push_back( {b.Translate( @@ -985,21 +985,10 @@ Manifold Manifold::Minkowski(const Manifold& other, bool inset) const { } std::vector newHulls(composedParts.size()); auto policy = autoPolicy(composedParts.size(), 100); - for_each_n(policy, std::zip(composedParts.begin(), newHulls.begin()), - composedParts.size(), - [](thrust::tuple, Manifold&> inOut) { - thrust::get<1>(inOut) = Manifold::Hull(thrust::get<0>(inOut)); - });*/ - std::vector newHulls; - for (size_t face = 0; face < aImpl->NumTri(); face++) { - newHulls.push_back(Manifold::Hull( - {b.Translate( - aImpl->vertPos_[aImpl->halfedge_[(face * 3) + 0].startVert]), - b.Translate( - aImpl->vertPos_[aImpl->halfedge_[(face * 3) + 1].startVert]), - b.Translate( - aImpl->vertPos_[aImpl->halfedge_[(face * 3) + 2].startVert])})); - } + for_each_n(policy, countAt(0), composedParts.size(), + [&newHulls, &composedParts](const int part) { + newHulls[part] = Manifold::Hull(composedParts[part]); + }); composedHulls.insert(composedHulls.end(), newHulls.begin(), newHulls.end()); // Non-Convex - Non-Convex Minkowski: Very Slow } else if (!aConvex && !bConvex) { diff --git a/test/boolean_test.cpp b/test/boolean_test.cpp index 3892e9aee..2e2293c12 100644 --- a/test/boolean_test.cpp +++ b/test/boolean_test.cpp @@ -382,9 +382,18 @@ TEST(Boolean, ConvexConvexMinkowski) { EXPECT_NEAR(difference.GetProperties().volume, 5.8319993019104004f, 1e-5); EXPECT_NEAR(difference.GetProperties().surfaceArea, 19.439998626708984, 1e-5); EXPECT_EQ(difference.Genus(), 0); + + #ifdef MANIFOLD_EXPORT + if (options.exportModels) + ExportMesh("minkowski-convex-convex.glb", sum.GetMeshGL(), {}); +#endif } TEST(Boolean, NonConvexConvexMinkowski) { + bool oldDeterministic = ManifoldParams().deterministic; + ManifoldParams().deterministic = true; + ManifoldParams().processOverlaps = true; + Manifold sphere = Manifold::Sphere(1.2, 20); Manifold cube = Manifold::Cube({2.0, 2.0, 2.0}, true); Manifold nonConvex = cube - sphere; @@ -395,9 +404,16 @@ TEST(Boolean, NonConvexConvexMinkowski) { Manifold difference = nonConvex.MinkowskiDifference(Manifold::Sphere(0.05, 20)); EXPECT_NEAR(difference.GetProperties().volume, 0.77841246128082275f, 1e-5); - EXPECT_NEAR(difference.GetProperties().surfaceArea, 16.703733444213867f, + EXPECT_NEAR(difference.GetProperties().surfaceArea, 16.703740785913258, 1e-5); EXPECT_EQ(difference.Genus(), 5); + +#ifdef MANIFOLD_EXPORT + if (options.exportModels) ExportMesh("minkowski-nonconvex-convex.glb", sum.GetMeshGL(), {}); +#endif + + ManifoldParams().deterministic = oldDeterministic; + ManifoldParams().processOverlaps = false; } TEST(Boolean, NonConvexNonConvexMinkowski) { @@ -411,7 +427,7 @@ TEST(Boolean, NonConvexNonConvexMinkowski) { Manifold sum = nonConvex.MinkowskiSum(nonConvex.Scale(vec3(0.5))); EXPECT_NEAR(sum.GetProperties().volume, 8.65625f, 1e-5); EXPECT_NEAR(sum.GetProperties().surfaceArea, 31.176914f, 1e-5); - EXPECT_EQ(sum.Genus(), -5); + EXPECT_EQ(sum.Genus(), 0); Manifold difference = nonConvex.MinkowskiDifference(nonConvex.Scale(vec3(0.1))); @@ -420,7 +436,7 @@ TEST(Boolean, NonConvexNonConvexMinkowski) { EXPECT_EQ(difference.Genus(), 0); #ifdef MANIFOLD_EXPORT - if (options.exportModels) ExportMesh("minkowski.glb", sum.GetMeshGL(), {}); + if (options.exportModels) ExportMesh("minkowski-nonconvex-nonconvex.glb", sum.GetMeshGL(), {}); #endif ManifoldParams().deterministic = oldDeterministic; From baefc515b649c3f560d3be44934295f6b6c3aa7f Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Mon, 16 Sep 2024 22:48:42 -0700 Subject: [PATCH 43/47] Update Formatting --- test/boolean_test.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/test/boolean_test.cpp b/test/boolean_test.cpp index 2e2293c12..558fa3ac8 100644 --- a/test/boolean_test.cpp +++ b/test/boolean_test.cpp @@ -383,7 +383,7 @@ TEST(Boolean, ConvexConvexMinkowski) { EXPECT_NEAR(difference.GetProperties().surfaceArea, 19.439998626708984, 1e-5); EXPECT_EQ(difference.Genus(), 0); - #ifdef MANIFOLD_EXPORT +#ifdef MANIFOLD_EXPORT if (options.exportModels) ExportMesh("minkowski-convex-convex.glb", sum.GetMeshGL(), {}); #endif @@ -404,12 +404,12 @@ TEST(Boolean, NonConvexConvexMinkowski) { Manifold difference = nonConvex.MinkowskiDifference(Manifold::Sphere(0.05, 20)); EXPECT_NEAR(difference.GetProperties().volume, 0.77841246128082275f, 1e-5); - EXPECT_NEAR(difference.GetProperties().surfaceArea, 16.703740785913258, - 1e-5); + EXPECT_NEAR(difference.GetProperties().surfaceArea, 16.703740785913258, 1e-5); EXPECT_EQ(difference.Genus(), 5); #ifdef MANIFOLD_EXPORT - if (options.exportModels) ExportMesh("minkowski-nonconvex-convex.glb", sum.GetMeshGL(), {}); + if (options.exportModels) + ExportMesh("minkowski-nonconvex-convex.glb", sum.GetMeshGL(), {}); #endif ManifoldParams().deterministic = oldDeterministic; @@ -436,7 +436,8 @@ TEST(Boolean, NonConvexNonConvexMinkowski) { EXPECT_EQ(difference.Genus(), 0); #ifdef MANIFOLD_EXPORT - if (options.exportModels) ExportMesh("minkowski-nonconvex-nonconvex.glb", sum.GetMeshGL(), {}); + if (options.exportModels) + ExportMesh("minkowski-nonconvex-nonconvex.glb", sum.GetMeshGL(), {}); #endif ManifoldParams().deterministic = oldDeterministic; From 098d2b13203a0bf0c56dfec406ceb16f0eb51cec Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Mon, 16 Sep 2024 23:41:03 -0700 Subject: [PATCH 44/47] Make more work multithreaded --- src/manifold/src/manifold.cpp | 42 +++++++++++++++++------------------ 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index 883f6a22a..a97bde5e9 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -973,22 +973,20 @@ Manifold Manifold::Minkowski(const Manifold& other, bool inset) const { composedHulls.push_back(Manifold::Hull(simpleHull)); // Convex - Non-Convex Minkowski: Slower } else if ((inset || !aConvex) && bConvex) { - std::vector> composedParts; - for (size_t face = 0; face < aImpl->NumTri(); face++) { - composedParts.push_back( - {b.Translate( - aImpl->vertPos_[aImpl->halfedge_[(face * 3) + 0].startVert]), - b.Translate( - aImpl->vertPos_[aImpl->halfedge_[(face * 3) + 1].startVert]), - b.Translate( - aImpl->vertPos_[aImpl->halfedge_[(face * 3) + 2].startVert])}); - } - std::vector newHulls(composedParts.size()); - auto policy = autoPolicy(composedParts.size(), 100); - for_each_n(policy, countAt(0), composedParts.size(), - [&newHulls, &composedParts](const int part) { - newHulls[part] = Manifold::Hull(composedParts[part]); - }); + const size_t numTri = aImpl->NumTri(); + std::vector newHulls(numTri); + auto policy = autoPolicy(numTri, 100); + for_each_n( + policy, countAt(0), numTri, + [&newHulls, &b, &aImpl](const int face) { + newHulls[face] = Manifold::Hull( + {b.Translate( + aImpl->vertPos_[aImpl->halfedge_[(face * 3) + 0].startVert]), + b.Translate( + aImpl->vertPos_[aImpl->halfedge_[(face * 3) + 1].startVert]), + b.Translate(aImpl->vertPos_[aImpl->halfedge_[(face * 3) + 2] + .startVert])}); + }); composedHulls.insert(composedHulls.end(), newHulls.begin(), newHulls.end()); // Non-Convex - Non-Convex Minkowski: Very Slow } else if (!aConvex && !bConvex) { @@ -1000,12 +998,12 @@ Manifold Manifold::Minkowski(const Manifold& other, bool inset) const { -bImpl->faceNormal_[bFace])); if (coplanar) continue; // Skip Coplanar Triangles - auto a1 = aImpl->vertPos_[aImpl->halfedge_[(aFace * 3) + 0].startVert]; - auto a2 = aImpl->vertPos_[aImpl->halfedge_[(aFace * 3) + 1].startVert]; - auto a3 = aImpl->vertPos_[aImpl->halfedge_[(aFace * 3) + 2].startVert]; - auto b1 = bImpl->vertPos_[bImpl->halfedge_[(bFace * 3) + 0].startVert]; - auto b2 = bImpl->vertPos_[bImpl->halfedge_[(bFace * 3) + 1].startVert]; - auto b3 = bImpl->vertPos_[bImpl->halfedge_[(bFace * 3) + 2].startVert]; + vec3 a1 = aImpl->vertPos_[aImpl->halfedge_[(aFace * 3) + 0].startVert]; + vec3 a2 = aImpl->vertPos_[aImpl->halfedge_[(aFace * 3) + 1].startVert]; + vec3 a3 = aImpl->vertPos_[aImpl->halfedge_[(aFace * 3) + 2].startVert]; + vec3 b1 = bImpl->vertPos_[bImpl->halfedge_[(bFace * 3) + 0].startVert]; + vec3 b2 = bImpl->vertPos_[bImpl->halfedge_[(bFace * 3) + 1].startVert]; + vec3 b3 = bImpl->vertPos_[bImpl->halfedge_[(bFace * 3) + 2].startVert]; composedHulls.push_back( Manifold::Hull({a1 + b1, a1 + b2, a1 + b3, a2 + b1, a2 + b2, a2 + b3, a3 + b1, a3 + b2, a3 + b3})); From 2dd5b8b1b2d6de2109bd0a7b3c11dd402577cc0d Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Mon, 16 Sep 2024 23:42:58 -0700 Subject: [PATCH 45/47] Fix formatting --- src/manifold/src/manifold.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index a97bde5e9..1a0b26765 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -977,8 +977,7 @@ Manifold Manifold::Minkowski(const Manifold& other, bool inset) const { std::vector newHulls(numTri); auto policy = autoPolicy(numTri, 100); for_each_n( - policy, countAt(0), numTri, - [&newHulls, &b, &aImpl](const int face) { + policy, countAt(0), numTri, [&newHulls, &b, &aImpl](const int face) { newHulls[face] = Manifold::Hull( {b.Translate( aImpl->vertPos_[aImpl->halfedge_[(face * 3) + 0].startVert]), From d1f35e2668bea54aec87727b3e8c8bce5ac0ebe4 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Tue, 17 Sep 2024 00:24:15 -0700 Subject: [PATCH 46/47] Disable test cheating? --- test/boolean_test.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test/boolean_test.cpp b/test/boolean_test.cpp index 558fa3ac8..058eaed11 100644 --- a/test/boolean_test.cpp +++ b/test/boolean_test.cpp @@ -391,8 +391,8 @@ TEST(Boolean, ConvexConvexMinkowski) { TEST(Boolean, NonConvexConvexMinkowski) { bool oldDeterministic = ManifoldParams().deterministic; - ManifoldParams().deterministic = true; - ManifoldParams().processOverlaps = true; + //ManifoldParams().deterministic = true; + //ManifoldParams().processOverlaps = true; Manifold sphere = Manifold::Sphere(1.2, 20); Manifold cube = Manifold::Cube({2.0, 2.0, 2.0}, true); @@ -412,14 +412,14 @@ TEST(Boolean, NonConvexConvexMinkowski) { ExportMesh("minkowski-nonconvex-convex.glb", sum.GetMeshGL(), {}); #endif - ManifoldParams().deterministic = oldDeterministic; - ManifoldParams().processOverlaps = false; + //ManifoldParams().deterministic = oldDeterministic; + //ManifoldParams().processOverlaps = false; } TEST(Boolean, NonConvexNonConvexMinkowski) { bool oldDeterministic = ManifoldParams().deterministic; - ManifoldParams().deterministic = true; - ManifoldParams().processOverlaps = true; + //ManifoldParams().deterministic = true; + //ManifoldParams().processOverlaps = true; Manifold tet = Manifold::Tetrahedron(); Manifold nonConvex = tet - tet.Rotate(0, 0, 90).Translate(vec3(1)); @@ -440,8 +440,8 @@ TEST(Boolean, NonConvexNonConvexMinkowski) { ExportMesh("minkowski-nonconvex-nonconvex.glb", sum.GetMeshGL(), {}); #endif - ManifoldParams().deterministic = oldDeterministic; - ManifoldParams().processOverlaps = false; + //ManifoldParams().deterministic = oldDeterministic; + //ManifoldParams().processOverlaps = false; } /** From be0f63be03a6bd271794340bebf7d5679ff9619f Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Tue, 17 Sep 2024 00:26:41 -0700 Subject: [PATCH 47/47] Revert "Disable test cheating?" This reverts commit d1f35e2668bea54aec87727b3e8c8bce5ac0ebe4. --- test/boolean_test.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test/boolean_test.cpp b/test/boolean_test.cpp index 058eaed11..558fa3ac8 100644 --- a/test/boolean_test.cpp +++ b/test/boolean_test.cpp @@ -391,8 +391,8 @@ TEST(Boolean, ConvexConvexMinkowski) { TEST(Boolean, NonConvexConvexMinkowski) { bool oldDeterministic = ManifoldParams().deterministic; - //ManifoldParams().deterministic = true; - //ManifoldParams().processOverlaps = true; + ManifoldParams().deterministic = true; + ManifoldParams().processOverlaps = true; Manifold sphere = Manifold::Sphere(1.2, 20); Manifold cube = Manifold::Cube({2.0, 2.0, 2.0}, true); @@ -412,14 +412,14 @@ TEST(Boolean, NonConvexConvexMinkowski) { ExportMesh("minkowski-nonconvex-convex.glb", sum.GetMeshGL(), {}); #endif - //ManifoldParams().deterministic = oldDeterministic; - //ManifoldParams().processOverlaps = false; + ManifoldParams().deterministic = oldDeterministic; + ManifoldParams().processOverlaps = false; } TEST(Boolean, NonConvexNonConvexMinkowski) { bool oldDeterministic = ManifoldParams().deterministic; - //ManifoldParams().deterministic = true; - //ManifoldParams().processOverlaps = true; + ManifoldParams().deterministic = true; + ManifoldParams().processOverlaps = true; Manifold tet = Manifold::Tetrahedron(); Manifold nonConvex = tet - tet.Rotate(0, 0, 90).Translate(vec3(1)); @@ -440,8 +440,8 @@ TEST(Boolean, NonConvexNonConvexMinkowski) { ExportMesh("minkowski-nonconvex-nonconvex.glb", sum.GetMeshGL(), {}); #endif - //ManifoldParams().deterministic = oldDeterministic; - //ManifoldParams().processOverlaps = false; + ManifoldParams().deterministic = oldDeterministic; + ManifoldParams().processOverlaps = false; } /**