From 3be2ea997ef19eba91467c0f4399ae7f9d7801ea Mon Sep 17 00:00:00 2001 From: Emmett Lalish Date: Thu, 6 Jul 2023 09:36:49 -0700 Subject: [PATCH 1/2] added tests and fixed a bug --- src/manifold/src/boolean_result.cpp | 53 +++++++++++++++++------------ test/boolean_test.cpp | 22 ++++++++++++ test/test.h | 1 + test/test_main.cpp | 24 +++++++++++++ 4 files changed, 78 insertions(+), 22 deletions(-) diff --git a/src/manifold/src/boolean_result.cpp b/src/manifold/src/boolean_result.cpp index d39838bf2..533e0f285 100644 --- a/src/manifold/src/boolean_result.cpp +++ b/src/manifold/src/boolean_result.cpp @@ -490,32 +490,37 @@ void CreateProperties(Manifold::Impl &outR, const VecDH &refPQ, const int triPQ = refPQ[tri].tri; const bool PQ = refPQ[tri].meshID == 0; + const int oldNumProp = PQ ? numPropP : numPropQ; const auto &properties = PQ ? inP.meshRelation_.properties : inQ.meshRelation_.properties; - const auto &triProp = PQ ? inP.meshRelation_.triProperties[triPQ] - : inQ.meshRelation_.triProperties[triPQ]; + const glm::ivec3 &triProp = oldNumProp == 0 ? glm::ivec3(-1) + : PQ ? inP.meshRelation_.triProperties[triPQ] + : inQ.meshRelation_.triProperties[triPQ]; for (const int i : {0, 1, 2}) { const int vert = outR.halfedge_[3 * tri + i].startVert; const glm::vec3 uvw = bary[3 * tri + i]; - auto key = std::make_tuple(PQ, vert, -1, -1); - int edge = -1; - for (const int j : {0, 1, 2}) { - if (uvw[j] == 1) { - // On a retained vert, the propVert must also match - std::get<2>(key) = triProp[j]; - edge = -1; - break; + auto key = std::make_tuple(PQ, -1, -1, -1); + if (oldNumProp > 0) { + std::get<1>(key) = vert; + int edge = -1; + for (const int j : {0, 1, 2}) { + if (uvw[j] == 1) { + // On a retained vert, the propVert must also match + std::get<2>(key) = triProp[j]; + edge = -1; + break; + } + if (uvw[j] == 0) edge = j; + } + if (edge >= 0) { + // On an edge, both propVerts must match + const int p0 = triProp[Next3(edge)]; + const int p1 = triProp[Prev3(edge)]; + std::get<2>(key) = glm::min(p0, p1); + std::get<3>(key) = glm::max(p0, p1); } - if (uvw[j] == 0) edge = j; - } - if (edge >= 0) { - // On an edge, both propVerts must match - const int p0 = triProp[Next3(edge)]; - const int p1 = triProp[Prev3(edge)]; - std::get<2>(key) = glm::min(p0, p1); - std::get<3>(key) = glm::max(p0, p1); } const auto it = propIdx.find(key); @@ -527,10 +532,14 @@ void CreateProperties(Manifold::Impl &outR, const VecDH &refPQ, propIdx.insert({key, idx++}); for (int p = 0; p < numProp; ++p) { - glm::vec3 oldProps; - for (const int j : {0, 1, 2}) - oldProps[j] = properties[numProp * triProp[j] + p]; - outR.meshRelation_.properties.push_back(glm::dot(uvw, oldProps)); + if (p < oldNumProp) { + glm::vec3 oldProps; + for (const int j : {0, 1, 2}) + oldProps[j] = properties[oldNumProp * triProp[j] + p]; + outR.meshRelation_.properties.push_back(glm::dot(uvw, oldProps)); + } else { + outR.meshRelation_.properties.push_back(0); + } } } } diff --git a/test/boolean_test.cpp b/test/boolean_test.cpp index 4d473dd00..36e5c887d 100644 --- a/test/boolean_test.cpp +++ b/test/boolean_test.cpp @@ -530,6 +530,28 @@ TEST(Boolean, Cubes) { #endif } +TEST(Boolean, CubesUVnoIntersection) { + auto m0 = CubeUV(); + auto m1 = m0.Translate(glm::vec3(1.5)); + ASSERT_EQ((m0 + m1).GetMeshGL().numProp, 5); + ASSERT_EQ((m0 - m1).GetMeshGL().numProp, 5); +} + +TEST(Boolean, MixedProperties) { + Manifold m0 = CubeUV(); + Manifold m1 = Manifold::Cube().Translate(glm::vec3(0.5)); + ASSERT_EQ((m0 + m1).GetMeshGL().numProp, 5); +} + +TEST(Boolean, MixedNumProp) { + Manifold m0 = CubeUV(); + Manifold m1 = Manifold::Cube() + .SetProperties(1, [](float* prop, glm::vec3 p, + const float* n) { prop[0] = 1; }) + .Translate(glm::vec3(0.5)); + ASSERT_EQ((m0 + m1).GetMeshGL().numProp, 5); +} + TEST(Boolean, Subtract) { Mesh firstMesh; firstMesh.vertPos = {{0, 0, 0}, {1540, 0, 0}, diff --git a/test/test.h b/test/test.h index bae9ed825..856ababfb 100644 --- a/test/test.h +++ b/test/test.h @@ -41,6 +41,7 @@ MeshGL CubeSTL(); MeshGL WithIndexColors(const Mesh& in); MeshGL WithPositionColors(const Manifold& in); MeshGL WithNormals(const Manifold& in); +Manifold CubeUV(); float GetMaxProperty(const MeshGL& mesh, int channel); float GetMinProperty(const MeshGL& mesh, int channel); void Identical(const Mesh& mesh1, const Mesh& mesh2); diff --git a/test/test_main.cpp b/test/test_main.cpp index 6dda9cedf..af1728eec 100644 --- a/test/test_main.cpp +++ b/test/test_main.cpp @@ -249,6 +249,30 @@ MeshGL WithNormals(const Manifold& in) { return out; } +Manifold CubeUV() { + MeshGL mgl; + mgl.numProp = 5; + mgl.vertProperties = {0.5, -0.5, 0.5, 0.5, 0.66, // + -0.5, -0.5, 0.5, 0.25, 0.66, // + 0.5, 0.5, 0.5, 0.5, 0.33, // + -0.5, 0.5, 0.5, 0.25, 0.33, // + -0.5, -0.5, -0.5, 1.0, 0.66, // + 0.5, -0.5, -0.5, 0.75, 0.66, // + -0.5, 0.5, -0.5, 1.0, 0.33, // + 0.5, 0.5, -0.5, 0.75, 0.33, // + -0.5, -0.5, -0.5, 0.0, 0.66, // + -0.5, 0.5, -0.5, 0.0, 0.33, // + -0.5, 0.5, -0.5, 0.25, 0.0, // + 0.5, 0.5, -0.5, 0.5, 0.0, // + -0.5, -0.5, -0.5, 0.25, 1.0, // + 0.5, -0.5, -0.5, 0.5, 1.0}; + mgl.triVerts = {3, 1, 0, 3, 0, 2, 7, 5, 4, 7, 4, 6, 2, 0, 5, 2, 5, 7, + 9, 8, 1, 9, 1, 3, 11, 10, 3, 11, 3, 2, 0, 1, 12, 0, 12, 13}; + mgl.mergeFromVert = {8, 12, 13, 9, 10, 11}; + mgl.mergeToVert = {4, 4, 5, 6, 6, 7}; + return Manifold(mgl); +} + float GetMaxProperty(const MeshGL& mesh, int channel) { float max = -std::numeric_limits::infinity(); const int numVert = mesh.NumVert(); From 019804258d954699fde0dbb2cf8e5234f05a12e0 Mon Sep 17 00:00:00 2001 From: Emmett Lalish Date: Thu, 6 Jul 2023 11:27:54 -0700 Subject: [PATCH 2/2] more testing, fixed compose --- src/manifold/src/boolean_result.cpp | 6 +-- src/manifold/src/csg_tree.cpp | 69 ++++++++++++++++++++++++----- test/boolean_test.cpp | 44 ++++++++++++------ test/test.h | 2 +- test/test_main.cpp | 7 +-- 5 files changed, 95 insertions(+), 33 deletions(-) diff --git a/src/manifold/src/boolean_result.cpp b/src/manifold/src/boolean_result.cpp index 533e0f285..8d54ed890 100644 --- a/src/manifold/src/boolean_result.cpp +++ b/src/manifold/src/boolean_result.cpp @@ -560,12 +560,12 @@ Manifold::Impl Boolean3::Result(OpType op) const { const int c2 = op == OpType::Add ? 1 : 0; const int c3 = op == OpType::Intersect ? 1 : -1; - if (w03_.size() == 0) { - if (w30_.size() != 0 && op == OpType::Add) { + if (inP_.IsEmpty()) { + if (!inQ_.IsEmpty() && op == OpType::Add) { return inQ_; } return Manifold::Impl(); - } else if (w30_.size() == 0) { + } else if (inQ_.IsEmpty()) { if (op == OpType::Intersect) { return Manifold::Impl(); } diff --git a/src/manifold/src/csg_tree.cpp b/src/manifold/src/csg_tree.cpp index 8a0ddd2cd..8ff37d1b3 100644 --- a/src/manifold/src/csg_tree.cpp +++ b/src/manifold/src/csg_tree.cpp @@ -45,6 +45,15 @@ struct UpdateHalfedge { } }; +struct UpdateTriProp { + const int nextProp; + + __host__ __device__ glm::ivec3 operator()(glm::ivec3 tri) { + tri += nextProp; + return tri; + } +}; + struct UpdateMeshIDs { const int offset; @@ -142,9 +151,12 @@ Manifold::Impl CsgLeafNode::Compose( int numVert = 0; int numEdge = 0; int numTri = 0; + int numPropVert = 0; std::vector vertIndices; std::vector edgeIndices; std::vector triIndices; + std::vector propVertIndices; + int numPropOut = 0; for (auto &node : nodes) { float nodeOldScale = node->pImpl_->bBox_.Scale(); float nodeNewScale = @@ -158,9 +170,15 @@ Manifold::Impl CsgLeafNode::Compose( vertIndices.push_back(numVert); edgeIndices.push_back(numEdge * 2); triIndices.push_back(numTri); + propVertIndices.push_back(numPropVert); numVert += node->pImpl_->NumVert(); numEdge += node->pImpl_->NumEdge(); numTri += node->pImpl_->NumTri(); + const int numProp = node->pImpl_->NumProp(); + numPropOut = glm::max(numPropOut, numProp); + numPropVert += + numProp == 0 ? 1 + : node->pImpl_->meshRelation_.properties.size() / numProp; } Manifold::Impl combined; @@ -170,6 +188,11 @@ Manifold::Impl CsgLeafNode::Compose( combined.faceNormal_.resize(numTri); combined.halfedgeTangent_.resize(2 * numEdge); combined.meshRelation_.triRef.resize(numTri); + if (numPropOut > 0) { + combined.meshRelation_.numProp = numPropOut; + combined.meshRelation_.properties.resize(numPropOut * numPropVert, 0); + combined.meshRelation_.triProperties.resize(numTri); + } auto policy = autoPolicy(numTri); // if we are already parallelizing for each node, do not perform multithreaded @@ -182,12 +205,44 @@ Manifold::Impl CsgLeafNode::Compose( for_each_n_host( nodes.size() > 1 ? ExecutionPolicy::Par : ExecutionPolicy::Seq, countAt(0), nodes.size(), - [&nodes, &vertIndices, &edgeIndices, &triIndices, &combined, - policy](int i) { + [&nodes, &vertIndices, &edgeIndices, &triIndices, &propVertIndices, + numPropOut, &combined, policy](int i) { auto &node = nodes[i]; copy(policy, node->pImpl_->halfedgeTangent_.begin(), node->pImpl_->halfedgeTangent_.end(), combined.halfedgeTangent_.begin() + edgeIndices[i]); + transform( + policy, node->pImpl_->halfedge_.begin(), + node->pImpl_->halfedge_.end(), + combined.halfedge_.begin() + edgeIndices[i], + UpdateHalfedge({vertIndices[i], edgeIndices[i], triIndices[i]})); + + if (numPropOut > 0) { + auto start = + combined.meshRelation_.triProperties.begin() + triIndices[i]; + if (node->pImpl_->NumProp() > 0) { + auto &triProp = node->pImpl_->meshRelation_.triProperties; + transform(policy, triProp.begin(), triProp.end(), start, + UpdateTriProp({propVertIndices[i]})); + + const int numProp = node->pImpl_->NumProp(); + auto &oldProp = node->pImpl_->meshRelation_.properties; + auto &newProp = combined.meshRelation_.properties; + for (int p = 0; p < numProp; ++p) { + strided_range::IterC> oldRange( + oldProp.begin() + p, oldProp.end(), numProp); + strided_range::Iter> newRange( + newProp.begin() + numPropOut * propVertIndices[i] + p, + newProp.end(), numPropOut); + copy(policy, oldRange.begin(), oldRange.end(), newRange.begin()); + } + } else { + // point all triangles at single new property of zeros. + fill(policy, start, start + node->pImpl_->NumTri(), + glm::ivec3(propVertIndices[i])); + } + } + if (node->transform_ == glm::mat4x3(1.0f)) { copy(policy, node->pImpl_->vertPos_.begin(), node->pImpl_->vertPos_.end(), @@ -195,11 +250,6 @@ Manifold::Impl CsgLeafNode::Compose( copy(policy, node->pImpl_->faceNormal_.begin(), node->pImpl_->faceNormal_.end(), combined.faceNormal_.begin() + triIndices[i]); - transform( - policy, node->pImpl_->halfedge_.begin(), - node->pImpl_->halfedge_.end(), - combined.halfedge_.begin() + edgeIndices[i], - UpdateHalfedge({vertIndices[i], edgeIndices[i], triIndices[i]})); } else { // no need to apply the transform to the node, just copy the vertices // and face normals and apply transform on the fly @@ -215,11 +265,6 @@ Manifold::Impl CsgLeafNode::Compose( copy_n(policy, faceNormalBegin, node->pImpl_->faceNormal_.size(), combined.faceNormal_.begin() + triIndices[i]); - transform( - policy, node->pImpl_->halfedge_.begin(), - node->pImpl_->halfedge_.end(), - combined.halfedge_.begin() + edgeIndices[i], - UpdateHalfedge({vertIndices[i], edgeIndices[i], triIndices[i]})); const bool invert = glm::determinant(glm::mat3(node->transform_)) < 0; for_each_n(policy, zip(combined.halfedgeTangent_.begin() + edgeIndices[i], diff --git a/test/boolean_test.cpp b/test/boolean_test.cpp index 36e5c887d..42ad4c244 100644 --- a/test/boolean_test.cpp +++ b/test/boolean_test.cpp @@ -530,26 +530,42 @@ TEST(Boolean, Cubes) { #endif } -TEST(Boolean, CubesUVnoIntersection) { - auto m0 = CubeUV(); - auto m1 = m0.Translate(glm::vec3(1.5)); - ASSERT_EQ((m0 + m1).GetMeshGL().numProp, 5); - ASSERT_EQ((m0 - m1).GetMeshGL().numProp, 5); +TEST(Boolean, NoRetainedVerts) { + Manifold cube = Manifold::Cube(glm::vec3(1), true); + Manifold oct = Manifold::Sphere(1, 4); + EXPECT_NEAR(cube.GetProperties().volume, 1, 0.001); + EXPECT_NEAR(oct.GetProperties().volume, 1.333, 0.001); + EXPECT_NEAR((cube ^ oct).GetProperties().volume, 0.833, 0.001); +} + +TEST(Boolean, PropertiesNoIntersection) { + MeshGL cubeUV = CubeUV(); + Manifold m0(cubeUV); + Manifold m1 = m0.Translate(glm::vec3(1.5)); + Manifold result = m0 + m1; + EXPECT_EQ(result.NumProp(), 2); + RelatedGL(result, {cubeUV}); } TEST(Boolean, MixedProperties) { - Manifold m0 = CubeUV(); - Manifold m1 = Manifold::Cube().Translate(glm::vec3(0.5)); - ASSERT_EQ((m0 + m1).GetMeshGL().numProp, 5); + MeshGL cubeUV = CubeUV(); + Manifold m0(cubeUV); + Manifold m1 = Manifold::Cube(); + Manifold result = m0 + m1.Translate(glm::vec3(0.5)); + EXPECT_EQ(result.NumProp(), 2); + RelatedGL(result, {cubeUV, m1.GetMeshGL()}); } TEST(Boolean, MixedNumProp) { - Manifold m0 = CubeUV(); - Manifold m1 = Manifold::Cube() - .SetProperties(1, [](float* prop, glm::vec3 p, - const float* n) { prop[0] = 1; }) - .Translate(glm::vec3(0.5)); - ASSERT_EQ((m0 + m1).GetMeshGL().numProp, 5); + MeshGL cubeUV = CubeUV(); + Manifold m0(cubeUV); + Manifold m1 = Manifold::Cube(); + Manifold result = + m0 + m1.SetProperties(1, [](float* prop, glm::vec3 p, const float* n) { + prop[0] = 1; + }).Translate(glm::vec3(0.5)); + EXPECT_EQ(result.NumProp(), 2); + RelatedGL(result, {cubeUV, m1.GetMeshGL()}); } TEST(Boolean, Subtract) { diff --git a/test/test.h b/test/test.h index 856ababfb..44a53f348 100644 --- a/test/test.h +++ b/test/test.h @@ -41,7 +41,7 @@ MeshGL CubeSTL(); MeshGL WithIndexColors(const Mesh& in); MeshGL WithPositionColors(const Manifold& in); MeshGL WithNormals(const Manifold& in); -Manifold CubeUV(); +MeshGL CubeUV(); float GetMaxProperty(const MeshGL& mesh, int channel); float GetMinProperty(const MeshGL& mesh, int channel); void Identical(const Mesh& mesh1, const Mesh& mesh2); diff --git a/test/test_main.cpp b/test/test_main.cpp index af1728eec..58073962c 100644 --- a/test/test_main.cpp +++ b/test/test_main.cpp @@ -249,7 +249,7 @@ MeshGL WithNormals(const Manifold& in) { return out; } -Manifold CubeUV() { +MeshGL CubeUV() { MeshGL mgl; mgl.numProp = 5; mgl.vertProperties = {0.5, -0.5, 0.5, 0.5, 0.66, // @@ -270,7 +270,8 @@ Manifold CubeUV() { 9, 8, 1, 9, 1, 3, 11, 10, 3, 11, 3, 2, 0, 1, 12, 0, 12, 13}; mgl.mergeFromVert = {8, 12, 13, 9, 10, 11}; mgl.mergeToVert = {4, 4, 5, 6, 6, 7}; - return Manifold(mgl); + mgl.runOriginalID.push_back(Manifold::ReserveIDs(1)); + return mgl; } float GetMaxProperty(const MeshGL& mesh, int channel) { @@ -368,7 +369,7 @@ void RelatedGL(const Manifold& out, const std::vector& originals, ASSERT_NEAR(glm::length(normal), 1, 0.0001); ASSERT_GT(glm::dot(normal, outNormal), 0); } else { - for (int p = 3; p < output.numProp; ++p) { + for (int p = 3; p < inMesh.numProp; ++p) { const float propOut = output.vertProperties[vert * output.numProp + p];