From 54517b7546370d779867ecf8f21cc9afe14e3b09 Mon Sep 17 00:00:00 2001 From: Emmett Lalish Date: Sat, 19 Oct 2024 10:04:08 -0700 Subject: [PATCH] Separate coplanarity from faceID (#991) * separated faceID * clean up tests * fix AsOriginal * reenable CreateFaces and keep mesh relation intact * fix compilation * updates docs --- bindings/python/manifold3d.cpp | 4 +- bindings/wasm/bindings.cpp | 2 +- bindings/wasm/bindings.js | 8 --- include/manifold/manifold.h | 13 ++-- src/impl.cpp | 109 +++++++++++++++------------------ src/impl.h | 7 ++- src/manifold.cpp | 29 +++------ src/quickhull.cpp | 1 + src/shared.h | 11 ++-- src/smoothing.cpp | 5 +- test/boolean_complex_test.cpp | 9 ++- test/boolean_test.cpp | 34 ++-------- test/manifold_test.cpp | 41 +++++++++---- test/smooth_test.cpp | 11 ++-- test/test.h | 3 +- test/test_main.cpp | 28 +-------- 16 files changed, 123 insertions(+), 192 deletions(-) diff --git a/bindings/python/manifold3d.cpp b/bindings/python/manifold3d.cpp index 1405ca0cb..c7884efeb 100644 --- a/bindings/python/manifold3d.cpp +++ b/bindings/python/manifold3d.cpp @@ -351,9 +351,7 @@ NB_MODULE(manifold3d, m) { "Get the surface area of the manifold\n This is clamped to zero for " "a given face if they are within the Precision().") .def("original_id", &Manifold::OriginalID, manifold__original_id) - .def("as_original", &Manifold::AsOriginal, - nb::arg("property_tolerance") = nb::list(), - manifold__as_original__property_tolerance) + .def("as_original", &Manifold::AsOriginal, manifold__as_original) .def("is_empty", &Manifold::IsEmpty, manifold__is_empty) .def("decompose", &Manifold::Decompose, manifold__decompose) .def("split", &Manifold::Split, nb::arg("cutter"), diff --git a/bindings/wasm/bindings.cpp b/bindings/wasm/bindings.cpp index 881ace443..c75edd20f 100644 --- a/bindings/wasm/bindings.cpp +++ b/bindings/wasm/bindings.cpp @@ -168,7 +168,7 @@ EMSCRIPTEN_BINDINGS(whatever) { .function("calculateCurvature", &Manifold::CalculateCurvature) .function("_CalculateNormals", &Manifold::CalculateNormals) .function("originalID", &Manifold::OriginalID) - .function("_AsOriginal", &Manifold::AsOriginal); + .function("asOriginal", &Manifold::AsOriginal); // Manifold Static Methods function("_Cube", &Manifold::Cube); diff --git a/bindings/wasm/bindings.js b/bindings/wasm/bindings.js index de757ae84..7d32deb4a 100644 --- a/bindings/wasm/bindings.js +++ b/bindings/wasm/bindings.js @@ -228,14 +228,6 @@ Module.setup = function() { return this._CalculateNormals(normalIdx, minSharpAngle); }; - Module.Manifold.prototype.asOriginal = function(propertyTolerance = []) { - const tol = new Module.Vector_f64(); - toVec(tol, propertyTolerance); - const result = this._AsOriginal(tol); - tol.delete(); - return result - }; - Module.Manifold.prototype.setProperties = function(numProp, func) { const oldNumProp = this.numProp(); const wasmFuncPtr = addFunction(function(newPtr, vec3Ptr, oldPtr) { diff --git a/include/manifold/manifold.h b/include/manifold/manifold.h index 0737a9ac1..ffe8bef26 100644 --- a/include/manifold/manifold.h +++ b/include/manifold/manifold.h @@ -78,13 +78,10 @@ struct MeshGLP { /// This matrix is stored in column-major order and the length of the overall /// vector is 12 * runOriginalID.size(). std::vector runTransform; - /// Optional: Length NumTri, contains an ID of the source face this triangle - /// comes from. When auto-generated, this ID will be a triangle index into the - /// original mesh. All neighboring coplanar triangles from that input mesh - /// will refer to a single triangle of that group as the faceID. When - /// supplying faceIDs, ensure that triangles with the same ID are in fact - /// coplanar and have consistent properties (within some tolerance) or the - /// output will be surprising. + /// Optional: Length NumTri, contains the source face ID this + /// triangle comes from. When auto-generated, this ID will be a triangle index + /// into the original mesh. This index/ID is purely for external use (e.g. + /// recreating polygonal faces) and will not affect Manifold's algorithms. std::vector faceID; /// Optional: The X-Y-Z-W weighted tangent vectors for smooth Refine(). If /// non-empty, must be exactly four times as long as Mesh.triVerts. Indexed @@ -245,7 +242,7 @@ class Manifold { */ ///@{ int OriginalID() const; - Manifold AsOriginal(const std::vector& propertyTolerance = {}) const; + Manifold AsOriginal() const; static uint32_t ReserveIDs(uint32_t); ///@} diff --git a/src/impl.cpp b/src/impl.cpp index 9857d92bd..12eb7bdb9 100644 --- a/src/impl.cpp +++ b/src/impl.cpp @@ -93,14 +93,11 @@ struct UpdateMeshID { struct CoplanarEdge { VecView> face2face; - VecView> vert2vert; VecView triArea; VecView halfedge; VecView vertPos; VecView triRef; VecView triProp; - VecView prop; - VecView propTol; const int numProp; const double precision; @@ -118,19 +115,9 @@ struct CoplanarEdge { const int jointNum = edge.pairedHalfedge - 3 * pairFace; if (numProp > 0) { - const int prop0 = triProp[edgeFace][baseNum]; - const int prop1 = triProp[pairFace][jointNum == 2 ? 0 : jointNum + 1]; - bool propEqual = true; - for (int p = 0; p < numProp; ++p) { - if (std::abs(prop[numProp * prop0 + p] - prop[numProp * prop1 + p]) > - propTol[p]) { - propEqual = false; - break; - } - } - if (propEqual) { - vert2vert[edgeIdx] = std::make_pair(prop0, prop1); - } + if (triProp[edgeFace][baseNum] != triProp[pairFace][Next3(jointNum)] || + triProp[edgeFace][Next3(baseNum)] != triProp[pairFace][jointNum]) + return; } if (!edge.IsForward()) return; @@ -158,32 +145,6 @@ struct CoplanarEdge { // Only operate on coplanar triangles if (volume > std::max(area, areaPair) * precision) return; - // Check property linearity - if (area > 0) { - normal /= area; - for (int i = 0; i < numProp; ++i) { - const double scale = precision / propTol[i]; - - const double baseProp = prop[numProp * triProp[edgeFace][baseNum] + i]; - const double jointProp = - prop[numProp * triProp[pairFace][jointNum] + i]; - const double edgeProp = prop[numProp * triProp[edgeFace][edgeNum] + i]; - const double pairProp = prop[numProp * triProp[pairFace][pairNum] + i]; - - const vec3 iJointVec = - jointVec + normal * scale * (jointProp - baseProp); - const vec3 iEdgeVec = edgeVec + normal * scale * (edgeProp - baseProp); - const vec3 iPairVec = pairVec + normal * scale * (pairProp - baseProp); - - vec3 cross = la::cross(iJointVec, iEdgeVec); - const double areaP = std::max( - la::length(cross), la::length(la::cross(iPairVec, iJointVec))); - const double volumeP = std::abs(la::dot(cross, iPairVec)); - // Only operate on consistent triangles - if (volumeP > areaP * precision) return; - } - } - face2face[edgeIdx] = std::make_pair(edgeFace, pairFace); } }; @@ -339,40 +300,67 @@ void Manifold::Impl::RemoveUnreferencedVerts() { }); } -void Manifold::Impl::InitializeOriginal() { +void Manifold::Impl::InitializeOriginal(bool keepFaceID) { const int meshID = ReserveIDs(1); meshRelation_.originalID = meshID; auto& triRef = meshRelation_.triRef; triRef.resize(NumTri()); for_each_n(autoPolicy(NumTri(), 1e5), countAt(0), NumTri(), - [meshID, &triRef](const int tri) { - triRef[tri] = {meshID, meshID, tri}; + [meshID, keepFaceID, &triRef](const int tri) { + triRef[tri] = {meshID, meshID, tri, + keepFaceID ? triRef[tri].faceID : tri}; }); meshRelation_.meshIDtransform.clear(); meshRelation_.meshIDtransform[meshID] = {meshID}; } -void Manifold::Impl::CreateFaces(const std::vector& propertyTolerance) { +void Manifold::Impl::CreateFaces() { ZoneScoped; - constexpr double kDefaultPropTolerance = 1e-5; - Vec propertyToleranceD = - propertyTolerance.empty() - ? Vec(meshRelation_.numProp, kDefaultPropTolerance) - : propertyTolerance; - Vec> face2face(halfedge_.size(), {-1, -1}); Vec> vert2vert(halfedge_.size(), {-1, -1}); Vec triArea(NumTri()); - for_each_n(autoPolicy(halfedge_.size(), 1e4), countAt(0), halfedge_.size(), - CoplanarEdge({face2face, vert2vert, triArea, halfedge_, vertPos_, - meshRelation_.triRef, meshRelation_.triProperties, - meshRelation_.properties, propertyToleranceD, - meshRelation_.numProp, precision_})); - if (meshRelation_.triProperties.size() > 0) { + const size_t numProp = NumProp(); + if (numProp > 0) { + for_each_n( + autoPolicy(halfedge_.size(), 1e4), countAt(0), halfedge_.size(), + [&vert2vert, numProp, this](const int edgeIdx) { + const Halfedge edge = halfedge_[edgeIdx]; + const Halfedge pair = halfedge_[edge.pairedHalfedge]; + const int edgeFace = edgeIdx / 3; + const int pairFace = edge.pairedHalfedge / 3; + + if (meshRelation_.triRef[edgeFace].meshID != + meshRelation_.triRef[pairFace].meshID) + return; + + const int baseNum = edgeIdx - 3 * edgeFace; + const int jointNum = edge.pairedHalfedge - 3 * pairFace; + + const int prop0 = meshRelation_.triProperties[edgeFace][baseNum]; + const int prop1 = + meshRelation_ + .triProperties[pairFace][jointNum == 2 ? 0 : jointNum + 1]; + bool propEqual = true; + for (size_t p = 0; p < numProp; ++p) { + if (meshRelation_.properties[numProp * prop0 + p] != + meshRelation_.properties[numProp * prop1 + p]) { + propEqual = false; + break; + } + } + if (propEqual) { + vert2vert[edgeIdx] = std::make_pair(prop0, prop1); + } + }); DedupePropVerts(meshRelation_.triProperties, vert2vert); } + for_each_n(autoPolicy(halfedge_.size(), 1e4), countAt(0), halfedge_.size(), + CoplanarEdge({face2face, triArea, halfedge_, vertPos_, + meshRelation_.triRef, meshRelation_.triProperties, + meshRelation_.numProp, precision_})); + std::vector components; const int numComponent = GetLabels(components, face2face, NumTri()); @@ -394,7 +382,7 @@ void Manifold::Impl::CreateFaces(const std::vector& propertyTolerance) { for (size_t tri = 0; tri < NumTri(); ++tri) { const int referenceTri = comp2tri[components[tri]]; if (referenceTri >= 0) { - triRef[tri].tri = referenceTri; + triRef[tri].faceID = referenceTri; } } } @@ -511,8 +499,9 @@ void Manifold::Impl::WarpBatch(std::function)> warpFunc) { faceNormal_.resize(0); // force recalculation of triNormal CalculateNormals(); SetPrecision(); - InitializeOriginal(); Finish(); + CreateFaces(); + meshRelation_.originalID = -1; } Manifold::Impl Manifold::Impl::Transform(const mat3x4& transform_) const { diff --git a/src/impl.h b/src/impl.h index 1ba94ad3e..a27d8f90f 100644 --- a/src/impl.h +++ b/src/impl.h @@ -147,6 +147,7 @@ struct Manifold::Impl { ref.meshID = meshID; ref.originalID = originalID; ref.tri = meshGL.faceID.empty() ? tri : meshGL.faceID[tri]; + ref.faceID = tri; } if (meshGL.runTransform.empty()) { @@ -209,6 +210,8 @@ struct Manifold::Impl { InitializeOriginal(); } + CreateFaces(); + SimplifyTopology(); Finish(); @@ -240,9 +243,9 @@ struct Manifold::Impl { } while (current != halfedge); } - void CreateFaces(const std::vector& propertyTolerance = {}); + void CreateFaces(); void RemoveUnreferencedVerts(); - void InitializeOriginal(); + void InitializeOriginal(bool keepFaceID = false); void CreateHalfedges(const Vec& triVerts); void CalculateNormals(); void IncrementMeshIDs(); diff --git a/src/manifold.cpp b/src/manifold.cpp index d91ed5b14..02ddf9188 100644 --- a/src/manifold.cpp +++ b/src/manifold.cpp @@ -282,13 +282,6 @@ Manifold::Manifold(const MeshGL& meshGL) * runs. * * @param meshGL The input MeshGL. - * @param propertyTolerance A vector of precision values for each property - * beyond position. If specified, the propertyTolerance vector must have size = - * numProp - 3. This is the amount of interpolation error allowed before two - * neighboring triangles are considered to be on a property boundary edge. - * Property boundary edges will be retained across operations even if the - * triangles are coplanar. Defaults to 1e-5, which works well for most - * properties in the [-1, 1] range. */ Manifold::Manifold(const MeshGL64& meshGL64) : pNode_(std::make_shared(std::make_shared(meshGL64))) {} @@ -418,21 +411,12 @@ int Manifold::OriginalID() const { } /** - * This function condenses all coplanar faces in the relation, and - * collapses those edges. In the process the relation to ancestor meshes is lost - * and this new Manifold is marked an original. Properties are preserved, so if - * they do not match across an edge, that edge will be kept. - * - * @param propertyTolerance A vector of precision values for each property - * beyond position. If specified, the propertyTolerance vector must have size = - * numProp - 3. This is the amount of interpolation error allowed before two - * neighboring triangles are considered to be on a property boundary edge. - * Property boundary edges will be retained across operations even if the - * triangles are coplanar. Defaults to 1e-5, which works well for most - * single-precision properties in the [-1, 1] range. + * This removes all relations (originalID, faceID, transform) to ancestor meshes + * and this new Manifold is marked an original. It also collapses colinear edges + * - these don't get collapsed at boundaries where originalID changes, so the + * reset may allow flat faces to be further simplified. */ -Manifold Manifold::AsOriginal( - const std::vector& propertyTolerance) const { +Manifold Manifold::AsOriginal() const { auto oldImpl = GetCsgLeafNode().GetImpl(); if (oldImpl->status_ != Error::NoError) { auto newImpl = std::make_shared(); @@ -441,9 +425,10 @@ Manifold Manifold::AsOriginal( } auto newImpl = std::make_shared(*oldImpl); newImpl->InitializeOriginal(); - newImpl->CreateFaces(propertyTolerance); + newImpl->CreateFaces(); newImpl->SimplifyTopology(); newImpl->Finish(); + newImpl->InitializeOriginal(true); return Manifold(std::make_shared(newImpl)); } diff --git a/src/quickhull.cpp b/src/quickhull.cpp index 14609ce78..2a5f27e95 100644 --- a/src/quickhull.cpp +++ b/src/quickhull.cpp @@ -851,6 +851,7 @@ void Manifold::Impl::Hull(VecView vertPos) { CalculateNormals(); InitializeOriginal(); Finish(); + CreateFaces(); } } // namespace manifold diff --git a/src/shared.h b/src/shared.h index 83759e0b7..ebf42348c 100644 --- a/src/shared.h +++ b/src/shared.h @@ -143,12 +143,14 @@ struct TriRef { /// The OriginalID of the mesh this triangle came from. This ID is ideal for /// reapplying properties like UV coordinates to the output mesh. int originalID; - /// The triangle index of the original triangle this was part of: - /// Mesh.triVerts[tri]. + /// Probably the triangle index of the original triangle this was part of: + /// Mesh.triVerts[tri], but it's an input, so just pass it along unchanged. int tri; + /// Triangles with the same face ID are coplanar. + int faceID; bool SameFace(const TriRef& other) const { - return meshID == other.meshID && tri == other.tri; + return meshID == other.meshID && faceID == other.faceID; } }; @@ -214,7 +216,8 @@ inline std::ostream& operator<<(std::ostream& stream, const Barycentric& bary) { inline std::ostream& operator<<(std::ostream& stream, const TriRef& ref) { return stream << "meshID: " << ref.meshID - << ", originalID: " << ref.originalID << ", tri: " << ref.tri; + << ", originalID: " << ref.originalID << ", tri: " << ref.tri + << ", faceID: " << ref.faceID; } #endif } // namespace manifold diff --git a/src/smoothing.cpp b/src/smoothing.cpp index c0e4aaeab..63719775d 100644 --- a/src/smoothing.cpp +++ b/src/smoothing.cpp @@ -986,13 +986,12 @@ void Manifold::Impl::Refine(std::function edgeDivisions, if (old.halfedgeTangent_.size() == old.halfedge_.size()) { for_each_n(autoPolicy(NumTri(), 1e4), countAt(0), NumVert(), InterpTri({vertPos_, vertBary, &old})); - // Make original since the subdivided faces have been warped into - // being non-coplanar, and hence not being related to the original faces. - InitializeOriginal(); } halfedgeTangent_.resize(0); Finish(); + CreateFaces(); + meshRelation_.originalID = -1; } } // namespace manifold diff --git a/test/boolean_complex_test.cpp b/test/boolean_complex_test.cpp index ea4151304..61aea86f4 100644 --- a/test/boolean_complex_test.cpp +++ b/test/boolean_complex_test.cpp @@ -28,9 +28,8 @@ using namespace manifold; */ TEST(BooleanComplex, Sphere) { - Manifold sphere = Manifold::Sphere(1.0, 12); - MeshGL sphereGL = WithPositionColors(sphere); - sphere = Manifold(sphereGL); + Manifold sphere = WithPositionColors(Manifold::Sphere(1.0, 12)); + MeshGL sphereGL = sphere.GetMeshGL(); Manifold sphere2 = sphere.Translate(vec3(0.5)); Manifold result = sphere - sphere2; @@ -52,8 +51,8 @@ TEST(BooleanComplex, Sphere) { } TEST(BooleanComplex, MeshRelation) { - MeshGL gyroidMeshGL = WithPositionColors(Gyroid()); - Manifold gyroid(gyroidMeshGL); + Manifold gyroid = WithPositionColors(Gyroid()).AsOriginal(); + MeshGL gyroidMeshGL = gyroid.GetMeshGL(); Manifold gyroid2 = gyroid.Translate(vec3(2.0)); diff --git a/test/boolean_test.cpp b/test/boolean_test.cpp index 043768c40..5321fb515 100644 --- a/test/boolean_test.cpp +++ b/test/boolean_test.cpp @@ -22,9 +22,8 @@ using namespace manifold; * The very simplest Boolean operation test. */ TEST(Boolean, Tetra) { - Manifold tetra = Manifold::Tetrahedron(); - MeshGL tetraGL = WithPositionColors(tetra); - tetra = Manifold(tetraGL); + Manifold tetra = WithPositionColors(Manifold::Tetrahedron()); + MeshGL tetraGL = tetra.GetMeshGL(); EXPECT_TRUE(!tetra.IsEmpty()); Manifold tetra2 = tetra.Translate(vec3(0.5)); @@ -242,9 +241,8 @@ TEST(Boolean, Perturb) { } TEST(Boolean, Coplanar) { - Manifold cylinder = Manifold::Cylinder(1.0, 1.0); - MeshGL cylinderGL = WithPositionColors(cylinder); - cylinder = Manifold(cylinderGL); + Manifold cylinder = WithPositionColors(Manifold::Cylinder(1.0, 1.0)); + MeshGL cylinderGL = cylinder.GetMeshGL(); Manifold cylinder2 = cylinder.Scale({0.8, 0.8, 1.0}).Rotate(0, 0, 185); Manifold out = cylinder - cylinder2; @@ -262,30 +260,6 @@ TEST(Boolean, Coplanar) { RelatedGL(out, {cylinderGL}); } -/** - * Colinear edges are not collapsed like above due to non-coplanar properties. - */ -TEST(Boolean, CoplanarProp) { - Manifold cylinder = Manifold::Cylinder(1.0, 1.0); - MeshGL cylinderGL = WithIndexColors(cylinder.GetMeshGL()); - cylinder = Manifold(cylinderGL); - - Manifold cylinder2 = cylinder.Scale({0.8, 0.8, 1.0}).Rotate(0, 0, 185); - Manifold out = cylinder - cylinder2; - ExpectMeshes(out, {{52, 104, 3, 88}}); - EXPECT_EQ(out.NumDegenerateTris(), 0); - EXPECT_EQ(out.Genus(), 1); - -#ifdef MANIFOLD_EXPORT - ExportOptions opt; - opt.mat.roughness = 1; - opt.mat.colorChannels = ivec4(3, 4, 5, -1); - if (options.exportModels) ExportMesh("coplanar.glb", out.GetMeshGL(), opt); -#endif - - RelatedGL(out, {cylinderGL}); -} - TEST(Boolean, MultiCoplanar) { Manifold cube = Manifold::Cube(); Manifold first = cube - cube.Translate({0.3, 0.3, 0.0}); diff --git a/test/manifold_test.cpp b/test/manifold_test.cpp index fbc0c922f..b73a21e1d 100644 --- a/test/manifold_test.cpp +++ b/test/manifold_test.cpp @@ -145,13 +145,13 @@ TEST(Manifold, DecomposeProps) { std::vector manifoldList; auto tet = WithPositionColors(Manifold::Tetrahedron()); manifoldList.emplace_back(tet); - input.emplace_back(tet); + input.emplace_back(tet.GetMeshGL()); auto cube = WithPositionColors(Manifold::Cube()); manifoldList.emplace_back(cube); - input.emplace_back(cube); + input.emplace_back(cube.GetMeshGL()); auto sphere = WithPositionColors(Manifold::Sphere(1, 4)); manifoldList.emplace_back(sphere); - input.emplace_back(sphere); + input.emplace_back(sphere.GetMeshGL()); Manifold manifolds = Manifold::Compose(manifoldList); ExpectMeshes(manifolds, {{8, 12, 3}, {6, 8, 3}, {4, 4, 3}}); @@ -310,17 +310,28 @@ TEST(Manifold, Warp2) { #endif TEST(Manifold, WarpBatch) { - Manifold shape1 = - Manifold::Cube({2, 3, 4}).Warp([](vec3& v) { v.x += v.z * v.z; }); + Manifold cube = Manifold::Cube({2, 3, 4}); + const int id = cube.OriginalID(); + + Manifold shape1 = cube.Warp([](vec3& v) { v.x += v.z * v.z; }); auto prop1 = shape1.GetProperties(); - Manifold shape2 = Manifold::Cube({2, 3, 4}).WarpBatch([](VecView vecs) { + Manifold shape2 = cube.WarpBatch([](VecView vecs) { for (vec3& v : vecs) { v.x += v.z * v.z; } }); auto prop2 = shape2.GetProperties(); + EXPECT_GE(id, 0); + EXPECT_EQ(shape1.OriginalID(), -1); + EXPECT_EQ(shape2.OriginalID(), -1); + std::vector runOriginalID1 = shape1.GetMeshGL().runOriginalID; + EXPECT_EQ(runOriginalID1.size(), 1); + EXPECT_EQ(runOriginalID1[0], id); + std::vector runOriginalID2 = shape2.GetMeshGL().runOriginalID; + EXPECT_EQ(runOriginalID2.size(), 1); + EXPECT_EQ(runOriginalID2[0], id); EXPECT_EQ(prop1.volume, prop2.volume); EXPECT_EQ(prop1.surfaceArea, prop2.surfaceArea); } @@ -452,7 +463,7 @@ TEST(Manifold, Slice) { #endif TEST(Manifold, MeshRelation) { - MeshGL gyroidMeshGL = WithIndexColors(Gyroid().GetMeshGL()); + MeshGL gyroidMeshGL = WithPositionColors(Gyroid()).AsOriginal().GetMeshGL(); Manifold gyroid(gyroidMeshGL); #ifdef MANIFOLD_EXPORT @@ -474,8 +485,8 @@ TEST(Manifold, MeshRelationTransform) { } TEST(Manifold, MeshRelationRefine) { - MeshGL inGL = WithIndexColors(Csaszar()); - Manifold csaszar(inGL); + Manifold csaszar = WithPositionColors(Csaszar()).AsOriginal(); + MeshGL inGL = csaszar.GetMeshGL(); RelatedGL(csaszar, {inGL}); csaszar = csaszar.RefineToLength(1); @@ -491,11 +502,15 @@ TEST(Manifold, MeshRelationRefine) { } TEST(Manifold, MeshRelationRefinePrecision) { - MeshGL inGL = WithPositionColors(Csaszar()); + MeshGL inGL = WithPositionColors(Csaszar()).GetMeshGL(); + const int id = inGL.runOriginalID[0]; Manifold csaszar = Manifold::Smooth(inGL); csaszar = csaszar.RefineToPrecision(0.05); ExpectMeshes(csaszar, {{2684, 5368, 3}}); + std::vector runOriginalID = csaszar.GetMeshGL().runOriginalID; + EXPECT_EQ(runOriginalID.size(), 1); + EXPECT_EQ(runOriginalID[0], id); #ifdef MANIFOLD_EXPORT ExportOptions opt; @@ -590,12 +605,12 @@ TEST(Manifold, FaceIDRoundTrip) { const Manifold cube = Manifold::Cube(); ASSERT_GE(cube.OriginalID(), 0); MeshGL inGL = cube.GetMeshGL(); - ASSERT_EQ(NumUnique(inGL.faceID), 6); - inGL.faceID = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; + ASSERT_EQ(NumUnique(inGL.faceID), 12); + inGL.faceID = {3, 3, 3, 3, 3, 3, 5, 5, 5, 5, 5, 5}; const Manifold cube2(inGL); const MeshGL outGL = cube2.GetMeshGL(); - ASSERT_EQ(NumUnique(outGL.faceID), 12); + ASSERT_EQ(NumUnique(outGL.faceID), 2); } TEST(Manifold, MirrorUnion) { diff --git a/test/smooth_test.cpp b/test/smooth_test.cpp index efbdfc153..90e433d7d 100644 --- a/test/smooth_test.cpp +++ b/test/smooth_test.cpp @@ -47,18 +47,17 @@ TEST(Smooth, Tetrahedron) { } TEST(Smooth, RefineQuads) { - Manifold cylinder = - Manifold(WithPositionColors(Manifold::Cylinder(2, 1, -1, 12))) - .SmoothOut() - .RefineToLength(0.05); - EXPECT_EQ(cylinder.NumTri(), 16892); + Manifold cylinder = WithPositionColors(Manifold::Cylinder(2, 1, -1, 12)) + .SmoothOut() + .RefineToLength(0.05); + EXPECT_EQ(cylinder.NumTri(), 17044); auto prop = cylinder.GetProperties(); EXPECT_NEAR(prop.volume, 2 * kPi, 0.003); EXPECT_NEAR(prop.surfaceArea, 6 * kPi, 0.004); const MeshGL out = cylinder.GetMeshGL(); CheckGL(out); - const MeshGL baseline = WithPositionColors(cylinder); + const MeshGL baseline = WithPositionColors(cylinder).GetMeshGL(); EXPECT_EQ(out.NumVert(), baseline.NumVert()); float maxDiff = 0; for (size_t i = 0; i < out.vertProperties.size(); ++i) { diff --git a/test/test.h b/test/test.h index 9c5f70ead..fc34afa6c 100644 --- a/test/test.h +++ b/test/test.h @@ -44,8 +44,7 @@ Manifold Gyroid(); MeshGL TetGL(); MeshGL CubeSTL(); MeshGL CubeUV(); -MeshGL WithIndexColors(const MeshGL& in); -MeshGL WithPositionColors(const Manifold& in); +Manifold WithPositionColors(const Manifold& in); float GetMaxProperty(const MeshGL& mesh, int channel); float GetMinProperty(const MeshGL& mesh, int channel); void CheckFinite(const MeshGL& mesh); diff --git a/test/test_main.cpp b/test/test_main.cpp index a8fc43490..13c45d013 100644 --- a/test/test_main.cpp +++ b/test/test_main.cpp @@ -125,6 +125,7 @@ MeshGL Csaszar() { 0, 5, 3, // 0, 3, 2, // 2, 4, 5}; + csaszar.runOriginalID = {Manifold::ReserveIDs(1)}; return csaszar; } @@ -198,39 +199,16 @@ MeshGL CubeSTL() { return cube; } -MeshGL WithIndexColors(const MeshGL& in) { - MeshGL out(in); - out.runIndex.clear(); - out.runTransform.clear(); - out.faceID.clear(); - out.runOriginalID = {Manifold::ReserveIDs(1)}; - const int numVert = out.NumVert(); - out.numProp = 6; - out.vertProperties.resize(6 * numVert); - for (int i = 0; i < numVert; ++i) { - for (int j : {0, 1, 2}) - out.vertProperties[6 * i + j] = in.vertProperties[3 * i + j]; - // vertex colors - double a; - out.vertProperties[6 * i + 3] = powf(modf(i * sqrt(2.0), &a), 2.2); - out.vertProperties[6 * i + 4] = powf(modf(i * sqrt(3.0), &a), 2.2); - out.vertProperties[6 * i + 5] = powf(modf(i * sqrt(5.0), &a), 2.2); - } - return out; -} - -MeshGL WithPositionColors(const Manifold& in) { +Manifold WithPositionColors(const Manifold& in) { const Box bbox = in.BoundingBox(); const vec3 size = bbox.Size(); - Manifold out = in.SetProperties( + return in.SetProperties( 3, [bbox, size](double* prop, vec3 pos, const double* oldProp) { for (int i : {0, 1, 2}) { prop[i] = (pos[i] - bbox.min[i]) / size[i]; } }); - - return out.GetMeshGL(); } MeshGL CubeUV() {