From 766ea58a5c5b87e2b7723aa04f9a5156799bc526 Mon Sep 17 00:00:00 2001 From: pca006132 Date: Sat, 19 Oct 2024 01:45:33 +0800 Subject: [PATCH 01/15] renaming precision to uncertainty, and hiding --- bindings/c/manifoldc.cpp | 2 +- bindings/python/manifold3d.cpp | 4 ++-- include/manifold/manifold.h | 3 ++- src/boolean_result.cpp | 4 ++-- src/constructors.cpp | 4 ++-- src/csg_tree.cpp | 4 ++-- src/edge_op.cpp | 20 ++++++++++---------- src/face_op.cpp | 4 ++-- src/impl.cpp | 20 ++++++++++---------- src/impl.h | 6 +++--- src/manifold.cpp | 16 +++++++++++----- src/properties.cpp | 6 +++--- src/quickhull.cpp | 2 +- src/shared.h | 6 +++--- src/sort.cpp | 11 +++++------ test/boolean_complex_test.cpp | 2 +- test/properties_test.cpp | 11 +++++------ test/samples_test.cpp | 4 ++-- test/sdf_test.cpp | 6 +++--- test/test_main.cpp | 10 ++++++++-- 20 files changed, 78 insertions(+), 67 deletions(-) diff --git a/bindings/c/manifoldc.cpp b/bindings/c/manifoldc.cpp index 20930cc20..f71b3b66e 100644 --- a/bindings/c/manifoldc.cpp +++ b/bindings/c/manifoldc.cpp @@ -525,7 +525,7 @@ ManifoldBox *manifold_bounding_box(void *mem, ManifoldManifold *m) { } double manifold_precision(ManifoldManifold *m) { - return from_c(m)->Precision(); + return from_c(m)->GetUncertainty(); } uint32_t manifold_reserve_ids(uint32_t n) { return Manifold::ReserveIDs(n); } diff --git a/bindings/python/manifold3d.cpp b/bindings/python/manifold3d.cpp index 1405ca0cb..628c05813 100644 --- a/bindings/python/manifold3d.cpp +++ b/bindings/python/manifold3d.cpp @@ -338,7 +338,7 @@ NB_MODULE(manifold3d, m) { .def("num_tri", &Manifold::NumTri, manifold__num_tri) .def("num_prop", &Manifold::NumProp, manifold__num_prop) .def("num_prop_vert", &Manifold::NumPropVert, manifold__num_prop_vert) - .def("precision", &Manifold::Precision, manifold__precision) + .def("precision", &Manifold::GetUncertainty, manifold__get_uncertainty) .def("genus", &Manifold::Genus, manifold__genus) .def( "volume", @@ -373,7 +373,7 @@ NB_MODULE(manifold3d, m) { .def( "project", [](const Manifold &self) { - return CrossSection(self.Project()).Simplify(self.Precision()); + return CrossSection(self.Project()).Simplify(self.GetUncertainty()); }, manifold__project) .def("status", &Manifold::Status, manifold__status) diff --git a/include/manifold/manifold.h b/include/manifold/manifold.h index 0737a9ac1..2f9bdce4f 100644 --- a/include/manifold/manifold.h +++ b/include/manifold/manifold.h @@ -233,7 +233,6 @@ class Manifold { size_t NumProp() const; size_t NumPropVert() const; Box BoundingBox() const; - double Precision() const; int Genus() const; Properties GetProperties() const; double MinGap(const Manifold& other, double searchLength) const; @@ -313,6 +312,8 @@ class Manifold { bool MatchesTriNormals() const; size_t NumDegenerateTris() const; size_t NumOverlaps(const Manifold& second) const; + double GetUncertainty() const; + Manifold SetUncertainty(double uncertainty) const; ///@} struct Impl; diff --git a/src/boolean_result.cpp b/src/boolean_result.cpp index 4e68f1b24..0af12b370 100644 --- a/src/boolean_result.cpp +++ b/src/boolean_result.cpp @@ -564,7 +564,7 @@ void CreateProperties(Manifold::Impl &outR, const Manifold::Impl &inP, for_each_n(autoPolicy(numTri, 1e4), countAt(0), numTri, Barycentric({bary, outR.meshRelation_.triRef, inP.vertPos_, inQ.vertPos_, outR.vertPos_, inP.halfedge_, - inQ.halfedge_, outR.halfedge_, outR.precision_})); + inQ.halfedge_, outR.halfedge_, outR.uncertainty_})); using Entry = std::pair; int idMissProp = outR.NumVert(); @@ -738,7 +738,7 @@ Manifold::Impl Boolean3::Result(OpType op) const { if (numVertR == 0) return outR; - outR.precision_ = std::max(inP_.precision_, inQ_.precision_); + outR.uncertainty_ = std::max(inP_.uncertainty_, inQ_.uncertainty_); outR.vertPos_.resize(numVertR); // Add vertices, duplicating for inclusion numbers not in [-1, 1]. diff --git a/src/constructors.cpp b/src/constructors.cpp index 34b89eddd..a7eeecf86 100644 --- a/src/constructors.cpp +++ b/src/constructors.cpp @@ -408,7 +408,7 @@ Manifold Manifold::Revolve(const Polygons& crossSection, int circularSegments, // Add front and back triangles if not a full revolution. if (!isFullRevolution) { std::vector frontTriangles = - Triangulate(polygons, pImpl_->precision_); + Triangulate(polygons, pImpl_->uncertainty_); for (auto& t : frontTriangles) { triVerts.push_back({startPoses[t.x], startPoses[t.y], startPoses[t.z]}); } @@ -468,7 +468,7 @@ std::vector Manifold::Decompose() const { for (int i = 0; i < numComponents; ++i) { auto impl = std::make_shared(); // inherit original object's precision - impl->precision_ = pImpl_->precision_; + impl->uncertainty_ = pImpl_->uncertainty_; Vec vertNew2Old(numVert); const int nVert = diff --git a/src/csg_tree.cpp b/src/csg_tree.cpp index 3e6cbe777..79e08a665 100644 --- a/src/csg_tree.cpp +++ b/src/csg_tree.cpp @@ -191,7 +191,7 @@ Manifold::Impl CsgLeafNode::Compose( double nodeOldScale = node->pImpl_->bBox_.Scale(); double nodeNewScale = node->pImpl_->bBox_.Transform(node->transform_).Scale(); - double nodePrecision = node->pImpl_->precision_; + double nodePrecision = node->pImpl_->uncertainty_; nodePrecision *= std::max(1.0, nodeNewScale / nodeOldScale); nodePrecision = std::max(nodePrecision, kTolerance * nodeNewScale); if (!std::isfinite(nodePrecision)) nodePrecision = -1; @@ -212,7 +212,7 @@ Manifold::Impl CsgLeafNode::Compose( } Manifold::Impl combined; - combined.precision_ = precision; + combined.uncertainty_ = precision; combined.vertPos_.resize(numVert); combined.halfedge_.resize(2 * numEdge); combined.faceNormal_.resize(numTri); diff --git a/src/edge_op.cpp b/src/edge_op.cpp index 9398deade..85f8a2bdd 100644 --- a/src/edge_op.cpp +++ b/src/edge_op.cpp @@ -206,7 +206,7 @@ void Manifold::Impl::SimplifyTopology() { { ZoneScopedN("CollapseShortEdge"); numFlagged = 0; - ShortEdge se{halfedge_, vertPos_, precision_}; + ShortEdge se{halfedge_, vertPos_, uncertainty_}; for_each_n(policy, countAt(0_uz), nbEdges, [&](size_t i) { bFlags[i] = se(i); }); for (size_t i = 0; i < nbEdges; ++i) { @@ -250,7 +250,7 @@ void Manifold::Impl::SimplifyTopology() { { ZoneScopedN("RecursiveEdgeSwap"); numFlagged = 0; - SwappableEdge se{halfedge_, vertPos_, faceNormal_, precision_}; + SwappableEdge se{halfedge_, vertPos_, faceNormal_, uncertainty_}; for_each_n(policy, countAt(0_uz), nbEdges, [&](size_t i) { bFlags[i] = se(i); }); std::vector edgeSwapStack; @@ -470,7 +470,7 @@ void Manifold::Impl::CollapseEdge(const int edge, std::vector& edges) { const vec3 pNew = vertPos_[endVert]; const vec3 pOld = vertPos_[toRemove.startVert]; const vec3 delta = pNew - pOld; - const bool shortEdge = la::dot(delta, delta) < precision_ * precision_; + const bool shortEdge = la::dot(delta, delta) < uncertainty_ * uncertainty_; // Orbit endVert int current = halfedge_[tri0edge[1]].pairedHalfedge; @@ -502,14 +502,14 @@ void Manifold::Impl::CollapseEdge(const int edge, std::vector& edges) { // Don't collapse if the edges separating the faces are not colinear // (can happen when the two faces are coplanar). if (CCW(projection * pOld, projection * pLast, projection * pNew, - precision_) != 0) + uncertainty_) != 0) return; } } // Don't collapse edge if it would cause a triangle to invert. if (CCW(projection * pNext, projection * pLast, projection * pNew, - precision_) < 0) + uncertainty_) < 0) return; pLast = pNext; @@ -582,7 +582,7 @@ void Manifold::Impl::RecursiveEdgeSwap(const int edge, int& tag, for (int i : {0, 1, 2}) v[i] = projection * vertPos_[halfedge_[tri0edge[i]].startVert]; // Only operate on the long edge of a degenerate triangle. - if (CCW(v[0], v[1], v[2], precision_) > 0 || !Is01Longest(v[0], v[1], v[2])) + if (CCW(v[0], v[1], v[2], uncertainty_) > 0 || !Is01Longest(v[0], v[1], v[2])) return; // Switch to neighbor's projection. @@ -644,12 +644,12 @@ void Manifold::Impl::RecursiveEdgeSwap(const int edge, int& tag, }; // Only operate if the other triangles are not degenerate. - if (CCW(v[1], v[0], v[3], precision_) <= 0) { + if (CCW(v[1], v[0], v[3], uncertainty_) <= 0) { if (!Is01Longest(v[1], v[0], v[3])) return; // Two facing, long-edge degenerates can swap. SwapEdge(); const vec2 e23 = v[3] - v[2]; - if (la::dot(e23, e23) < precision_ * precision_) { + if (la::dot(e23, e23) < uncertainty_ * uncertainty_) { tag++; CollapseEdge(tri0edge[2], edges); edges.resize(0); @@ -660,8 +660,8 @@ void Manifold::Impl::RecursiveEdgeSwap(const int edge, int& tag, tri0edge[1], tri0edge[0]}); } return; - } else if (CCW(v[0], v[3], v[2], precision_) <= 0 || - CCW(v[1], v[2], v[3], precision_) <= 0) { + } else if (CCW(v[0], v[3], v[2], uncertainty_) <= 0 || + CCW(v[1], v[2], v[3], uncertainty_) <= 0) { return; } // Normal path diff --git a/src/face_op.cpp b/src/face_op.cpp index eb65d278e..d78cc9961 100644 --- a/src/face_op.cpp +++ b/src/face_op.cpp @@ -79,7 +79,7 @@ void Manifold::Impl::Face2Tri(const Vec& faceEdge, auto triCCW = [&projection, this](const ivec3 tri) { return CCW(projection * this->vertPos_[tri[0]], projection * this->vertPos_[tri[1]], - projection * this->vertPos_[tri[2]], precision_) >= 0; + projection * this->vertPos_[tri[2]], uncertainty_) >= 0; }; ivec3 tri0(halfedge_[firstEdge].startVert, halfedge_[firstEdge].endVert, @@ -130,7 +130,7 @@ void Manifold::Impl::Face2Tri(const Vec& faceEdge, const PolygonsIdx polys = Face2Polygons(halfedge_.cbegin() + faceEdge[face], halfedge_.cbegin() + faceEdge[face + 1], projection); - return TriangulateIdx(polys, precision_); + return TriangulateIdx(polys, uncertainty_); }; #if (MANIFOLD_PAR == 1) && __has_include() tbb::task_group group; diff --git a/src/impl.cpp b/src/impl.cpp index 9857d92bd..8aea71a57 100644 --- a/src/impl.cpp +++ b/src/impl.cpp @@ -367,7 +367,7 @@ void Manifold::Impl::CreateFaces(const std::vector& propertyTolerance) { CoplanarEdge({face2face, vert2vert, triArea, halfedge_, vertPos_, meshRelation_.triRef, meshRelation_.triProperties, meshRelation_.properties, propertyToleranceD, - meshRelation_.numProp, precision_})); + meshRelation_.numProp, uncertainty_})); if (meshRelation_.triProperties.size() > 0) { DedupePropVerts(meshRelation_.triProperties, vert2vert); @@ -388,7 +388,7 @@ void Manifold::Impl::CreateFaces(const std::vector& propertyTolerance) { for_each_n(autoPolicy(halfedge_.size(), 1e4), countAt(0), NumTri(), CheckCoplanarity( - {comp2tri, halfedge_, vertPos_, &components, precision_})); + {comp2tri, halfedge_, vertPos_, &components, uncertainty_})); Vec& triRef = meshRelation_.triRef; for (size_t tri = 0; tri < NumTri(); ++tri) { @@ -510,7 +510,7 @@ void Manifold::Impl::WarpBatch(std::function)> warpFunc) { Update(); faceNormal_.resize(0); // force recalculation of triNormal CalculateNormals(); - SetPrecision(); + SetUncertainty(); InitializeOriginal(); Finish(); } @@ -526,7 +526,7 @@ Manifold::Impl Manifold::Impl::Transform(const mat3x4& transform_) const { } result.collider_ = collider_; result.meshRelation_ = meshRelation_; - result.precision_ = precision_; + result.uncertainty_ = uncertainty_; result.bBox_ = bBox_; result.halfedge_ = halfedge_; result.halfedgeTangent_.resize(halfedgeTangent_.size()); @@ -567,9 +567,9 @@ Manifold::Impl Manifold::Impl::Transform(const mat3x4& transform_) const { result.CalculateBBox(); // Scale the precision by the norm of the 3x3 portion of the transform. - result.precision_ *= SpectralNorm(mat3(transform_)); + result.uncertainty_ *= SpectralNorm(mat3(transform_)); // Maximum of inherited precision loss and translational precision loss. - result.SetPrecision(result.precision_); + result.SetUncertainty(result.uncertainty_); return result; } @@ -577,8 +577,8 @@ Manifold::Impl Manifold::Impl::Transform(const mat3x4& transform_) const { * Sets the precision based on the bounding box, and limits its minimum value * by the optional input. */ -void Manifold::Impl::SetPrecision(double minPrecision) { - precision_ = MaxPrecision(minPrecision, bBox_); +void Manifold::Impl::SetUncertainty(double minUncertainty) { + uncertainty_ = MaxUncertainty(minUncertainty, bBox_); } /** @@ -606,11 +606,11 @@ void Manifold::Impl::CalculateNormals() { if (calculateTriNormal) for_each_n(policy, countAt(0), NumTri(), AssignNormals({faceNormal_, vertNormal_, vertPos_, - halfedge_, precision_})); + halfedge_, uncertainty_})); else for_each_n(policy, countAt(0), NumTri(), AssignNormals({faceNormal_, vertNormal_, vertPos_, - halfedge_, precision_})); + halfedge_, uncertainty_})); for_each(policy, vertNormal_.begin(), vertNormal_.end(), [](vec3& v) { v = SafeNormalize(v); }); } diff --git a/src/impl.h b/src/impl.h index 1ba94ad3e..6de270bec 100644 --- a/src/impl.h +++ b/src/impl.h @@ -45,7 +45,7 @@ struct Manifold::Impl { }; Box bBox_; - double precision_ = -1; + double uncertainty_ = -1; Error status_ = Error::NoError; Vec vertPos_; Vec halfedge_; @@ -199,7 +199,7 @@ struct Manifold::Impl { MarkFailure(Error::NonFiniteVertex); return; } - SetPrecision(meshGL.precision); + SetUncertainty(); SplitPinchedVerts(); @@ -272,7 +272,7 @@ struct Manifold::Impl { void CalculateBBox(); bool IsFinite() const; bool IsIndexInBounds(VecView triVerts) const; - void SetPrecision(double minPrecision = -1); + void SetUncertainty(double minUncertainty = -1); bool IsManifold() const; bool Is2Manifold() const; bool MatchesTriNormals() const; diff --git a/src/manifold.cpp b/src/manifold.cpp index d91ed5b14..adfd4d528 100644 --- a/src/manifold.cpp +++ b/src/manifold.cpp @@ -70,9 +70,6 @@ MeshGLP GetMeshGLImpl(const manifold::Manifold::Impl& impl, !isOriginal && la::all(la::greater(normalIdx, ivec3(2))); MeshGLP out; - out.precision = - std::max(impl.precision_, - std::numeric_limits::epsilon() * impl.bBox_.Scale()); out.numProp = 3 + numProp; out.triVerts.resize(3 * numTri); @@ -387,8 +384,17 @@ Box Manifold::BoundingBox() const { return GetCsgLeafNode().GetImpl()->bBox_; } * considered degenerate and removed. This is the value of ε defining * [ε-valid](https://github.com/elalish/manifold/wiki/Manifold-Library#definition-of-%CE%B5-valid). */ -double Manifold::Precision() const { - return GetCsgLeafNode().GetImpl()->precision_; +double Manifold::GetUncertainty() const { + auto impl = GetCsgLeafNode().GetImpl(); + return std::max(impl->uncertainty_, std::numeric_limits::epsilon() * impl->bBox_.Scale()); +} + + +Manifold Manifold::SetUncertainty(double uncertainty) const { + auto impl = std::make_shared(*GetCsgLeafNode().GetImpl()); + impl->SetUncertainty(uncertainty); + impl->SimplifyTopology(); + return Manifold(impl); } /** diff --git a/src/properties.cpp b/src/properties.cpp index ab42c597b..81c8b54e2 100644 --- a/src/properties.cpp +++ b/src/properties.cpp @@ -229,7 +229,7 @@ bool Manifold::Impl::Is2Manifold() const { bool Manifold::Impl::MatchesTriNormals() const { if (halfedge_.size() == 0 || faceNormal_.size() != NumTri()) return true; return all_of(countAt(0_uz), countAt(NumTri()), - CheckCCW({halfedge_, vertPos_, faceNormal_, 2 * precision_})); + CheckCCW({halfedge_, vertPos_, faceNormal_, 2 * uncertainty_})); } /** @@ -239,7 +239,7 @@ int Manifold::Impl::NumDegenerateTris() const { if (halfedge_.size() == 0 || faceNormal_.size() != NumTri()) return true; return count_if( countAt(0_uz), countAt(NumTri()), - CheckCCW({halfedge_, vertPos_, faceNormal_, -1 * precision_ / 2})); + CheckCCW({halfedge_, vertPos_, faceNormal_, -1 * uncertainty_ / 2})); } Properties Manifold::Impl::GetProperties() const { @@ -252,7 +252,7 @@ Properties Manifold::Impl::GetProperties() const { double volumeCompensation = 0; for (size_t i = 0; i < NumTri(); ++i) { auto [area1, volume1] = - FaceAreaVolume({halfedge_, vertPos_, precision_})(i); + FaceAreaVolume({halfedge_, vertPos_, uncertainty_})(i); const double t1 = area + area1; const double t2 = volume + volume1; areaCompensation += (area - t1) + area1; diff --git a/src/quickhull.cpp b/src/quickhull.cpp index 14609ce78..3d0037677 100644 --- a/src/quickhull.cpp +++ b/src/quickhull.cpp @@ -847,7 +847,7 @@ void Manifold::Impl::Hull(VecView vertPos) { QuickHull qh(vertPos); std::tie(halfedge_, vertPos_) = qh.buildMesh(); CalculateBBox(); - SetPrecision(bBox_.Scale() * kTolerance); + SetUncertainty(bBox_.Scale() * kTolerance); CalculateNormals(); InitializeOriginal(); Finish(); diff --git a/src/shared.h b/src/shared.h index 83759e0b7..e77398e27 100644 --- a/src/shared.h +++ b/src/shared.h @@ -29,9 +29,9 @@ inline vec3 SafeNormalize(vec3 v) { return std::isfinite(v.x) ? v : vec3(0.0); } -inline double MaxPrecision(double minPrecision, const Box& bBox) { - double precision = std::max(minPrecision, kTolerance * bBox.Scale()); - return std::isfinite(precision) ? precision : -1; +inline double MaxUncertainty(double minUncertainty, const Box& bBox) { + double uncertainty = std::max(minUncertainty, kTolerance * bBox.Scale()); + return std::isfinite(uncertainty) ? uncertainty : -1; } inline int NextHalfedge(int current) { diff --git a/src/sort.cpp b/src/sort.cpp index ae32c147b..453b9e766 100644 --- a/src/sort.cpp +++ b/src/sort.cpp @@ -143,22 +143,21 @@ bool MergeMeshGLP(MeshGLP& mesh) { bBox.min[i] = minMax.first; bBox.max[i] = minMax.second; } - mesh.precision = MaxPrecision(mesh.precision, bBox); - if (mesh.precision < 0) return false; + auto uncertainty = MaxUncertainty(0, bBox); auto policy = autoPolicy(numOpenVert, 1e5); Vec vertBox(numOpenVert); Vec vertMorton(numOpenVert); for_each_n(policy, countAt(0), numOpenVert, - [&vertMorton, &vertBox, &openVerts, &bBox, &mesh](const int i) { + [&vertMorton, &vertBox, &openVerts, &bBox, &mesh, uncertainty](const int i) { int vert = openVerts[i]; const vec3 center(mesh.vertProperties[mesh.numProp * vert], mesh.vertProperties[mesh.numProp * vert + 1], mesh.vertProperties[mesh.numProp * vert + 2]); - vertBox[i].min = center - mesh.precision / 2.0; - vertBox[i].max = center + mesh.precision / 2.0; + vertBox[i].min = center - uncertainty / 2.0; + vertBox[i].max = center + uncertainty / 2.0; vertMorton[i] = MortonCode(center, bBox); }); @@ -212,7 +211,7 @@ void Manifold::Impl::Finish() { if (halfedge_.size() == 0) return; CalculateBBox(); - SetPrecision(precision_); + SetUncertainty(uncertainty_); if (!bBox_.IsFinite()) { // Decimated out of existence - early out. MarkFailure(Error::NoError); diff --git a/test/boolean_complex_test.cpp b/test/boolean_complex_test.cpp index 5f99e88eb..71aaa4579 100644 --- a/test/boolean_complex_test.cpp +++ b/test/boolean_complex_test.cpp @@ -236,7 +236,7 @@ TEST(BooleanComplex, Close) { Manifold result = a; for (int i = 0; i < 10; i++) { // std::cout << i << std::endl; - result ^= a.Translate({a.Precision() / 10 * i, 0.0, 0.0}); + result ^= a.Translate({a.GetUncertainty() / 10 * i, 0.0, 0.0}); } auto prop = result.GetProperties(); const double tol = 0.004; diff --git a/test/properties_test.cpp b/test/properties_test.cpp index ada1a2efe..9b1f72fed 100644 --- a/test/properties_test.cpp +++ b/test/properties_test.cpp @@ -37,17 +37,17 @@ TEST(Properties, GetProperties) { TEST(Properties, Precision) { Manifold cube = Manifold::Cube(); - EXPECT_FLOAT_EQ(cube.Precision(), kTolerance); + EXPECT_FLOAT_EQ(cube.GetUncertainty(), kTolerance); cube = cube.Scale({0.1, 1, 10}); - EXPECT_FLOAT_EQ(cube.Precision(), 10 * kTolerance); + EXPECT_FLOAT_EQ(cube.GetUncertainty(), 10 * kTolerance); cube = cube.Translate({-100, -10, -1}); - EXPECT_FLOAT_EQ(cube.Precision(), 100 * kTolerance); + EXPECT_FLOAT_EQ(cube.GetUncertainty(), 100 * kTolerance); } TEST(Properties, Precision2) { Manifold cube = Manifold::Cube(); cube = cube.Translate({-0.5, 0, 0}).Scale({2, 1, 1}); - EXPECT_FLOAT_EQ(cube.Precision(), 2 * kTolerance); + EXPECT_FLOAT_EQ(cube.GetUncertainty(), 2 * kTolerance); } TEST(Properties, Precision3) { @@ -55,9 +55,8 @@ TEST(Properties, Precision3) { const auto prop = cylinder.GetProperties(); MeshGL mesh = cylinder.GetMeshGL(); - mesh.precision = 0.001; mesh.faceID.clear(); - Manifold cylinder2(mesh); + Manifold cylinder2 = Manifold(mesh).SetUncertainty(0.001); const auto prop2 = cylinder2.GetProperties(); EXPECT_NEAR(prop.volume, prop2.volume, 0.001); diff --git a/test/samples_test.cpp b/test/samples_test.cpp index dc1dd6548..1c64ec270 100644 --- a/test/samples_test.cpp +++ b/test/samples_test.cpp @@ -221,7 +221,7 @@ TEST(Samples, GyroidModule) { CheckGL(gyroid); const Box bounds = gyroid.BoundingBox(); - const double precision = gyroid.Precision(); + const double precision = gyroid.GetUncertainty(); EXPECT_NEAR(bounds.min.z, 0, precision); EXPECT_NEAR(bounds.max.z, size * std::sqrt(2.0), precision); @@ -267,7 +267,7 @@ TEST(Samples, Sponge4) { EXPECT_EQ(cutSponge.second.Genus(), 13394); CrossSection projection(cutSponge.first.Project()); - projection = projection.Simplify(cutSponge.first.Precision()); + projection = projection.Simplify(cutSponge.first.GetUncertainty()); Rect rect = projection.Bounds(); Box box = cutSponge.first.BoundingBox(); EXPECT_EQ(rect.min.x, box.min.x); diff --git a/test/sdf_test.cpp b/test/sdf_test.cpp index f5fe4a1b7..120db1fa9 100644 --- a/test/sdf_test.cpp +++ b/test/sdf_test.cpp @@ -70,7 +70,7 @@ TEST(SDF, Bounds) { Manifold cubeVoid = Manifold::LevelSet( CubeVoid(), {vec3(-size / 2), vec3(size / 2)}, edgeLength); Box bounds = cubeVoid.BoundingBox(); - const double precision = cubeVoid.Precision(); + const double precision = cubeVoid.GetUncertainty(); #ifdef MANIFOLD_EXPORT if (options.exportModels) ExportMesh("cubeVoid.glb", cubeVoid.GetMeshGL(), {}); @@ -94,7 +94,7 @@ TEST(SDF, Bounds2) { Manifold cubeVoid = Manifold::LevelSet( CubeVoid(), {vec3(-size / 2), vec3(size / 2)}, edgeLength); Box bounds = cubeVoid.BoundingBox(); - const double precision = cubeVoid.Precision(); + const double precision = cubeVoid.GetUncertainty(); #ifdef MANIFOLD_EXPORT if (options.exportModels) ExportMesh("cubeVoid2.glb", cubeVoid.GetMeshGL(), {}); @@ -121,7 +121,7 @@ TEST(SDF, Surface) { Manifold cube = Manifold::Cube(vec3(size), true); cube -= cubeVoid; Box bounds = cube.BoundingBox(); - const double precision = cube.Precision(); + const double precision = cube.GetUncertainty(); #ifdef MANIFOLD_EXPORT if (options.exportModels) ExportMesh("cube.gltf", cube.GetMeshGL(), {}); #endif diff --git a/test/test_main.cpp b/test/test_main.cpp index a8fc43490..c6d871a2c 100644 --- a/test/test_main.cpp +++ b/test/test_main.cpp @@ -321,6 +321,12 @@ void RelatedGL(const Manifold& out, const std::vector& originals, ASSERT_FALSE(out.IsEmpty()); const ivec3 normalIdx = updateNormals ? ivec3(3, 4, 5) : ivec3(0); MeshGL output = out.GetMeshGL(normalIdx); + + float uncertainty = + std::max(static_cast(out.GetUncertainty()), + std::numeric_limits::epsilon() * + static_cast(out.BoundingBox().Scale())); + for (size_t run = 0; run < output.runOriginalID.size(); ++run) { const float* m = output.runTransform.data() + 12 * run; const mat3x4 transform = @@ -372,7 +378,7 @@ void RelatedGL(const Manifold& out, const std::vector& originals, vec3 edges[3]; for (int k : {0, 1, 2}) edges[k] = inTriPos[k] - outTriPos[j]; const double volume = la::dot(edges[0], la::cross(edges[1], edges[2])); - ASSERT_LE(volume, area * output.precision); + ASSERT_LE(volume, area * uncertainty); if (checkNormals) { vec3 normal; @@ -395,7 +401,7 @@ void RelatedGL(const Manifold& out, const std::vector& originals, const double volumeP = la::dot(edgesP[0], la::cross(edgesP[1], edgesP[2])); - ASSERT_LE(volumeP, area * output.precision); + ASSERT_LE(volumeP, area * uncertainty); } } } From 4393132dc133f80cfff025a66260835a6fd58bb8 Mon Sep 17 00:00:00 2001 From: pca006132 Date: Sat, 19 Oct 2024 02:01:54 +0800 Subject: [PATCH 02/15] add tolerance --- include/manifold/manifold.h | 11 +++++++---- src/boolean_result.cpp | 7 ++++--- src/constructors.cpp | 3 ++- src/csg_tree.cpp | 17 ++++++++++------- src/edge_op.cpp | 20 ++++++++++---------- src/face_op.cpp | 4 ++-- src/impl.cpp | 33 +++++++++++++++++---------------- src/impl.h | 1 + src/manifold.cpp | 34 ++++++++++++++++++++++++++++------ src/properties.cpp | 6 +++--- src/shared.h | 4 ++-- 11 files changed, 86 insertions(+), 54 deletions(-) diff --git a/include/manifold/manifold.h b/include/manifold/manifold.h index 2f9bdce4f..ccc47d0b9 100644 --- a/include/manifold/manifold.h +++ b/include/manifold/manifold.h @@ -91,11 +91,12 @@ struct MeshGLP { /// as 4 * (3 * tri + i) + j, i < 3, j < 4, representing the tangent value /// Mesh.triVerts[tri][i] along the CCW edge. If empty, mesh is faceted. std::vector halfedgeTangent; - /// The absolute precision of the vertex positions, based on accrued rounding - /// errors. When creating a Manifold, the precision used will be the maximum + /// Tolerance for mesh simplification. + /// When creating a Manifold, the precision used will be the maximum /// of this and a baseline precision from the size of the bounding box. Any - /// edge shorter than precision may be collapsed. - Precision precision = 0; + /// edge shorter than tolerance may be collapsed. + /// Tolerance may be enlarged when floating point error accumulates. + Precision tolerance = 0; MeshGLP() = default; @@ -236,6 +237,7 @@ class Manifold { int Genus() const; Properties GetProperties() const; double MinGap(const Manifold& other, double searchLength) const; + double GetTolerance() const; ///@} /** @name Mesh ID @@ -268,6 +270,7 @@ class Manifold { Manifold Refine(int) const; Manifold RefineToLength(double) const; Manifold RefineToPrecision(double) const; + Manifold SetTolerance(double) const; ///@} /** @name Boolean diff --git a/src/boolean_result.cpp b/src/boolean_result.cpp index 0af12b370..68db7cf09 100644 --- a/src/boolean_result.cpp +++ b/src/boolean_result.cpp @@ -526,7 +526,7 @@ struct Barycentric { VecView halfedgeP; VecView halfedgeQ; VecView halfedgeR; - const double precision; + const double tolerance; void operator()(const int tri) { const TriRef refPQ = ref[tri]; @@ -543,7 +543,7 @@ struct Barycentric { for (const int i : {0, 1, 2}) { const int vert = halfedgeR[3 * tri + i].startVert; - uvw[3 * tri + i] = GetBarycentric(vertPosR[vert], triPos, precision); + uvw[3 * tri + i] = GetBarycentric(vertPosR[vert], triPos, tolerance); } } }; @@ -564,7 +564,7 @@ void CreateProperties(Manifold::Impl &outR, const Manifold::Impl &inP, for_each_n(autoPolicy(numTri, 1e4), countAt(0), numTri, Barycentric({bary, outR.meshRelation_.triRef, inP.vertPos_, inQ.vertPos_, outR.vertPos_, inP.halfedge_, - inQ.halfedge_, outR.halfedge_, outR.uncertainty_})); + inQ.halfedge_, outR.halfedge_, outR.tolerance_})); using Entry = std::pair; int idMissProp = outR.NumVert(); @@ -739,6 +739,7 @@ Manifold::Impl Boolean3::Result(OpType op) const { if (numVertR == 0) return outR; outR.uncertainty_ = std::max(inP_.uncertainty_, inQ_.uncertainty_); + outR.tolerance_ = std::max(inP_.tolerance_, inQ_.tolerance_); outR.vertPos_.resize(numVertR); // Add vertices, duplicating for inclusion numbers not in [-1, 1]. diff --git a/src/constructors.cpp b/src/constructors.cpp index a7eeecf86..9816c18ee 100644 --- a/src/constructors.cpp +++ b/src/constructors.cpp @@ -408,7 +408,7 @@ Manifold Manifold::Revolve(const Polygons& crossSection, int circularSegments, // Add front and back triangles if not a full revolution. if (!isFullRevolution) { std::vector frontTriangles = - Triangulate(polygons, pImpl_->uncertainty_); + Triangulate(polygons, pImpl_->tolerance_); for (auto& t : frontTriangles) { triVerts.push_back({startPoses[t.x], startPoses[t.y], startPoses[t.z]}); } @@ -469,6 +469,7 @@ std::vector Manifold::Decompose() const { auto impl = std::make_shared(); // inherit original object's precision impl->uncertainty_ = pImpl_->uncertainty_; + impl->tolerance_ = pImpl_->tolerance_; Vec vertNew2Old(numVert); const int nVert = diff --git a/src/csg_tree.cpp b/src/csg_tree.cpp index 79e08a665..441472ab5 100644 --- a/src/csg_tree.cpp +++ b/src/csg_tree.cpp @@ -172,7 +172,8 @@ CsgNodeType CsgLeafNode::GetNodeType() const { return CsgNodeType::Leaf; } Manifold::Impl CsgLeafNode::Compose( const std::vector> &nodes) { ZoneScoped; - double precision = -1; + double uncertainty = -1; + double tolerance = -1; int numVert = 0; int numEdge = 0; int numTri = 0; @@ -191,11 +192,12 @@ Manifold::Impl CsgLeafNode::Compose( double nodeOldScale = node->pImpl_->bBox_.Scale(); double nodeNewScale = node->pImpl_->bBox_.Transform(node->transform_).Scale(); - double nodePrecision = node->pImpl_->uncertainty_; - nodePrecision *= std::max(1.0, nodeNewScale / nodeOldScale); - nodePrecision = std::max(nodePrecision, kTolerance * nodeNewScale); - if (!std::isfinite(nodePrecision)) nodePrecision = -1; - precision = std::max(precision, nodePrecision); + double nodeUncertainty = node->pImpl_->uncertainty_; + nodeUncertainty *= std::max(1.0, nodeNewScale / nodeOldScale); + nodeUncertainty = std::max(nodeUncertainty, kTolerance * nodeNewScale); + if (!std::isfinite(nodeUncertainty)) nodeUncertainty = -1; + uncertainty = std::max(uncertainty, nodeUncertainty); + tolerance = std::max(tolerance, node->pImpl_->tolerance_); vertIndices.push_back(numVert); edgeIndices.push_back(numEdge * 2); @@ -212,7 +214,8 @@ Manifold::Impl CsgLeafNode::Compose( } Manifold::Impl combined; - combined.uncertainty_ = precision; + combined.uncertainty_ = uncertainty; + combined.tolerance_ = tolerance; combined.vertPos_.resize(numVert); combined.halfedge_.resize(2 * numEdge); combined.faceNormal_.resize(numTri); diff --git a/src/edge_op.cpp b/src/edge_op.cpp index 85f8a2bdd..079c9d244 100644 --- a/src/edge_op.cpp +++ b/src/edge_op.cpp @@ -206,7 +206,7 @@ void Manifold::Impl::SimplifyTopology() { { ZoneScopedN("CollapseShortEdge"); numFlagged = 0; - ShortEdge se{halfedge_, vertPos_, uncertainty_}; + ShortEdge se{halfedge_, vertPos_, tolerance_}; for_each_n(policy, countAt(0_uz), nbEdges, [&](size_t i) { bFlags[i] = se(i); }); for (size_t i = 0; i < nbEdges; ++i) { @@ -250,7 +250,7 @@ void Manifold::Impl::SimplifyTopology() { { ZoneScopedN("RecursiveEdgeSwap"); numFlagged = 0; - SwappableEdge se{halfedge_, vertPos_, faceNormal_, uncertainty_}; + SwappableEdge se{halfedge_, vertPos_, faceNormal_, tolerance_}; for_each_n(policy, countAt(0_uz), nbEdges, [&](size_t i) { bFlags[i] = se(i); }); std::vector edgeSwapStack; @@ -470,7 +470,7 @@ void Manifold::Impl::CollapseEdge(const int edge, std::vector& edges) { const vec3 pNew = vertPos_[endVert]; const vec3 pOld = vertPos_[toRemove.startVert]; const vec3 delta = pNew - pOld; - const bool shortEdge = la::dot(delta, delta) < uncertainty_ * uncertainty_; + const bool shortEdge = la::dot(delta, delta) < tolerance_ * tolerance_; // Orbit endVert int current = halfedge_[tri0edge[1]].pairedHalfedge; @@ -502,14 +502,14 @@ void Manifold::Impl::CollapseEdge(const int edge, std::vector& edges) { // Don't collapse if the edges separating the faces are not colinear // (can happen when the two faces are coplanar). if (CCW(projection * pOld, projection * pLast, projection * pNew, - uncertainty_) != 0) + tolerance_) != 0) return; } } // Don't collapse edge if it would cause a triangle to invert. if (CCW(projection * pNext, projection * pLast, projection * pNew, - uncertainty_) < 0) + tolerance_) < 0) return; pLast = pNext; @@ -582,7 +582,7 @@ void Manifold::Impl::RecursiveEdgeSwap(const int edge, int& tag, for (int i : {0, 1, 2}) v[i] = projection * vertPos_[halfedge_[tri0edge[i]].startVert]; // Only operate on the long edge of a degenerate triangle. - if (CCW(v[0], v[1], v[2], uncertainty_) > 0 || !Is01Longest(v[0], v[1], v[2])) + if (CCW(v[0], v[1], v[2], tolerance_) > 0 || !Is01Longest(v[0], v[1], v[2])) return; // Switch to neighbor's projection. @@ -644,12 +644,12 @@ void Manifold::Impl::RecursiveEdgeSwap(const int edge, int& tag, }; // Only operate if the other triangles are not degenerate. - if (CCW(v[1], v[0], v[3], uncertainty_) <= 0) { + if (CCW(v[1], v[0], v[3], tolerance_) <= 0) { if (!Is01Longest(v[1], v[0], v[3])) return; // Two facing, long-edge degenerates can swap. SwapEdge(); const vec2 e23 = v[3] - v[2]; - if (la::dot(e23, e23) < uncertainty_ * uncertainty_) { + if (la::dot(e23, e23) < tolerance_ * tolerance_) { tag++; CollapseEdge(tri0edge[2], edges); edges.resize(0); @@ -660,8 +660,8 @@ void Manifold::Impl::RecursiveEdgeSwap(const int edge, int& tag, tri0edge[1], tri0edge[0]}); } return; - } else if (CCW(v[0], v[3], v[2], uncertainty_) <= 0 || - CCW(v[1], v[2], v[3], uncertainty_) <= 0) { + } else if (CCW(v[0], v[3], v[2], tolerance_) <= 0 || + CCW(v[1], v[2], v[3], tolerance_) <= 0) { return; } // Normal path diff --git a/src/face_op.cpp b/src/face_op.cpp index d78cc9961..e265c615e 100644 --- a/src/face_op.cpp +++ b/src/face_op.cpp @@ -79,7 +79,7 @@ void Manifold::Impl::Face2Tri(const Vec& faceEdge, auto triCCW = [&projection, this](const ivec3 tri) { return CCW(projection * this->vertPos_[tri[0]], projection * this->vertPos_[tri[1]], - projection * this->vertPos_[tri[2]], uncertainty_) >= 0; + projection * this->vertPos_[tri[2]], tolerance_) >= 0; }; ivec3 tri0(halfedge_[firstEdge].startVert, halfedge_[firstEdge].endVert, @@ -130,7 +130,7 @@ void Manifold::Impl::Face2Tri(const Vec& faceEdge, const PolygonsIdx polys = Face2Polygons(halfedge_.cbegin() + faceEdge[face], halfedge_.cbegin() + faceEdge[face + 1], projection); - return TriangulateIdx(polys, uncertainty_); + return TriangulateIdx(polys, tolerance_); }; #if (MANIFOLD_PAR == 1) && __has_include() tbb::task_group group; diff --git a/src/impl.cpp b/src/impl.cpp index 8aea71a57..d02ac8635 100644 --- a/src/impl.cpp +++ b/src/impl.cpp @@ -51,7 +51,6 @@ struct AssignNormals { VecView vertNormal; VecView vertPos; VecView halfedges; - const double precision; void operator()(const int face) { vec3& triNormal = faceNormal[face]; @@ -102,7 +101,7 @@ struct CoplanarEdge { VecView prop; VecView propTol; const int numProp; - const double precision; + const double tolerance; // FIXME: race condition void operator()(const int edgeIdx) { @@ -152,17 +151,17 @@ struct CoplanarEdge { triArea[edgeFace] = area; triArea[pairFace] = areaPair; // Don't link degenerate triangles - if (area < length * precision || areaPair < lengthPair * precision) return; + if (area < length * tolerance || areaPair < lengthPair * tolerance) return; const double volume = std::abs(la::dot(normal, pairVec)); // Only operate on coplanar triangles - if (volume > std::max(area, areaPair) * precision) return; + if (volume > std::max(area, areaPair) * tolerance) return; // Check property linearity if (area > 0) { normal /= area; for (int i = 0; i < numProp; ++i) { - const double scale = precision / propTol[i]; + const double scale = tolerance / propTol[i]; const double baseProp = prop[numProp * triProp[edgeFace][baseNum] + i]; const double jointProp = @@ -180,7 +179,7 @@ struct CoplanarEdge { 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; + if (volumeP > areaP * tolerance) return; } } @@ -193,7 +192,7 @@ struct CheckCoplanarity { VecView halfedge; VecView vertPos; std::vector* components; - const double precision; + const double tolerance; void operator()(int tri) { const int component = (*components)[tri]; @@ -210,7 +209,7 @@ struct CheckCoplanarity { // If any component vertex is not coplanar with the component's reference // triangle, unmark the entire component so that none of its triangles are // marked coplanar. - if (std::abs(la::dot(normal, vert - origin)) > precision) { + if (std::abs(la::dot(normal, vert - origin)) > tolerance) { reinterpret_cast*>(&comp2tri[component]) ->store(-1, std::memory_order_relaxed); break; @@ -367,7 +366,7 @@ void Manifold::Impl::CreateFaces(const std::vector& propertyTolerance) { CoplanarEdge({face2face, vert2vert, triArea, halfedge_, vertPos_, meshRelation_.triRef, meshRelation_.triProperties, meshRelation_.properties, propertyToleranceD, - meshRelation_.numProp, uncertainty_})); + meshRelation_.numProp, tolerance_})); if (meshRelation_.triProperties.size() > 0) { DedupePropVerts(meshRelation_.triProperties, vert2vert); @@ -388,7 +387,7 @@ void Manifold::Impl::CreateFaces(const std::vector& propertyTolerance) { for_each_n(autoPolicy(halfedge_.size(), 1e4), countAt(0), NumTri(), CheckCoplanarity( - {comp2tri, halfedge_, vertPos_, &components, uncertainty_})); + {comp2tri, halfedge_, vertPos_, &components, tolerance_})); Vec& triRef = meshRelation_.triRef; for (size_t tri = 0; tri < NumTri(); ++tri) { @@ -527,6 +526,7 @@ Manifold::Impl Manifold::Impl::Transform(const mat3x4& transform_) const { result.collider_ = collider_; result.meshRelation_ = meshRelation_; result.uncertainty_ = uncertainty_; + result.tolerance_ = tolerance_; result.bBox_ = bBox_; result.halfedge_ = halfedge_; result.halfedgeTangent_.resize(halfedgeTangent_.size()); @@ -579,6 +579,7 @@ Manifold::Impl Manifold::Impl::Transform(const mat3x4& transform_) const { */ void Manifold::Impl::SetUncertainty(double minUncertainty) { uncertainty_ = MaxUncertainty(minUncertainty, bBox_); + tolerance_ = std::max(tolerance_, uncertainty_); } /** @@ -604,13 +605,13 @@ void Manifold::Impl::CalculateNormals() { calculateTriNormal = true; } if (calculateTriNormal) - for_each_n(policy, countAt(0), NumTri(), - AssignNormals({faceNormal_, vertNormal_, vertPos_, - halfedge_, uncertainty_})); + for_each_n( + policy, countAt(0), NumTri(), + AssignNormals({faceNormal_, vertNormal_, vertPos_, halfedge_})); else - for_each_n(policy, countAt(0), NumTri(), - AssignNormals({faceNormal_, vertNormal_, vertPos_, - halfedge_, uncertainty_})); + for_each_n( + policy, countAt(0), NumTri(), + AssignNormals({faceNormal_, vertNormal_, vertPos_, halfedge_})); for_each(policy, vertNormal_.begin(), vertNormal_.end(), [](vec3& v) { v = SafeNormalize(v); }); } diff --git a/src/impl.h b/src/impl.h index 6de270bec..a50d2e679 100644 --- a/src/impl.h +++ b/src/impl.h @@ -46,6 +46,7 @@ struct Manifold::Impl { Box bBox_; double uncertainty_ = -1; + double tolerance_ = -1; Error status_ = Error::NoError; Vec vertPos_; Vec halfedge_; diff --git a/src/manifold.cpp b/src/manifold.cpp index adfd4d528..d524be760 100644 --- a/src/manifold.cpp +++ b/src/manifold.cpp @@ -378,18 +378,15 @@ size_t Manifold::NumPropVert() const { Box Manifold::BoundingBox() const { return GetCsgLeafNode().GetImpl()->bBox_; } /** - * Returns the precision of this Manifold's vertices, which tracks the + * Returns the uncertainty of this Manifold's vertices, which tracks the * approximate rounding error over all the transforms and operations that have - * led to this state. Any triangles that are colinear within this precision are - * considered degenerate and removed. This is the value of ε defining + * led to this state. This is the value of ε defining * [ε-valid](https://github.com/elalish/manifold/wiki/Manifold-Library#definition-of-%CE%B5-valid). */ double Manifold::GetUncertainty() const { - auto impl = GetCsgLeafNode().GetImpl(); - return std::max(impl->uncertainty_, std::numeric_limits::epsilon() * impl->bBox_.Scale()); + return GetCsgLeafNode().GetImpl()->uncertainty_; } - Manifold Manifold::SetUncertainty(double uncertainty) const { auto impl = std::make_shared(*GetCsgLeafNode().GetImpl()); impl->SetUncertainty(uncertainty); @@ -397,6 +394,31 @@ Manifold Manifold::SetUncertainty(double uncertainty) const { return Manifold(impl); } +/** + * Returns the tolerance of this Manifold's vertices. + * Edges shorter than this tolerance value will be collapsed. + */ +double Manifold::GetTolerance() const { + return GetCsgLeafNode().GetImpl()->tolerance_; +} + +/** + * Return a copy of the manifold with the set tolerance value. + * This performs mesh simplification when the tolerance value is increased. + */ +Manifold Manifold::SetTolerance(double tolerance) const { + auto impl = std::make_shared(*GetCsgLeafNode().GetImpl()); + if (tolerance > impl->tolerance_) { + impl->tolerance_ = tolerance; + impl->SimplifyTopology(); + } else { + // for reducing tolerance, we need to make sure it is still at least + // equal to uncertainty. + impl->tolerance_ = std::max(impl->uncertainty_, tolerance); + } + return Manifold(impl); +} + /** * The genus is a topological property of the manifold, representing the number * of "handles". A sphere is 0, torus 1, etc. It is only meaningful for a single diff --git a/src/properties.cpp b/src/properties.cpp index 81c8b54e2..16bb6f3a7 100644 --- a/src/properties.cpp +++ b/src/properties.cpp @@ -229,7 +229,7 @@ bool Manifold::Impl::Is2Manifold() const { bool Manifold::Impl::MatchesTriNormals() const { if (halfedge_.size() == 0 || faceNormal_.size() != NumTri()) return true; return all_of(countAt(0_uz), countAt(NumTri()), - CheckCCW({halfedge_, vertPos_, faceNormal_, 2 * uncertainty_})); + CheckCCW({halfedge_, vertPos_, faceNormal_, 2 * tolerance_})); } /** @@ -239,7 +239,7 @@ int Manifold::Impl::NumDegenerateTris() const { if (halfedge_.size() == 0 || faceNormal_.size() != NumTri()) return true; return count_if( countAt(0_uz), countAt(NumTri()), - CheckCCW({halfedge_, vertPos_, faceNormal_, -1 * uncertainty_ / 2})); + CheckCCW({halfedge_, vertPos_, faceNormal_, -1 * tolerance_ / 2})); } Properties Manifold::Impl::GetProperties() const { @@ -252,7 +252,7 @@ Properties Manifold::Impl::GetProperties() const { double volumeCompensation = 0; for (size_t i = 0; i < NumTri(); ++i) { auto [area1, volume1] = - FaceAreaVolume({halfedge_, vertPos_, uncertainty_})(i); + FaceAreaVolume({halfedge_, vertPos_, tolerance_})(i); const double t1 = area + area1; const double t2 = volume + volume1; areaCompensation += (area - t1) + area1; diff --git a/src/shared.h b/src/shared.h index e77398e27..677baab87 100644 --- a/src/shared.h +++ b/src/shared.h @@ -70,7 +70,7 @@ inline mat2x3 GetAxisAlignedProjection(vec3 normal) { } inline vec3 GetBarycentric(const vec3& v, const mat3& triPos, - double precision) { + double tolerance) { const mat3 edges(triPos[2] - triPos[1], triPos[0] - triPos[2], triPos[1] - triPos[0]); const vec3 d2(la::dot(edges[0], edges[0]), la::dot(edges[1], edges[1]), @@ -80,7 +80,7 @@ inline vec3 GetBarycentric(const vec3& v, const mat3& triPos, : 2; const vec3 crossP = la::cross(edges[0], edges[1]); const double area2 = la::dot(crossP, crossP); - const double tol2 = precision * precision; + const double tol2 = tolerance * tolerance; vec3 uvw(0.0); for (const int i : {0, 1, 2}) { From 823847f09a0a468c3489b287d53d2403d5218d25 Mon Sep 17 00:00:00 2001 From: pca006132 Date: Sun, 20 Oct 2024 13:36:59 +0800 Subject: [PATCH 03/15] rename uncertainty to epsilon --- bindings/c/manifoldc.cpp | 2 +- bindings/python/manifold3d.cpp | 4 ++-- include/manifold/manifold.h | 4 ++-- src/boolean_result.cpp | 2 +- src/constructors.cpp | 2 +- src/csg_tree.cpp | 14 +++++++------- src/impl.cpp | 14 +++++++------- src/impl.h | 6 +++--- src/manifold.cpp | 14 +++++++------- src/polygon.cpp | 24 ++++++++++++------------ src/quickhull.cpp | 2 +- src/shared.h | 6 +++--- src/sort.cpp | 10 +++++----- test/boolean_complex_test.cpp | 2 +- test/properties_test.cpp | 10 +++++----- test/samples_test.cpp | 4 ++-- test/sdf_test.cpp | 6 +++--- test/test_main.cpp | 8 ++++---- 18 files changed, 67 insertions(+), 67 deletions(-) diff --git a/bindings/c/manifoldc.cpp b/bindings/c/manifoldc.cpp index f71b3b66e..2887f5683 100644 --- a/bindings/c/manifoldc.cpp +++ b/bindings/c/manifoldc.cpp @@ -525,7 +525,7 @@ ManifoldBox *manifold_bounding_box(void *mem, ManifoldManifold *m) { } double manifold_precision(ManifoldManifold *m) { - return from_c(m)->GetUncertainty(); + return from_c(m)->GetEpsilon(); } uint32_t manifold_reserve_ids(uint32_t n) { return Manifold::ReserveIDs(n); } diff --git a/bindings/python/manifold3d.cpp b/bindings/python/manifold3d.cpp index 628c05813..a670d72bc 100644 --- a/bindings/python/manifold3d.cpp +++ b/bindings/python/manifold3d.cpp @@ -338,7 +338,7 @@ NB_MODULE(manifold3d, m) { .def("num_tri", &Manifold::NumTri, manifold__num_tri) .def("num_prop", &Manifold::NumProp, manifold__num_prop) .def("num_prop_vert", &Manifold::NumPropVert, manifold__num_prop_vert) - .def("precision", &Manifold::GetUncertainty, manifold__get_uncertainty) + .def("precision", &Manifold::GetEpsilon, manifold__get_uncertainty) .def("genus", &Manifold::Genus, manifold__genus) .def( "volume", @@ -373,7 +373,7 @@ NB_MODULE(manifold3d, m) { .def( "project", [](const Manifold &self) { - return CrossSection(self.Project()).Simplify(self.GetUncertainty()); + return CrossSection(self.Project()).Simplify(self.GetEpsilon()); }, manifold__project) .def("status", &Manifold::Status, manifold__status) diff --git a/include/manifold/manifold.h b/include/manifold/manifold.h index ccc47d0b9..0ab80b263 100644 --- a/include/manifold/manifold.h +++ b/include/manifold/manifold.h @@ -315,8 +315,8 @@ class Manifold { bool MatchesTriNormals() const; size_t NumDegenerateTris() const; size_t NumOverlaps(const Manifold& second) const; - double GetUncertainty() const; - Manifold SetUncertainty(double uncertainty) const; + double GetEpsilon() const; + Manifold SetEpsilon(double epsilon) const; ///@} struct Impl; diff --git a/src/boolean_result.cpp b/src/boolean_result.cpp index 68db7cf09..88bf04930 100644 --- a/src/boolean_result.cpp +++ b/src/boolean_result.cpp @@ -738,7 +738,7 @@ Manifold::Impl Boolean3::Result(OpType op) const { if (numVertR == 0) return outR; - outR.uncertainty_ = std::max(inP_.uncertainty_, inQ_.uncertainty_); + outR.epsilon_ = std::max(inP_.epsilon_, inQ_.epsilon_); outR.tolerance_ = std::max(inP_.tolerance_, inQ_.tolerance_); outR.vertPos_.resize(numVertR); diff --git a/src/constructors.cpp b/src/constructors.cpp index 9816c18ee..3110ee55a 100644 --- a/src/constructors.cpp +++ b/src/constructors.cpp @@ -468,7 +468,7 @@ std::vector Manifold::Decompose() const { for (int i = 0; i < numComponents; ++i) { auto impl = std::make_shared(); // inherit original object's precision - impl->uncertainty_ = pImpl_->uncertainty_; + impl->epsilon_ = pImpl_->epsilon_; impl->tolerance_ = pImpl_->tolerance_; Vec vertNew2Old(numVert); diff --git a/src/csg_tree.cpp b/src/csg_tree.cpp index 441472ab5..a01804a59 100644 --- a/src/csg_tree.cpp +++ b/src/csg_tree.cpp @@ -172,7 +172,7 @@ CsgNodeType CsgLeafNode::GetNodeType() const { return CsgNodeType::Leaf; } Manifold::Impl CsgLeafNode::Compose( const std::vector> &nodes) { ZoneScoped; - double uncertainty = -1; + double epsilon = -1; double tolerance = -1; int numVert = 0; int numEdge = 0; @@ -192,11 +192,11 @@ Manifold::Impl CsgLeafNode::Compose( double nodeOldScale = node->pImpl_->bBox_.Scale(); double nodeNewScale = node->pImpl_->bBox_.Transform(node->transform_).Scale(); - double nodeUncertainty = node->pImpl_->uncertainty_; - nodeUncertainty *= std::max(1.0, nodeNewScale / nodeOldScale); - nodeUncertainty = std::max(nodeUncertainty, kTolerance * nodeNewScale); - if (!std::isfinite(nodeUncertainty)) nodeUncertainty = -1; - uncertainty = std::max(uncertainty, nodeUncertainty); + double nodeEpsilon = node->pImpl_->epsilon_; + nodeEpsilon *= std::max(1.0, nodeNewScale / nodeOldScale); + nodeEpsilon = std::max(nodeEpsilon, kTolerance * nodeNewScale); + if (!std::isfinite(nodeEpsilon)) nodeEpsilon = -1; + epsilon = std::max(epsilon, nodeEpsilon); tolerance = std::max(tolerance, node->pImpl_->tolerance_); vertIndices.push_back(numVert); @@ -214,7 +214,7 @@ Manifold::Impl CsgLeafNode::Compose( } Manifold::Impl combined; - combined.uncertainty_ = uncertainty; + combined.epsilon_ = epsilon; combined.tolerance_ = tolerance; combined.vertPos_.resize(numVert); combined.halfedge_.resize(2 * numEdge); diff --git a/src/impl.cpp b/src/impl.cpp index d02ac8635..273df5838 100644 --- a/src/impl.cpp +++ b/src/impl.cpp @@ -509,7 +509,7 @@ void Manifold::Impl::WarpBatch(std::function)> warpFunc) { Update(); faceNormal_.resize(0); // force recalculation of triNormal CalculateNormals(); - SetUncertainty(); + SetEpsilon(); InitializeOriginal(); Finish(); } @@ -525,7 +525,7 @@ Manifold::Impl Manifold::Impl::Transform(const mat3x4& transform_) const { } result.collider_ = collider_; result.meshRelation_ = meshRelation_; - result.uncertainty_ = uncertainty_; + result.epsilon_ = epsilon_; result.tolerance_ = tolerance_; result.bBox_ = bBox_; result.halfedge_ = halfedge_; @@ -567,9 +567,9 @@ Manifold::Impl Manifold::Impl::Transform(const mat3x4& transform_) const { result.CalculateBBox(); // Scale the precision by the norm of the 3x3 portion of the transform. - result.uncertainty_ *= SpectralNorm(mat3(transform_)); + result.epsilon_ *= SpectralNorm(mat3(transform_)); // Maximum of inherited precision loss and translational precision loss. - result.SetUncertainty(result.uncertainty_); + result.SetEpsilon(result.epsilon_); return result; } @@ -577,9 +577,9 @@ Manifold::Impl Manifold::Impl::Transform(const mat3x4& transform_) const { * Sets the precision based on the bounding box, and limits its minimum value * by the optional input. */ -void Manifold::Impl::SetUncertainty(double minUncertainty) { - uncertainty_ = MaxUncertainty(minUncertainty, bBox_); - tolerance_ = std::max(tolerance_, uncertainty_); +void Manifold::Impl::SetEpsilon(double minEpsilon) { + epsilon_ = MaxEpsilon(minEpsilon, bBox_); + tolerance_ = std::max(tolerance_, epsilon_); } /** diff --git a/src/impl.h b/src/impl.h index a50d2e679..8de2a97c0 100644 --- a/src/impl.h +++ b/src/impl.h @@ -45,7 +45,7 @@ struct Manifold::Impl { }; Box bBox_; - double uncertainty_ = -1; + double epsilon_ = -1; double tolerance_ = -1; Error status_ = Error::NoError; Vec vertPos_; @@ -200,7 +200,7 @@ struct Manifold::Impl { MarkFailure(Error::NonFiniteVertex); return; } - SetUncertainty(); + SetEpsilon(); SplitPinchedVerts(); @@ -273,7 +273,7 @@ struct Manifold::Impl { void CalculateBBox(); bool IsFinite() const; bool IsIndexInBounds(VecView triVerts) const; - void SetUncertainty(double minUncertainty = -1); + void SetEpsilon(double minEpsilon = -1); bool IsManifold() const; bool Is2Manifold() const; bool MatchesTriNormals() const; diff --git a/src/manifold.cpp b/src/manifold.cpp index d524be760..c710490b3 100644 --- a/src/manifold.cpp +++ b/src/manifold.cpp @@ -378,18 +378,18 @@ size_t Manifold::NumPropVert() const { Box Manifold::BoundingBox() const { return GetCsgLeafNode().GetImpl()->bBox_; } /** - * Returns the uncertainty of this Manifold's vertices, which tracks the + * Returns the epsilon value of this Manifold's vertices, which tracks the * approximate rounding error over all the transforms and operations that have * led to this state. This is the value of ε defining * [ε-valid](https://github.com/elalish/manifold/wiki/Manifold-Library#definition-of-%CE%B5-valid). */ -double Manifold::GetUncertainty() const { - return GetCsgLeafNode().GetImpl()->uncertainty_; +double Manifold::GetEpsilon() const { + return GetCsgLeafNode().GetImpl()->epsilon_; } -Manifold Manifold::SetUncertainty(double uncertainty) const { +Manifold Manifold::SetEpsilon(double epsilon) const { auto impl = std::make_shared(*GetCsgLeafNode().GetImpl()); - impl->SetUncertainty(uncertainty); + impl->SetEpsilon(epsilon); impl->SimplifyTopology(); return Manifold(impl); } @@ -413,8 +413,8 @@ Manifold Manifold::SetTolerance(double tolerance) const { impl->SimplifyTopology(); } else { // for reducing tolerance, we need to make sure it is still at least - // equal to uncertainty. - impl->tolerance_ = std::max(impl->uncertainty_, tolerance); + // equal to epsilon. + impl->tolerance_ = std::max(impl->epsilon_, tolerance); } return Manifold(impl); } diff --git a/src/polygon.cpp b/src/polygon.cpp index 8f8fcdaa9..ae0ef5b3a 100644 --- a/src/polygon.cpp +++ b/src/polygon.cpp @@ -945,39 +945,39 @@ namespace manifold { * @param polys The set of polygons, wound CCW and representing multiple * polygons and/or holes. These have 2D-projected positions as well as * references back to the original vertices. - * @param precision The value of ε, bounding the uncertainty of the + * @param epsilon The value of ε, bounding the uncertainty of the * input. * @return std::vector The triangles, referencing the original * vertex indicies. */ -std::vector TriangulateIdx(const PolygonsIdx &polys, double precision) { +std::vector TriangulateIdx(const PolygonsIdx &polys, double epsilon) { std::vector triangles; - double updatedPrecision = precision; + double updatedEpsilon = epsilon; #ifdef MANIFOLD_EXCEPTIONS try { #endif - if (IsConvex(polys, precision)) { // fast path + if (IsConvex(polys, epsilon)) { // fast path triangles = TriangulateConvex(polys); } else { - EarClip triangulator(polys, precision); + EarClip triangulator(polys, epsilon); triangles = triangulator.Triangulate(); - updatedPrecision = triangulator.GetPrecision(); + updatedEpsilon = triangulator.GetPrecision(); } #ifdef MANIFOLD_EXCEPTIONS #ifdef MANIFOLD_DEBUG if (params.intermediateChecks) { CheckTopology(triangles, polys); if (!params.processOverlaps) { - CheckGeometry(triangles, polys, 2 * updatedPrecision); + CheckGeometry(triangles, polys, 2 * updatedEpsilon); } } } catch (const geometryErr &e) { if (!params.suppressErrors) { - PrintFailure(e, polys, triangles, updatedPrecision); + PrintFailure(e, polys, triangles, updatedEpsilon); } throw; } catch (const std::exception &e) { - PrintFailure(e, polys, triangles, updatedPrecision); + PrintFailure(e, polys, triangles, updatedEpsilon); throw; #else } catch (const std::exception &e) { @@ -994,12 +994,12 @@ std::vector TriangulateIdx(const PolygonsIdx &polys, double precision) { * * @param polygons The set of polygons, wound CCW and representing multiple * polygons and/or holes. - * @param precision The value of ε, bounding the uncertainty of the + * @param epsilon The value of ε, bounding the uncertainty of the * input. * @return std::vector The triangles, referencing the original * polygon points in order. */ -std::vector Triangulate(const Polygons &polygons, double precision) { +std::vector Triangulate(const Polygons &polygons, double epsilon) { int idx = 0; PolygonsIdx polygonsIndexed; for (const auto &poly : polygons) { @@ -1009,7 +1009,7 @@ std::vector Triangulate(const Polygons &polygons, double precision) { } polygonsIndexed.push_back(simpleIndexed); } - return TriangulateIdx(polygonsIndexed, precision); + return TriangulateIdx(polygonsIndexed, epsilon); } ExecutionParams &PolygonParams() { return params; } diff --git a/src/quickhull.cpp b/src/quickhull.cpp index 3d0037677..4abb7d42e 100644 --- a/src/quickhull.cpp +++ b/src/quickhull.cpp @@ -847,7 +847,7 @@ void Manifold::Impl::Hull(VecView vertPos) { QuickHull qh(vertPos); std::tie(halfedge_, vertPos_) = qh.buildMesh(); CalculateBBox(); - SetUncertainty(bBox_.Scale() * kTolerance); + SetEpsilon(bBox_.Scale() * kTolerance); CalculateNormals(); InitializeOriginal(); Finish(); diff --git a/src/shared.h b/src/shared.h index 677baab87..e5749a642 100644 --- a/src/shared.h +++ b/src/shared.h @@ -29,9 +29,9 @@ inline vec3 SafeNormalize(vec3 v) { return std::isfinite(v.x) ? v : vec3(0.0); } -inline double MaxUncertainty(double minUncertainty, const Box& bBox) { - double uncertainty = std::max(minUncertainty, kTolerance * bBox.Scale()); - return std::isfinite(uncertainty) ? uncertainty : -1; +inline double MaxEpsilon(double minEpsilon, const Box& bBox) { + double epsilon = std::max(minEpsilon, kTolerance * bBox.Scale()); + return std::isfinite(epsilon) ? epsilon : -1; } inline int NextHalfedge(int current) { diff --git a/src/sort.cpp b/src/sort.cpp index 453b9e766..57889ca67 100644 --- a/src/sort.cpp +++ b/src/sort.cpp @@ -143,21 +143,21 @@ bool MergeMeshGLP(MeshGLP& mesh) { bBox.min[i] = minMax.first; bBox.max[i] = minMax.second; } - auto uncertainty = MaxUncertainty(0, bBox); + auto epsilon = MaxEpsilon(0, bBox); auto policy = autoPolicy(numOpenVert, 1e5); Vec vertBox(numOpenVert); Vec vertMorton(numOpenVert); for_each_n(policy, countAt(0), numOpenVert, - [&vertMorton, &vertBox, &openVerts, &bBox, &mesh, uncertainty](const int i) { + [&vertMorton, &vertBox, &openVerts, &bBox, &mesh, epsilon](const int i) { int vert = openVerts[i]; const vec3 center(mesh.vertProperties[mesh.numProp * vert], mesh.vertProperties[mesh.numProp * vert + 1], mesh.vertProperties[mesh.numProp * vert + 2]); - vertBox[i].min = center - uncertainty / 2.0; - vertBox[i].max = center + uncertainty / 2.0; + vertBox[i].min = center - epsilon / 2.0; + vertBox[i].max = center + epsilon / 2.0; vertMorton[i] = MortonCode(center, bBox); }); @@ -211,7 +211,7 @@ void Manifold::Impl::Finish() { if (halfedge_.size() == 0) return; CalculateBBox(); - SetUncertainty(uncertainty_); + SetEpsilon(epsilon_); if (!bBox_.IsFinite()) { // Decimated out of existence - early out. MarkFailure(Error::NoError); diff --git a/test/boolean_complex_test.cpp b/test/boolean_complex_test.cpp index 71aaa4579..6ef843936 100644 --- a/test/boolean_complex_test.cpp +++ b/test/boolean_complex_test.cpp @@ -236,7 +236,7 @@ TEST(BooleanComplex, Close) { Manifold result = a; for (int i = 0; i < 10; i++) { // std::cout << i << std::endl; - result ^= a.Translate({a.GetUncertainty() / 10 * i, 0.0, 0.0}); + result ^= a.Translate({a.GetEpsilon() / 10 * i, 0.0, 0.0}); } auto prop = result.GetProperties(); const double tol = 0.004; diff --git a/test/properties_test.cpp b/test/properties_test.cpp index 9b1f72fed..0ce338122 100644 --- a/test/properties_test.cpp +++ b/test/properties_test.cpp @@ -37,17 +37,17 @@ TEST(Properties, GetProperties) { TEST(Properties, Precision) { Manifold cube = Manifold::Cube(); - EXPECT_FLOAT_EQ(cube.GetUncertainty(), kTolerance); + EXPECT_FLOAT_EQ(cube.GetEpsilon(), kTolerance); cube = cube.Scale({0.1, 1, 10}); - EXPECT_FLOAT_EQ(cube.GetUncertainty(), 10 * kTolerance); + EXPECT_FLOAT_EQ(cube.GetEpsilon(), 10 * kTolerance); cube = cube.Translate({-100, -10, -1}); - EXPECT_FLOAT_EQ(cube.GetUncertainty(), 100 * kTolerance); + EXPECT_FLOAT_EQ(cube.GetEpsilon(), 100 * kTolerance); } TEST(Properties, Precision2) { Manifold cube = Manifold::Cube(); cube = cube.Translate({-0.5, 0, 0}).Scale({2, 1, 1}); - EXPECT_FLOAT_EQ(cube.GetUncertainty(), 2 * kTolerance); + EXPECT_FLOAT_EQ(cube.GetEpsilon(), 2 * kTolerance); } TEST(Properties, Precision3) { @@ -56,7 +56,7 @@ TEST(Properties, Precision3) { MeshGL mesh = cylinder.GetMeshGL(); mesh.faceID.clear(); - Manifold cylinder2 = Manifold(mesh).SetUncertainty(0.001); + Manifold cylinder2 = Manifold(mesh).SetEpsilon(0.001); const auto prop2 = cylinder2.GetProperties(); EXPECT_NEAR(prop.volume, prop2.volume, 0.001); diff --git a/test/samples_test.cpp b/test/samples_test.cpp index 1c64ec270..f2c2d2ec8 100644 --- a/test/samples_test.cpp +++ b/test/samples_test.cpp @@ -221,7 +221,7 @@ TEST(Samples, GyroidModule) { CheckGL(gyroid); const Box bounds = gyroid.BoundingBox(); - const double precision = gyroid.GetUncertainty(); + const double precision = gyroid.GetEpsilon(); EXPECT_NEAR(bounds.min.z, 0, precision); EXPECT_NEAR(bounds.max.z, size * std::sqrt(2.0), precision); @@ -267,7 +267,7 @@ TEST(Samples, Sponge4) { EXPECT_EQ(cutSponge.second.Genus(), 13394); CrossSection projection(cutSponge.first.Project()); - projection = projection.Simplify(cutSponge.first.GetUncertainty()); + projection = projection.Simplify(cutSponge.first.GetEpsilon()); Rect rect = projection.Bounds(); Box box = cutSponge.first.BoundingBox(); EXPECT_EQ(rect.min.x, box.min.x); diff --git a/test/sdf_test.cpp b/test/sdf_test.cpp index 120db1fa9..21ebcfae7 100644 --- a/test/sdf_test.cpp +++ b/test/sdf_test.cpp @@ -70,7 +70,7 @@ TEST(SDF, Bounds) { Manifold cubeVoid = Manifold::LevelSet( CubeVoid(), {vec3(-size / 2), vec3(size / 2)}, edgeLength); Box bounds = cubeVoid.BoundingBox(); - const double precision = cubeVoid.GetUncertainty(); + const double precision = cubeVoid.GetEpsilon(); #ifdef MANIFOLD_EXPORT if (options.exportModels) ExportMesh("cubeVoid.glb", cubeVoid.GetMeshGL(), {}); @@ -94,7 +94,7 @@ TEST(SDF, Bounds2) { Manifold cubeVoid = Manifold::LevelSet( CubeVoid(), {vec3(-size / 2), vec3(size / 2)}, edgeLength); Box bounds = cubeVoid.BoundingBox(); - const double precision = cubeVoid.GetUncertainty(); + const double precision = cubeVoid.GetEpsilon(); #ifdef MANIFOLD_EXPORT if (options.exportModels) ExportMesh("cubeVoid2.glb", cubeVoid.GetMeshGL(), {}); @@ -121,7 +121,7 @@ TEST(SDF, Surface) { Manifold cube = Manifold::Cube(vec3(size), true); cube -= cubeVoid; Box bounds = cube.BoundingBox(); - const double precision = cube.GetUncertainty(); + const double precision = cube.GetEpsilon(); #ifdef MANIFOLD_EXPORT if (options.exportModels) ExportMesh("cube.gltf", cube.GetMeshGL(), {}); #endif diff --git a/test/test_main.cpp b/test/test_main.cpp index c6d871a2c..b34dbece4 100644 --- a/test/test_main.cpp +++ b/test/test_main.cpp @@ -322,8 +322,8 @@ void RelatedGL(const Manifold& out, const std::vector& originals, const ivec3 normalIdx = updateNormals ? ivec3(3, 4, 5) : ivec3(0); MeshGL output = out.GetMeshGL(normalIdx); - float uncertainty = - std::max(static_cast(out.GetUncertainty()), + float epsilon = + std::max(static_cast(out.GetEpsilon()), std::numeric_limits::epsilon() * static_cast(out.BoundingBox().Scale())); @@ -378,7 +378,7 @@ void RelatedGL(const Manifold& out, const std::vector& originals, vec3 edges[3]; for (int k : {0, 1, 2}) edges[k] = inTriPos[k] - outTriPos[j]; const double volume = la::dot(edges[0], la::cross(edges[1], edges[2])); - ASSERT_LE(volume, area * uncertainty); + ASSERT_LE(volume, area * epsilon); if (checkNormals) { vec3 normal; @@ -401,7 +401,7 @@ void RelatedGL(const Manifold& out, const std::vector& originals, const double volumeP = la::dot(edgesP[0], la::cross(edgesP[1], edgesP[2])); - ASSERT_LE(volumeP, area * uncertainty); + ASSERT_LE(volumeP, area * epsilon); } } } From cf1c296d5e232c7af7470de9d0dcf15cf546ed5d Mon Sep 17 00:00:00 2001 From: pca006132 Date: Sun, 20 Oct 2024 13:41:33 +0800 Subject: [PATCH 04/15] use epsilon instead of tolerance --- src/constructors.cpp | 3 +-- src/face_op.cpp | 4 ++-- src/properties.cpp | 6 +++--- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/constructors.cpp b/src/constructors.cpp index 3110ee55a..f90ab8834 100644 --- a/src/constructors.cpp +++ b/src/constructors.cpp @@ -407,8 +407,7 @@ Manifold Manifold::Revolve(const Polygons& crossSection, int circularSegments, // Add front and back triangles if not a full revolution. if (!isFullRevolution) { - std::vector frontTriangles = - Triangulate(polygons, pImpl_->tolerance_); + std::vector frontTriangles = Triangulate(polygons, pImpl_->epsilon_); for (auto& t : frontTriangles) { triVerts.push_back({startPoses[t.x], startPoses[t.y], startPoses[t.z]}); } diff --git a/src/face_op.cpp b/src/face_op.cpp index e265c615e..15cab42a8 100644 --- a/src/face_op.cpp +++ b/src/face_op.cpp @@ -79,7 +79,7 @@ void Manifold::Impl::Face2Tri(const Vec& faceEdge, auto triCCW = [&projection, this](const ivec3 tri) { return CCW(projection * this->vertPos_[tri[0]], projection * this->vertPos_[tri[1]], - projection * this->vertPos_[tri[2]], tolerance_) >= 0; + projection * this->vertPos_[tri[2]], epsilon_) >= 0; }; ivec3 tri0(halfedge_[firstEdge].startVert, halfedge_[firstEdge].endVert, @@ -130,7 +130,7 @@ void Manifold::Impl::Face2Tri(const Vec& faceEdge, const PolygonsIdx polys = Face2Polygons(halfedge_.cbegin() + faceEdge[face], halfedge_.cbegin() + faceEdge[face + 1], projection); - return TriangulateIdx(polys, tolerance_); + return TriangulateIdx(polys, epsilon_); }; #if (MANIFOLD_PAR == 1) && __has_include() tbb::task_group group; diff --git a/src/properties.cpp b/src/properties.cpp index 16bb6f3a7..1c19784aa 100644 --- a/src/properties.cpp +++ b/src/properties.cpp @@ -229,7 +229,7 @@ bool Manifold::Impl::Is2Manifold() const { bool Manifold::Impl::MatchesTriNormals() const { if (halfedge_.size() == 0 || faceNormal_.size() != NumTri()) return true; return all_of(countAt(0_uz), countAt(NumTri()), - CheckCCW({halfedge_, vertPos_, faceNormal_, 2 * tolerance_})); + CheckCCW({halfedge_, vertPos_, faceNormal_, 2 * epsilon_})); } /** @@ -239,7 +239,7 @@ int Manifold::Impl::NumDegenerateTris() const { if (halfedge_.size() == 0 || faceNormal_.size() != NumTri()) return true; return count_if( countAt(0_uz), countAt(NumTri()), - CheckCCW({halfedge_, vertPos_, faceNormal_, -1 * tolerance_ / 2})); + CheckCCW({halfedge_, vertPos_, faceNormal_, -1 * epsilon_ / 2})); } Properties Manifold::Impl::GetProperties() const { @@ -252,7 +252,7 @@ Properties Manifold::Impl::GetProperties() const { double volumeCompensation = 0; for (size_t i = 0; i < NumTri(); ++i) { auto [area1, volume1] = - FaceAreaVolume({halfedge_, vertPos_, tolerance_})(i); + FaceAreaVolume({halfedge_, vertPos_, epsilon_})(i); const double t1 = area + area1; const double t2 = volume + volume1; areaCompensation += (area - t1) + area1; From faec2f96256bce8f050380a913c03cfeaa2c2429 Mon Sep 17 00:00:00 2001 From: pca006132 Date: Sun, 20 Oct 2024 13:42:38 +0800 Subject: [PATCH 05/15] guard simplify topology for SetEpsilon --- src/manifold.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/manifold.cpp b/src/manifold.cpp index c710490b3..228cc0a95 100644 --- a/src/manifold.cpp +++ b/src/manifold.cpp @@ -389,8 +389,9 @@ double Manifold::GetEpsilon() const { Manifold Manifold::SetEpsilon(double epsilon) const { auto impl = std::make_shared(*GetCsgLeafNode().GetImpl()); + auto oldTolerance = impl->tolerance_; impl->SetEpsilon(epsilon); - impl->SimplifyTopology(); + if (impl->tolerance_ > oldTolerance) impl->SimplifyTopology(); return Manifold(impl); } From 7a463160a08c58aa8ee4d06cdd1290737e639223 Mon Sep 17 00:00:00 2001 From: pca006132 Date: Sun, 20 Oct 2024 13:44:21 +0800 Subject: [PATCH 06/15] format --- src/properties.cpp | 3 +-- src/sort.cpp | 21 +++++++++++---------- test/test_main.cpp | 7 +++---- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/properties.cpp b/src/properties.cpp index 1c19784aa..17f3658d1 100644 --- a/src/properties.cpp +++ b/src/properties.cpp @@ -251,8 +251,7 @@ Properties Manifold::Impl::GetProperties() const { double areaCompensation = 0; double volumeCompensation = 0; for (size_t i = 0; i < NumTri(); ++i) { - auto [area1, volume1] = - FaceAreaVolume({halfedge_, vertPos_, epsilon_})(i); + auto [area1, volume1] = FaceAreaVolume({halfedge_, vertPos_, epsilon_})(i); const double t1 = area + area1; const double t2 = volume + volume1; areaCompensation += (area - t1) + area1; diff --git a/src/sort.cpp b/src/sort.cpp index 57889ca67..1ef2f6c25 100644 --- a/src/sort.cpp +++ b/src/sort.cpp @@ -148,19 +148,20 @@ bool MergeMeshGLP(MeshGLP& mesh) { Vec vertBox(numOpenVert); Vec vertMorton(numOpenVert); - for_each_n(policy, countAt(0), numOpenVert, - [&vertMorton, &vertBox, &openVerts, &bBox, &mesh, epsilon](const int i) { - int vert = openVerts[i]; + for_each_n( + policy, countAt(0), numOpenVert, + [&vertMorton, &vertBox, &openVerts, &bBox, &mesh, epsilon](const int i) { + int vert = openVerts[i]; - const vec3 center(mesh.vertProperties[mesh.numProp * vert], - mesh.vertProperties[mesh.numProp * vert + 1], - mesh.vertProperties[mesh.numProp * vert + 2]); + const vec3 center(mesh.vertProperties[mesh.numProp * vert], + mesh.vertProperties[mesh.numProp * vert + 1], + mesh.vertProperties[mesh.numProp * vert + 2]); - vertBox[i].min = center - epsilon / 2.0; - vertBox[i].max = center + epsilon / 2.0; + vertBox[i].min = center - epsilon / 2.0; + vertBox[i].max = center + epsilon / 2.0; - vertMorton[i] = MortonCode(center, bBox); - }); + vertMorton[i] = MortonCode(center, bBox); + }); Vec vertNew2Old(numOpenVert); sequence(vertNew2Old.begin(), vertNew2Old.end()); diff --git a/test/test_main.cpp b/test/test_main.cpp index cbe907a63..cda4be654 100644 --- a/test/test_main.cpp +++ b/test/test_main.cpp @@ -300,10 +300,9 @@ void RelatedGL(const Manifold& out, const std::vector& originals, const ivec3 normalIdx = updateNormals ? ivec3(3, 4, 5) : ivec3(0); MeshGL output = out.GetMeshGL(normalIdx); - float epsilon = - std::max(static_cast(out.GetEpsilon()), - std::numeric_limits::epsilon() * - static_cast(out.BoundingBox().Scale())); + float epsilon = std::max(static_cast(out.GetEpsilon()), + std::numeric_limits::epsilon() * + static_cast(out.BoundingBox().Scale())); for (size_t run = 0; run < output.runOriginalID.size(); ++run) { const float* m = output.runTransform.data() + 12 * run; From cdee4e955b059c02d8b0832a792ac803aa1793a8 Mon Sep 17 00:00:00 2001 From: pca006132 Date: Sun, 20 Oct 2024 13:47:50 +0800 Subject: [PATCH 07/15] remove conflict markers --- src/impl.cpp | 34 ---------------------------------- 1 file changed, 34 deletions(-) diff --git a/src/impl.cpp b/src/impl.cpp index 2c7db0532..d140b8ebb 100644 --- a/src/impl.cpp +++ b/src/impl.cpp @@ -144,35 +144,6 @@ struct CoplanarEdge { // Only operate on coplanar triangles if (volume > std::max(area, areaPair) * tolerance) return; -<<<<<<< HEAD - // Check property linearity - if (area > 0) { - normal /= area; - for (int i = 0; i < numProp; ++i) { - const double scale = tolerance / 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 * tolerance) return; - } - } - -======= ->>>>>>> upstream/master face2face[edgeIdx] = std::make_pair(edgeFace, pairFace); } }; @@ -526,12 +497,7 @@ void Manifold::Impl::WarpBatch(std::function)> warpFunc) { Update(); faceNormal_.resize(0); // force recalculation of triNormal CalculateNormals(); -<<<<<<< HEAD - SetEpsilon(); - InitializeOriginal(); -======= SetPrecision(); ->>>>>>> upstream/master Finish(); CreateFaces(); meshRelation_.originalID = -1; From e3ceb1ad890e114c559dcbaf4b76044830213837 Mon Sep 17 00:00:00 2001 From: pca006132 Date: Sun, 20 Oct 2024 13:50:03 +0800 Subject: [PATCH 08/15] fix builds --- bindings/python/manifold3d.cpp | 7 +++---- bindings/wasm/bindings.cpp | 2 +- bindings/wasm/bindings.js | 2 +- src/impl.cpp | 4 ++-- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/bindings/python/manifold3d.cpp b/bindings/python/manifold3d.cpp index a85b7f191..cd0d63d61 100644 --- a/bindings/python/manifold3d.cpp +++ b/bindings/python/manifold3d.cpp @@ -216,7 +216,7 @@ NB_MODULE(manifold3d, m) { nb::arg("radius"), get_circular_segments__radius); m.def("triangulate", &Triangulate, nb::arg("polygons"), - nb::arg("precision") = -1, triangulate__polygons__precision); + nb::arg("epsilon") = -1, triangulate__polygons__epsilon); nb::class_(m, "Manifold") .def(nb::init<>(), manifold__manifold) @@ -338,18 +338,17 @@ NB_MODULE(manifold3d, m) { .def("num_tri", &Manifold::NumTri, manifold__num_tri) .def("num_prop", &Manifold::NumProp, manifold__num_prop) .def("num_prop_vert", &Manifold::NumPropVert, manifold__num_prop_vert) - .def("precision", &Manifold::GetEpsilon, manifold__get_uncertainty) .def("genus", &Manifold::Genus, manifold__genus) .def( "volume", [](const Manifold &self) { return self.GetProperties().volume; }, "Get the volume of the manifold\n This is clamped to zero for a " - "given face if they are within the Precision().") + "given face if they are within the Epsilon().") .def( "surface_area", [](const Manifold &self) { return self.GetProperties().surfaceArea; }, "Get the surface area of the manifold\n This is clamped to zero for " - "a given face if they are within the Precision().") + "a given face if they are within the Epsilon().") .def("original_id", &Manifold::OriginalID, manifold__original_id) .def("as_original", &Manifold::AsOriginal, manifold__as_original) .def("is_empty", &Manifold::IsEmpty, manifold__is_empty) diff --git a/bindings/wasm/bindings.cpp b/bindings/wasm/bindings.cpp index c75edd20f..d8d4b1815 100644 --- a/bindings/wasm/bindings.cpp +++ b/bindings/wasm/bindings.cpp @@ -161,7 +161,7 @@ EMSCRIPTEN_BINDINGS(whatever) { .function("numProp", &Manifold::NumProp) .function("numPropVert", &Manifold::NumPropVert) .function("_boundingBox", &Manifold::BoundingBox) - .function("precision", &Manifold::Precision) + .function("tolerance", &Manifold::GetTolerance) .function("genus", &Manifold::Genus) .function("getProperties", &Manifold::GetProperties) .function("minGap", &Manifold::MinGap) diff --git a/bindings/wasm/bindings.js b/bindings/wasm/bindings.js index 7d32deb4a..fd2b23df6 100644 --- a/bindings/wasm/bindings.js +++ b/bindings/wasm/bindings.js @@ -294,7 +294,7 @@ Module.setup = function() { const polygonsVec = this._Project(); const result = new CrossSectionCtor(polygonsVec, fillRuleToInt('Positive')); disposePolygons(polygonsVec); - return result.simplify(this.precision); + return result.simplify(this.tolerance); }; Module.Manifold.prototype.split = function(manifold) { diff --git a/src/impl.cpp b/src/impl.cpp index d140b8ebb..39b971917 100644 --- a/src/impl.cpp +++ b/src/impl.cpp @@ -358,7 +358,7 @@ void Manifold::Impl::CreateFaces() { for_each_n(autoPolicy(halfedge_.size(), 1e4), countAt(0), halfedge_.size(), CoplanarEdge({face2face, triArea, halfedge_, vertPos_, meshRelation_.triRef, meshRelation_.triProperties, - meshRelation_.numProp, precision_})); + meshRelation_.numProp, epsilon_})); std::vector components; const int numComponent = GetLabels(components, face2face, NumTri()); @@ -497,7 +497,7 @@ void Manifold::Impl::WarpBatch(std::function)> warpFunc) { Update(); faceNormal_.resize(0); // force recalculation of triNormal CalculateNormals(); - SetPrecision(); + SetEpsilon(); Finish(); CreateFaces(); meshRelation_.originalID = -1; From 72a7433d579296149185c0bc09564a715d38a1c8 Mon Sep 17 00:00:00 2001 From: pca006132 Date: Sun, 20 Oct 2024 13:50:59 +0800 Subject: [PATCH 09/15] use epsilon for barycentric --- src/boolean_result.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/boolean_result.cpp b/src/boolean_result.cpp index 88bf04930..63876b300 100644 --- a/src/boolean_result.cpp +++ b/src/boolean_result.cpp @@ -526,7 +526,7 @@ struct Barycentric { VecView halfedgeP; VecView halfedgeQ; VecView halfedgeR; - const double tolerance; + const double epsilon; void operator()(const int tri) { const TriRef refPQ = ref[tri]; @@ -543,7 +543,7 @@ struct Barycentric { for (const int i : {0, 1, 2}) { const int vert = halfedgeR[3 * tri + i].startVert; - uvw[3 * tri + i] = GetBarycentric(vertPosR[vert], triPos, tolerance); + uvw[3 * tri + i] = GetBarycentric(vertPosR[vert], triPos, epsilon); } } }; @@ -564,7 +564,7 @@ void CreateProperties(Manifold::Impl &outR, const Manifold::Impl &inP, for_each_n(autoPolicy(numTri, 1e4), countAt(0), numTri, Barycentric({bary, outR.meshRelation_.triRef, inP.vertPos_, inQ.vertPos_, outR.vertPos_, inP.halfedge_, - inQ.halfedge_, outR.halfedge_, outR.tolerance_})); + inQ.halfedge_, outR.halfedge_, outR.epsilon_})); using Entry = std::pair; int idMissProp = outR.NumVert(); From 8ccbeb7df52c26cb29a593f77ce40fb2a66e350a Mon Sep 17 00:00:00 2001 From: pca006132 Date: Sun, 20 Oct 2024 14:00:19 +0800 Subject: [PATCH 10/15] prefer using epsilon --- src/edge_op.cpp | 4 ++-- src/impl.cpp | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/edge_op.cpp b/src/edge_op.cpp index 079c9d244..197585e58 100644 --- a/src/edge_op.cpp +++ b/src/edge_op.cpp @@ -502,14 +502,14 @@ void Manifold::Impl::CollapseEdge(const int edge, std::vector& edges) { // Don't collapse if the edges separating the faces are not colinear // (can happen when the two faces are coplanar). if (CCW(projection * pOld, projection * pLast, projection * pNew, - tolerance_) != 0) + epsilon_) != 0) return; } } // Don't collapse edge if it would cause a triangle to invert. if (CCW(projection * pNext, projection * pLast, projection * pNew, - tolerance_) < 0) + epsilon_) < 0) return; pLast = pNext; diff --git a/src/impl.cpp b/src/impl.cpp index 39b971917..a5cdbcf19 100644 --- a/src/impl.cpp +++ b/src/impl.cpp @@ -98,7 +98,7 @@ struct CoplanarEdge { VecView triRef; VecView triProp; const int numProp; - const double tolerance; + const double epsilon; // FIXME: race condition void operator()(const int edgeIdx) { @@ -138,11 +138,11 @@ struct CoplanarEdge { triArea[edgeFace] = area; triArea[pairFace] = areaPair; // Don't link degenerate triangles - if (area < length * tolerance || areaPair < lengthPair * tolerance) return; + if (area < length * epsilon || areaPair < lengthPair * epsilon) return; const double volume = std::abs(la::dot(normal, pairVec)); // Only operate on coplanar triangles - if (volume > std::max(area, areaPair) * tolerance) return; + if (volume > std::max(area, areaPair) * epsilon) return; face2face[edgeIdx] = std::make_pair(edgeFace, pairFace); } @@ -153,7 +153,7 @@ struct CheckCoplanarity { VecView halfedge; VecView vertPos; std::vector* components; - const double tolerance; + const double epsilon; void operator()(int tri) { const int component = (*components)[tri]; @@ -170,7 +170,7 @@ struct CheckCoplanarity { // If any component vertex is not coplanar with the component's reference // triangle, unmark the entire component so that none of its triangles are // marked coplanar. - if (std::abs(la::dot(normal, vert - origin)) > tolerance) { + if (std::abs(la::dot(normal, vert - origin)) > epsilon) { reinterpret_cast*>(&comp2tri[component]) ->store(-1, std::memory_order_relaxed); break; @@ -375,7 +375,7 @@ void Manifold::Impl::CreateFaces() { for_each_n(autoPolicy(halfedge_.size(), 1e4), countAt(0), NumTri(), CheckCoplanarity( - {comp2tri, halfedge_, vertPos_, &components, tolerance_})); + {comp2tri, halfedge_, vertPos_, &components, epsilon_})); Vec& triRef = meshRelation_.triRef; for (size_t tri = 0; tri < NumTri(); ++tri) { From 6cdf3451134199a6ee9229c0cc28eb2ae4ce01ee Mon Sep 17 00:00:00 2001 From: pca006132 Date: Sun, 20 Oct 2024 14:15:06 +0800 Subject: [PATCH 11/15] update python bindings --- bindings/python/examples/all_apis.py | 3 ++- bindings/python/manifold3d.cpp | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/bindings/python/examples/all_apis.py b/bindings/python/examples/all_apis.py index 027b4acf2..c9fcff831 100644 --- a/bindings/python/examples/all_apis.py +++ b/bindings/python/examples/all_apis.py @@ -90,7 +90,8 @@ def all_manifold(): n = m.num_tri() n = m.num_vert() i = m.original_id() - p = m.precision() + p = m.get_tolerance() + pp = m.set_tolerance(0.0001) c = m.project() m = m.refine(2) m = m.refine_to_length(0.1) diff --git a/bindings/python/manifold3d.cpp b/bindings/python/manifold3d.cpp index cd0d63d61..f9b5bced3 100644 --- a/bindings/python/manifold3d.cpp +++ b/bindings/python/manifold3d.cpp @@ -350,6 +350,8 @@ 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 Epsilon().") .def("original_id", &Manifold::OriginalID, manifold__original_id) + .def("get_tolerance", &Manifold::GetTolerance, manifold__get_tolerance) + .def("set_tolerance", &Manifold::SetTolerance, manifold__set_tolerance__tolerance) .def("as_original", &Manifold::AsOriginal, manifold__as_original) .def("is_empty", &Manifold::IsEmpty, manifold__is_empty) .def("decompose", &Manifold::Decompose, manifold__decompose) From 5efe0986ae5a58e389b085fc9d95c168ac62c4ad Mon Sep 17 00:00:00 2001 From: pca006132 Date: Mon, 21 Oct 2024 14:56:45 +0800 Subject: [PATCH 12/15] address comments --- src/impl.cpp | 25 +++++++++++++++++++------ src/impl.h | 1 + src/manifold.cpp | 11 +---------- 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/src/impl.cpp b/src/impl.cpp index a5cdbcf19..9e6c9894e 100644 --- a/src/impl.cpp +++ b/src/impl.cpp @@ -99,6 +99,7 @@ struct CoplanarEdge { VecView triProp; const int numProp; const double epsilon; + const double tolerance; // FIXME: race condition void operator()(const int edgeIdx) { @@ -142,7 +143,7 @@ struct CoplanarEdge { const double volume = std::abs(la::dot(normal, pairVec)); // Only operate on coplanar triangles - if (volume > std::max(area, areaPair) * epsilon) return; + if (volume > std::max(area, areaPair) * tolerance) return; face2face[edgeIdx] = std::make_pair(edgeFace, pairFace); } @@ -153,7 +154,7 @@ struct CheckCoplanarity { VecView halfedge; VecView vertPos; std::vector* components; - const double epsilon; + const double tolerance; void operator()(int tri) { const int component = (*components)[tri]; @@ -170,7 +171,7 @@ struct CheckCoplanarity { // If any component vertex is not coplanar with the component's reference // triangle, unmark the entire component so that none of its triangles are // marked coplanar. - if (std::abs(la::dot(normal, vert - origin)) > epsilon) { + if (std::abs(la::dot(normal, vert - origin)) > tolerance) { reinterpret_cast*>(&comp2tri[component]) ->store(-1, std::memory_order_relaxed); break; @@ -358,7 +359,7 @@ void Manifold::Impl::CreateFaces() { for_each_n(autoPolicy(halfedge_.size(), 1e4), countAt(0), halfedge_.size(), CoplanarEdge({face2face, triArea, halfedge_, vertPos_, meshRelation_.triRef, meshRelation_.triProperties, - meshRelation_.numProp, epsilon_})); + meshRelation_.numProp, epsilon_, tolerance_})); std::vector components; const int numComponent = GetLabels(components, face2face, NumTri()); @@ -375,7 +376,7 @@ void Manifold::Impl::CreateFaces() { for_each_n(autoPolicy(halfedge_.size(), 1e4), countAt(0), NumTri(), CheckCoplanarity( - {comp2tri, halfedge_, vertPos_, &components, epsilon_})); + {comp2tri, halfedge_, vertPos_, &components, tolerance_})); Vec& triRef = meshRelation_.triRef; for (size_t tri = 0; tri < NumTri(); ++tri) { @@ -568,7 +569,19 @@ Manifold::Impl Manifold::Impl::Transform(const mat3x4& transform_) const { */ void Manifold::Impl::SetEpsilon(double minEpsilon) { epsilon_ = MaxEpsilon(minEpsilon, bBox_); - tolerance_ = std::max(tolerance_, epsilon_); + SetTolerance(std::max(tolerance_, epsilon_)); +} + +void Manifold::Impl::SetTolerance(double newTolerance) { + if (newTolerance > tolerance_) { + // increased tolerance -> simplify opportunity + tolerance_ = newTolerance; + SimplifyTopology(); + } else { + // for reducing tolerance, we need to make sure it is still at least + // equal to epsilon. + tolerance_ = std::max(epsilon_, newTolerance); + } } /** diff --git a/src/impl.h b/src/impl.h index 19d4ba7bc..579751608 100644 --- a/src/impl.h +++ b/src/impl.h @@ -277,6 +277,7 @@ struct Manifold::Impl { bool IsFinite() const; bool IsIndexInBounds(VecView triVerts) const; void SetEpsilon(double minEpsilon = -1); + void SetTolerance(double minTolerance); bool IsManifold() const; bool Is2Manifold() const; bool MatchesTriNormals() const; diff --git a/src/manifold.cpp b/src/manifold.cpp index 064c2b67b..a25bc656f 100644 --- a/src/manifold.cpp +++ b/src/manifold.cpp @@ -382,9 +382,7 @@ double Manifold::GetEpsilon() const { Manifold Manifold::SetEpsilon(double epsilon) const { auto impl = std::make_shared(*GetCsgLeafNode().GetImpl()); - auto oldTolerance = impl->tolerance_; impl->SetEpsilon(epsilon); - if (impl->tolerance_ > oldTolerance) impl->SimplifyTopology(); return Manifold(impl); } @@ -402,14 +400,7 @@ double Manifold::GetTolerance() const { */ Manifold Manifold::SetTolerance(double tolerance) const { auto impl = std::make_shared(*GetCsgLeafNode().GetImpl()); - if (tolerance > impl->tolerance_) { - impl->tolerance_ = tolerance; - impl->SimplifyTopology(); - } else { - // for reducing tolerance, we need to make sure it is still at least - // equal to epsilon. - impl->tolerance_ = std::max(impl->epsilon_, tolerance); - } + impl->SetTolerance(tolerance); return Manifold(impl); } From ce0938f076c42709a26654e44e4b48ae3a62f614 Mon Sep 17 00:00:00 2001 From: pca006132 Date: Mon, 21 Oct 2024 14:57:06 +0800 Subject: [PATCH 13/15] format --- 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 f9b5bced3..cafa89a72 100644 --- a/bindings/python/manifold3d.cpp +++ b/bindings/python/manifold3d.cpp @@ -351,7 +351,8 @@ NB_MODULE(manifold3d, m) { "a given face if they are within the Epsilon().") .def("original_id", &Manifold::OriginalID, manifold__original_id) .def("get_tolerance", &Manifold::GetTolerance, manifold__get_tolerance) - .def("set_tolerance", &Manifold::SetTolerance, manifold__set_tolerance__tolerance) + .def("set_tolerance", &Manifold::SetTolerance, + manifold__set_tolerance__tolerance) .def("as_original", &Manifold::AsOriginal, manifold__as_original) .def("is_empty", &Manifold::IsEmpty, manifold__is_empty) .def("decompose", &Manifold::Decompose, manifold__decompose) From c20fad0479d5121f790f03b56629dccc7ebdeeb2 Mon Sep 17 00:00:00 2001 From: pca006132 Date: Mon, 21 Oct 2024 16:07:09 +0800 Subject: [PATCH 14/15] remove SetTolerance Tolerance value is set before the mesh is created, when SimplfiyTopology cannot be called. --- src/impl.cpp | 14 +------------- src/impl.h | 1 - src/manifold.cpp | 11 ++++++++++- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/src/impl.cpp b/src/impl.cpp index 9e6c9894e..69fee504d 100644 --- a/src/impl.cpp +++ b/src/impl.cpp @@ -569,19 +569,7 @@ Manifold::Impl Manifold::Impl::Transform(const mat3x4& transform_) const { */ void Manifold::Impl::SetEpsilon(double minEpsilon) { epsilon_ = MaxEpsilon(minEpsilon, bBox_); - SetTolerance(std::max(tolerance_, epsilon_)); -} - -void Manifold::Impl::SetTolerance(double newTolerance) { - if (newTolerance > tolerance_) { - // increased tolerance -> simplify opportunity - tolerance_ = newTolerance; - SimplifyTopology(); - } else { - // for reducing tolerance, we need to make sure it is still at least - // equal to epsilon. - tolerance_ = std::max(epsilon_, newTolerance); - } + tolerance_ = std::max(tolerance_, epsilon_); } /** diff --git a/src/impl.h b/src/impl.h index 579751608..19d4ba7bc 100644 --- a/src/impl.h +++ b/src/impl.h @@ -277,7 +277,6 @@ struct Manifold::Impl { bool IsFinite() const; bool IsIndexInBounds(VecView triVerts) const; void SetEpsilon(double minEpsilon = -1); - void SetTolerance(double minTolerance); bool IsManifold() const; bool Is2Manifold() const; bool MatchesTriNormals() const; diff --git a/src/manifold.cpp b/src/manifold.cpp index a25bc656f..064c2b67b 100644 --- a/src/manifold.cpp +++ b/src/manifold.cpp @@ -382,7 +382,9 @@ double Manifold::GetEpsilon() const { Manifold Manifold::SetEpsilon(double epsilon) const { auto impl = std::make_shared(*GetCsgLeafNode().GetImpl()); + auto oldTolerance = impl->tolerance_; impl->SetEpsilon(epsilon); + if (impl->tolerance_ > oldTolerance) impl->SimplifyTopology(); return Manifold(impl); } @@ -400,7 +402,14 @@ double Manifold::GetTolerance() const { */ Manifold Manifold::SetTolerance(double tolerance) const { auto impl = std::make_shared(*GetCsgLeafNode().GetImpl()); - impl->SetTolerance(tolerance); + if (tolerance > impl->tolerance_) { + impl->tolerance_ = tolerance; + impl->SimplifyTopology(); + } else { + // for reducing tolerance, we need to make sure it is still at least + // equal to epsilon. + impl->tolerance_ = std::max(impl->epsilon_, tolerance); + } return Manifold(impl); } From 72ea845db36ea0d97d90613c7ef32388d17bbeef Mon Sep 17 00:00:00 2001 From: pca006132 Date: Mon, 21 Oct 2024 17:17:15 +0800 Subject: [PATCH 15/15] read/write MeshGL tolerance field --- src/impl.h | 1 + src/manifold.cpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/impl.h b/src/impl.h index 19d4ba7bc..f8671cef4 100644 --- a/src/impl.h +++ b/src/impl.h @@ -111,6 +111,7 @@ struct Manifold::Impl { const auto numProp = meshGL.numProp - 3; meshRelation_.numProp = numProp; meshRelation_.properties.resize(meshGL.NumVert() * numProp); + tolerance_ = meshGL.tolerance; // This will have unreferenced duplicate positions that will be removed by // Impl::RemoveUnreferencedVerts(). vertPos_.resize(meshGL.NumVert()); diff --git a/src/manifold.cpp b/src/manifold.cpp index 064c2b67b..565437436 100644 --- a/src/manifold.cpp +++ b/src/manifold.cpp @@ -71,6 +71,8 @@ MeshGLP GetMeshGLImpl(const manifold::Manifold::Impl& impl, MeshGLP out; out.numProp = 3 + numProp; + out.tolerance = std::max(std::numeric_limits::epsilon(), + static_cast(impl.tolerance_)); out.triVerts.resize(3 * numTri); const int numHalfedge = impl.halfedgeTangent_.size();