diff --git a/bindings/c/include/manifold/manifoldc.h b/bindings/c/include/manifold/manifoldc.h index 7dd725c34..f8ab09ee4 100644 --- a/bindings/c/include/manifold/manifoldc.h +++ b/bindings/c/include/manifold/manifoldc.h @@ -167,6 +167,7 @@ ManifoldManifold *manifold_revolve(void *mem, ManifoldPolygons *cs, int circular_segments); ManifoldManifold *manifold_compose(void *mem, ManifoldManifoldVec *ms); ManifoldManifoldVec *manifold_decompose(void *mem, ManifoldManifold *m); + ManifoldManifold *manifold_as_original(void *mem, ManifoldManifold *m); // Manifold Info diff --git a/bindings/python/manifold3d.cpp b/bindings/python/manifold3d.cpp index e9000ff46..921bb899f 100644 --- a/bindings/python/manifold3d.cpp +++ b/bindings/python/manifold3d.cpp @@ -233,9 +233,8 @@ NB_MODULE(manifold3d, m) { nb::class_(m, "Manifold") .def(nb::init<>(), manifold__manifold) - .def(nb::init &>(), - nb::arg("mesh"), nb::arg("property_tolerance") = nb::list(), - manifold__manifold__mesh_gl__property_tolerance) + .def(nb::init(), nb::arg("mesh"), + manifold__manifold__mesh_gl) .def(nb::self + nb::self, manifold__operator_plus__q) .def(nb::self - nb::self, manifold__operator_minus__q) .def(nb::self ^ nb::self, manifold__operator_xor__q) @@ -348,7 +347,9 @@ 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, manifold__as_original) + .def("as_original", &Manifold::AsOriginal, + nb::arg("property_tolerance") = nb::list(), + manifold__as_original__property_tolerance) .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 07e7323f6..f27a20eba 100644 --- a/bindings/wasm/bindings.cpp +++ b/bindings/wasm/bindings.cpp @@ -167,7 +167,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 b413859fc..bdd5c6ee2 100644 --- a/bindings/wasm/bindings.js +++ b/bindings/wasm/bindings.js @@ -228,6 +228,14 @@ 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/src/manifold/include/manifold/manifold.h b/src/manifold/include/manifold/manifold.h index 0ed82cedb..15d78bb0c 100644 --- a/src/manifold/include/manifold/manifold.h +++ b/src/manifold/include/manifold/manifold.h @@ -175,9 +175,9 @@ class Manifold { Manifold(Manifold&&) noexcept; Manifold& operator=(Manifold&&) noexcept; - Manifold(const MeshGL&, const std::vector& propertyTolerance = {}); + Manifold(const MeshGL&); Manifold(const Mesh&); - Manifold(const MeshGL64&, const std::vector& propertyTolerance = {}); + Manifold(const MeshGL64&); static Manifold Smooth(const MeshGL&, const std::vector& sharpenedEdges = {}); @@ -249,7 +249,7 @@ class Manifold { */ ///@{ int OriginalID() const; - Manifold AsOriginal() const; + Manifold AsOriginal(const std::vector& propertyTolerance = {}) const; static uint32_t ReserveIDs(uint32_t); ///@} diff --git a/src/manifold/src/constructors.cpp b/src/manifold/src/constructors.cpp index 397c6701c..b4a8340c5 100644 --- a/src/manifold/src/constructors.cpp +++ b/src/manifold/src/constructors.cpp @@ -52,10 +52,7 @@ Manifold Manifold::Smooth(const MeshGL& meshGL, "when supplying tangents, the normal constructor should be used " "rather than Smooth()."); - // Don't allow any triangle merging. - std::vector propertyTolerance(meshGL.numProp - 3, -1); - std::shared_ptr impl = - std::make_shared(meshGL, propertyTolerance); + std::shared_ptr impl = std::make_shared(meshGL); impl->CreateTangents(impl->UpdateSharpenedEdges(sharpenedEdges)); return Manifold(impl); } @@ -94,10 +91,7 @@ Manifold Manifold::Smooth(const MeshGL64& meshGL64, "when supplying tangents, the normal constructor should be used " "rather than Smooth()."); - // Don't allow any triangle merging. - std::vector propertyTolerance(meshGL64.numProp - 3, -1); - std::shared_ptr impl = - std::make_shared(meshGL64, propertyTolerance); + std::shared_ptr impl = std::make_shared(meshGL64); impl->CreateTangents(impl->UpdateSharpenedEdges(sharpenedEdges)); return Manifold(impl); } @@ -278,7 +272,6 @@ Manifold Manifold::Extrude(const Polygons& crossSection, double height, pImpl_->CreateHalfedges(triVertsDH); pImpl_->Finish(); - pImpl_->meshRelation_.originalID = ReserveIDs(1); pImpl_->InitializeOriginal(); pImpl_->CreateFaces(); return Manifold(pImpl_); @@ -423,7 +416,6 @@ Manifold Manifold::Revolve(const Polygons& crossSection, int circularSegments, pImpl_->CreateHalfedges(triVertsDH); pImpl_->Finish(); - pImpl_->meshRelation_.originalID = ReserveIDs(1); pImpl_->InitializeOriginal(); pImpl_->CreateFaces(); return Manifold(pImpl_); diff --git a/src/manifold/src/impl.cpp b/src/manifold/src/impl.cpp index 590e51860..390554aec 100644 --- a/src/manifold/src/impl.cpp +++ b/src/manifold/src/impl.cpp @@ -294,9 +294,6 @@ Manifold::Impl::Impl(const Mesh& mesh, const MeshRelationD& relation, CalculateNormals(); InitializeOriginal(); - if (!hasFaceIDs) { - CreateFaces(propertyTolerance); - } SimplifyTopology(); Finish(); @@ -350,7 +347,6 @@ Manifold::Impl::Impl(Shape shape, const mat4x3 m) { for (auto& v : vertPos_) v = m * vec4(v, 1.0); CreateHalfedges(triVerts); Finish(); - meshRelation_.originalID = ReserveIDs(1); InitializeOriginal(); CreateFaces(); } @@ -392,9 +388,8 @@ void Manifold::Impl::RemoveUnreferencedVerts() { } void Manifold::Impl::InitializeOriginal() { - const int meshID = meshRelation_.originalID; - // Don't initialize if it's not an original - if (meshID < 0) return; + const int meshID = ReserveIDs(1); + meshRelation_.originalID = meshID; auto& triRef = meshRelation_.triRef; triRef.resize(NumTri()); for_each_n(autoPolicy(NumTri(), 1e5), countAt(0), NumTri(), @@ -407,9 +402,11 @@ void Manifold::Impl::InitializeOriginal() { void Manifold::Impl::CreateFaces(const std::vector& propertyTolerance) { ZoneScoped; + constexpr double kDefaultPropTolerance = 1e-5; Vec propertyToleranceD = - propertyTolerance.empty() ? Vec(meshRelation_.numProp, kTolerance) - : propertyTolerance; + propertyTolerance.empty() + ? Vec(meshRelation_.numProp, kDefaultPropTolerance) + : propertyTolerance; Vec> face2face(halfedge_.size(), {-1, -1}); Vec> vert2vert(halfedge_.size(), {-1, -1}); @@ -569,7 +566,7 @@ void Manifold::Impl::WarpBatch(std::function)> warpFunc) { faceNormal_.resize(0); // force recalculation of triNormal CalculateNormals(); SetPrecision(); - CreateFaces(); + InitializeOriginal(); Finish(); } diff --git a/src/manifold/src/impl.h b/src/manifold/src/impl.h index 8ea88b36f..7c82b1fa7 100644 --- a/src/manifold/src/impl.h +++ b/src/manifold/src/impl.h @@ -66,8 +66,7 @@ struct Manifold::Impl { Impl(Shape, const mat4x3 = mat4x3(1)); template - Impl(const MeshGLP& meshGL, - std::vector propertyTolerance) { + Impl(const MeshGLP& meshGL) { const uint32_t numVert = meshGL.NumVert(); const uint32_t numTri = meshGL.NumTri(); @@ -133,11 +132,7 @@ struct Manifold::Impl { } Vec triRef; - if (meshGL.runOriginalID.empty()) { - // FIXME: This affects Impl::InitializeOriginal, and removing this - // apparently make things fail. Not sure if this is expected. - meshRelation_.originalID = Impl::ReserveIDs(1); - } else { + if (!meshGL.runOriginalID.empty()) { std::vector runIndex = meshGL.runIndex; const uint32_t runEnd = meshGL.triVerts.size(); if (runIndex.empty()) { @@ -194,11 +189,6 @@ struct Manifold::Impl { } } - std::vector propertyToleranceD(propertyTolerance.size()); - manifold::transform(propertyTolerance.begin(), propertyTolerance.end(), - propertyToleranceD.begin(), - [](Precision v) { return (double)v; }); - CreateHalfedges(triVerts); if (!IsManifold()) { MarkFailure(Error::NotManifold); @@ -216,9 +206,8 @@ struct Manifold::Impl { CalculateNormals(); - InitializeOriginal(); - if (meshGL.faceID.empty()) { - CreateFaces(propertyToleranceD); + if (meshGL.runOriginalID.empty()) { + InitializeOriginal(); } SimplifyTopology(); diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index d225500c9..452fffcdc 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -266,18 +266,9 @@ CsgLeafNode& Manifold::GetCsgLeafNode() const { * 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 MeshGL& meshGL, - const std::vector& propertyTolerance) - : pNode_(std::make_shared( - std::make_shared(meshGL, propertyTolerance))) {} +Manifold::Manifold(const MeshGL& meshGL) + : pNode_(std::make_shared(std::make_shared(meshGL))) {} /** * Convert a MeshGL into a Manifold, retaining its properties and merging only @@ -299,10 +290,8 @@ Manifold::Manifold(const MeshGL& meshGL, * triangles are coplanar. Defaults to 1e-5, which works well for most * properties in the [-1, 1] range. */ -Manifold::Manifold(const MeshGL64& meshGL64, - const std::vector& propertyTolerance) - : pNode_(std::make_shared( - std::make_shared(meshGL64, propertyTolerance))) {} +Manifold::Manifold(const MeshGL64& meshGL64) + : pNode_(std::make_shared(std::make_shared(meshGL64))) {} /** * Convert a Mesh into a Manifold. Will return an empty Manifold @@ -312,7 +301,7 @@ Manifold::Manifold(const MeshGL64& meshGL64, * @param mesh The input Mesh. */ Manifold::Manifold(const Mesh& mesh) { - Impl::MeshRelationD relation = {(int)ReserveIDs(1)}; + Impl::MeshRelationD relation; pNode_ = std::make_shared(std::make_shared(mesh, relation)); } @@ -471,12 +460,20 @@ int Manifold::OriginalID() const { * 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. */ -Manifold Manifold::AsOriginal() const { +Manifold Manifold::AsOriginal( + const std::vector& propertyTolerance) const { auto newImpl = std::make_shared(*GetCsgLeafNode().GetImpl()); - newImpl->meshRelation_.originalID = ReserveIDs(1); newImpl->InitializeOriginal(); - newImpl->CreateFaces(); + newImpl->CreateFaces(propertyTolerance); newImpl->SimplifyTopology(); newImpl->Finish(); return Manifold(std::make_shared(newImpl)); @@ -645,13 +642,12 @@ Manifold Manifold::SetProperties( if (triProperties.size() == 0) { const int numTri = NumTri(); triProperties.resize(numTri); - int idx = 0; for (int i = 0; i < numTri; ++i) { for (const int j : {0, 1, 2}) { - triProperties[i][j] = idx++; + triProperties[i][j] = pImpl->halfedge_[3 * i + j].startVert; } } - pImpl->meshRelation_.properties = Vec(numProp * idx, 0); + pImpl->meshRelation_.properties = Vec(numProp * NumVert(), 0); } else { pImpl->meshRelation_.properties = Vec(numProp * NumPropVert(), 0); } @@ -668,8 +664,6 @@ Manifold Manifold::SetProperties( } pImpl->meshRelation_.numProp = numProp; - pImpl->CreateFaces(); - pImpl->Finish(); return Manifold(std::make_shared(pImpl)); } diff --git a/src/manifold/src/properties.cpp b/src/manifold/src/properties.cpp index 1b10d8e70..4eb31b6c9 100644 --- a/src/manifold/src/properties.cpp +++ b/src/manifold/src/properties.cpp @@ -301,9 +301,6 @@ void Manifold::Impl::CalculateCurvature(int gaussianIdx, int meanIdx) { oldProperties, halfedge_, vertMeanCurvature, vertGaussianCurvature, oldNumProp, numProp, gaussianIdx, meanIdx})); - - CreateFaces(); - Finish(); } /** diff --git a/src/manifold/src/quickhull.cpp b/src/manifold/src/quickhull.cpp index 625604e77..bf04ba624 100644 --- a/src/manifold/src/quickhull.cpp +++ b/src/manifold/src/quickhull.cpp @@ -830,14 +830,10 @@ void Manifold::Impl::Hull(VecView vertPos) { QuickHull qh(vertPos); std::tie(halfedge_, vertPos_) = qh.buildMesh(); - meshRelation_.originalID = ReserveIDs(1); CalculateBBox(); SetPrecision(bBox_.Scale() * kTolerance); - SplitPinchedVerts(); CalculateNormals(); InitializeOriginal(); - CreateFaces({}); - SimplifyTopology(); Finish(); } diff --git a/src/manifold/src/sdf.cpp b/src/manifold/src/sdf.cpp index 9f29cb617..a7a96c176 100644 --- a/src/manifold/src/sdf.cpp +++ b/src/manifold/src/sdf.cpp @@ -530,7 +530,6 @@ Manifold Manifold::LevelSet(std::function sdf, Box bounds, pImpl_->CreateHalfedges(triVerts); pImpl_->CleanupTopology(); pImpl_->Finish(); - pImpl_->meshRelation_.originalID = ReserveIDs(1); pImpl_->InitializeOriginal(); return Manifold(pImpl_); } diff --git a/src/manifold/src/smoothing.cpp b/src/manifold/src/smoothing.cpp index e4925c0a8..94527a9c9 100644 --- a/src/manifold/src/smoothing.cpp +++ b/src/manifold/src/smoothing.cpp @@ -993,9 +993,7 @@ void Manifold::Impl::Refine(std::function edgeDivisions) { 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. - meshRelation_.originalID = ReserveIDs(1); InitializeOriginal(); - CreateFaces(); } halfedgeTangent_.resize(0); diff --git a/test/boolean_complex_test.cpp b/test/boolean_complex_test.cpp index 73622c184..ed1ae9d45 100644 --- a/test/boolean_complex_test.cpp +++ b/test/boolean_complex_test.cpp @@ -71,7 +71,7 @@ TEST(BooleanComplex, MeshRelation) { #endif EXPECT_TRUE(result.MatchesTriNormals()); - EXPECT_LE(result.NumDegenerateTris(), 1); + EXPECT_LE(result.NumDegenerateTris(), 12); EXPECT_EQ(result.Decompose().size(), 1); auto prop = result.GetProperties(); EXPECT_NEAR(prop.volume, 226, 1); diff --git a/test/hull_test.cpp b/test/hull_test.cpp index f0ab57255..8676a614c 100644 --- a/test/hull_test.cpp +++ b/test/hull_test.cpp @@ -109,10 +109,10 @@ TEST(Hull, Cube) { TEST(Hull, Empty) { const std::vector tooFew{{0, 0, 0}, {1, 0, 0}, {0, 1, 0}}; - EXPECT_TRUE(Manifold::Hull(tooFew).IsEmpty()); + EXPECT_TRUE(Manifold::Hull(tooFew).AsOriginal().IsEmpty()); const std::vector coplanar{{0, 0, 0}, {1, 0, 0}, {0, 1, 0}, {1, 1, 0}}; - EXPECT_TRUE(Manifold::Hull(coplanar).IsEmpty()); + EXPECT_TRUE(Manifold::Hull(coplanar).AsOriginal().IsEmpty()); } TEST(Hull, MengerSponge) { diff --git a/test/manifold_test.cpp b/test/manifold_test.cpp index 08ead200d..203b88648 100644 --- a/test/manifold_test.cpp +++ b/test/manifold_test.cpp @@ -59,9 +59,8 @@ TEST(Manifold, Empty) { } TEST(Manifold, ValidInput) { - std::vector propTol = {0.1, 0.2}; MeshGL tetGL = TetGL(); - Manifold tet(tetGL, propTol); + Manifold tet(tetGL); EXPECT_FALSE(tet.IsEmpty()); EXPECT_EQ(tet.Status(), Manifold::Error::NoError); } @@ -521,6 +520,7 @@ TEST(Manifold, MeshGLRoundTrip) { void CheckCube(const MeshGL& cubeSTL) { Manifold cube(cubeSTL); + cube = cube.AsOriginal(); EXPECT_EQ(cube.NumTri(), 12); EXPECT_EQ(cube.NumVert(), 8); EXPECT_EQ(cube.NumPropVert(), 24); @@ -540,6 +540,7 @@ TEST(Manifold, Merge) { EXPECT_EQ(cubeBad.Status(), Manifold::Error::NotManifold); EXPECT_TRUE(cubeSTL.Merge()); + EXPECT_EQ(cubeSTL.mergeFromVert.size(), 28); CheckCube(cubeSTL); EXPECT_FALSE(cubeSTL.Merge()); diff --git a/test/smooth_test.cpp b/test/smooth_test.cpp index 086128646..efb7f48e8 100644 --- a/test/smooth_test.cpp +++ b/test/smooth_test.cpp @@ -58,6 +58,7 @@ TEST(Smooth, RefineQuads) { CheckGL(out); const MeshGL baseline = WithPositionColors(cylinder); + EXPECT_EQ(out.NumVert(), baseline.NumVert()); float maxDiff = 0; for (size_t i = 0; i < out.vertProperties.size(); ++i) { maxDiff = std::max( diff --git a/test/test_main.cpp b/test/test_main.cpp index 3cc83c822..72bb1e6f0 100644 --- a/test/test_main.cpp +++ b/test/test_main.cpp @@ -230,13 +230,7 @@ MeshGL WithPositionColors(const Manifold& in) { } }); - MeshGL outGL = out.GetMeshGL(); - outGL.runIndex.clear(); - outGL.runOriginalID.clear(); - outGL.runTransform.clear(); - outGL.faceID.clear(); - outGL.runOriginalID = {Manifold::ReserveIDs(1)}; - return outGL; + return out.GetMeshGL(); } MeshGL WithNormals(const Manifold& in) {