Path: blob/master/thirdparty/manifold/src/properties.cpp
20959 views
// Copyright 2021 The Manifold Authors.1//2// Licensed under the Apache License, Version 2.0 (the "License");3// you may not use this file except in compliance with the License.4// You may obtain a copy of the License at5//6// http://www.apache.org/licenses/LICENSE-2.07//8// Unless required by applicable law or agreed to in writing, software9// distributed under the License is distributed on an "AS IS" BASIS,10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.11// See the License for the specific language governing permissions and12// limitations under the License.1314#include <limits>1516#if MANIFOLD_PAR == 117#include <tbb/combinable.h>18#endif1920#include "impl.h"21#include "parallel.h"22#include "tri_dist.h"2324namespace {25using namespace manifold;2627struct CurvatureAngles {28VecView<double> meanCurvature;29VecView<double> gaussianCurvature;30VecView<double> area;31VecView<double> degree;32VecView<const Halfedge> halfedge;33VecView<const vec3> vertPos;34VecView<const vec3> triNormal;3536void operator()(size_t tri) {37vec3 edge[3];38vec3 edgeLength(0.0);39for (int i : {0, 1, 2}) {40const int startVert = halfedge[3 * tri + i].startVert;41const int endVert = halfedge[3 * tri + i].endVert;42edge[i] = vertPos[endVert] - vertPos[startVert];43edgeLength[i] = la::length(edge[i]);44edge[i] /= edgeLength[i];45const int neighborTri = halfedge[3 * tri + i].pairedHalfedge / 3;46const double dihedral =470.25 * edgeLength[i] *48std::asin(la::dot(la::cross(triNormal[tri], triNormal[neighborTri]),49edge[i]));50AtomicAdd(meanCurvature[startVert], dihedral);51AtomicAdd(meanCurvature[endVert], dihedral);52AtomicAdd(degree[startVert], 1.0);53}5455vec3 phi;56phi[0] = std::acos(-la::dot(edge[2], edge[0]));57phi[1] = std::acos(-la::dot(edge[0], edge[1]));58phi[2] = kPi - phi[0] - phi[1];59const double area3 = edgeLength[0] * edgeLength[1] *60la::length(la::cross(edge[0], edge[1])) / 6;6162for (int i : {0, 1, 2}) {63const int vert = halfedge[3 * tri + i].startVert;64AtomicAdd(gaussianCurvature[vert], -phi[i]);65AtomicAdd(area[vert], area3);66}67}68};6970struct CheckHalfedges {71VecView<const Halfedge> halfedges;7273bool operator()(size_t edge) const {74const Halfedge halfedge = halfedges[edge];75if (halfedge.startVert == -1 && halfedge.endVert == -1 &&76halfedge.pairedHalfedge == -1)77return true;78if (halfedges[NextHalfedge(edge)].startVert == -1 ||79halfedges[NextHalfedge(NextHalfedge(edge))].startVert == -1) {80return false;81}82if (halfedge.pairedHalfedge == -1) return false;8384const Halfedge paired = halfedges[halfedge.pairedHalfedge];85bool good = true;86good &= paired.pairedHalfedge == static_cast<int>(edge);87good &= halfedge.startVert != halfedge.endVert;88good &= halfedge.startVert == paired.endVert;89good &= halfedge.endVert == paired.startVert;90return good;91}92};9394struct CheckCCW {95VecView<const Halfedge> halfedges;96VecView<const vec3> vertPos;97VecView<const vec3> triNormal;98const double tol;99100bool operator()(size_t face) const {101if (halfedges[3 * face].pairedHalfedge < 0) return true;102103const mat2x3 projection = GetAxisAlignedProjection(triNormal[face]);104vec2 v[3];105for (int i : {0, 1, 2})106v[i] = projection * vertPos[halfedges[3 * face + i].startVert];107108const int ccw = CCW(v[0], v[1], v[2], std::abs(tol));109return tol > 0 ? ccw >= 0 : ccw == 0;110}111};112} // namespace113114namespace manifold {115116/**117* Returns true if this manifold is in fact an oriented even manifold and all of118* the data structures are consistent.119*/120bool Manifold::Impl::IsManifold() const {121if (halfedge_.size() == 0) return true;122if (halfedge_.size() % 3 != 0) return false;123return all_of(countAt(0_uz), countAt(halfedge_.size()),124CheckHalfedges({halfedge_}));125}126127/**128* Returns true if this manifold is in fact an oriented 2-manifold and all of129* the data structures are consistent.130*/131bool Manifold::Impl::Is2Manifold() const {132if (halfedge_.size() == 0) return true;133if (!IsManifold()) return false;134135Vec<Halfedge> halfedge(halfedge_);136stable_sort(halfedge.begin(), halfedge.end());137138return all_of(139countAt(0_uz), countAt(2 * NumEdge() - 1), [&halfedge](size_t edge) {140const Halfedge h = halfedge[edge];141if (h.startVert == -1 && h.endVert == -1 && h.pairedHalfedge == -1)142return true;143return h.startVert != halfedge[edge + 1].startVert ||144h.endVert != halfedge[edge + 1].endVert;145});146}147148#ifdef MANIFOLD_DEBUG149std::mutex dump_lock;150#endif151152/**153* Returns true if this manifold is self-intersecting.154* Note that this is not checking for epsilon-validity.155*/156bool Manifold::Impl::IsSelfIntersecting() const {157const double ep = 2 * epsilon_;158const double epsilonSq = ep * ep;159Vec<Box> faceBox;160Vec<uint32_t> faceMorton;161GetFaceBoxMorton(faceBox, faceMorton);162163std::atomic<bool> intersecting(false);164165auto f = [&](int tri0, int tri1) {166std::array<vec3, 3> triVerts0, triVerts1;167for (int i : {0, 1, 2}) {168triVerts0[i] = vertPos_[halfedge_[3 * tri0 + i].startVert];169triVerts1[i] = vertPos_[halfedge_[3 * tri1 + i].startVert];170}171// if triangles tri0 and tri1 share a vertex, return true to skip the172// check. we relax the sharing criteria a bit to allow for at most173// distance epsilon squared174for (int i : {0, 1, 2})175for (int j : {0, 1, 2})176if (distance2(triVerts0[i], triVerts1[j]) <= epsilonSq) return;177178if (DistanceTriangleTriangleSquared(triVerts0, triVerts1) == 0.0) {179// try to move the triangles around the normal of the other face180std::array<vec3, 3> tmp0, tmp1;181for (int i : {0, 1, 2}) tmp0[i] = triVerts0[i] + ep * faceNormal_[tri1];182if (DistanceTriangleTriangleSquared(tmp0, triVerts1) > 0.0) return;183for (int i : {0, 1, 2}) tmp0[i] = triVerts0[i] - ep * faceNormal_[tri1];184if (DistanceTriangleTriangleSquared(tmp0, triVerts1) > 0.0) return;185for (int i : {0, 1, 2}) tmp1[i] = triVerts1[i] + ep * faceNormal_[tri0];186if (DistanceTriangleTriangleSquared(triVerts0, tmp1) > 0.0) return;187for (int i : {0, 1, 2}) tmp1[i] = triVerts1[i] - ep * faceNormal_[tri0];188if (DistanceTriangleTriangleSquared(triVerts0, tmp1) > 0.0) return;189190#ifdef MANIFOLD_DEBUG191if (ManifoldParams().verbose > 0) {192dump_lock.lock();193std::cout << "intersecting:" << std::endl;194for (int i : {0, 1, 2}) std::cout << triVerts0[i] << " ";195std::cout << std::endl;196for (int i : {0, 1, 2}) std::cout << triVerts1[i] << " ";197std::cout << std::endl;198dump_lock.unlock();199}200#endif201intersecting.store(true);202}203};204205auto recorder = MakeSimpleRecorder(f);206collider_.Collisions<true>(faceBox.cview(), recorder);207208return intersecting.load();209}210211/**212* Returns true if all triangles are CCW relative to their triNormals_.213*/214bool Manifold::Impl::MatchesTriNormals() const {215if (halfedge_.size() == 0 || faceNormal_.size() != NumTri()) return true;216return all_of(countAt(0_uz), countAt(NumTri()),217CheckCCW({halfedge_, vertPos_, faceNormal_, 2 * epsilon_}));218}219220/**221* Returns the number of triangles that are colinear within epsilon_.222*/223int Manifold::Impl::NumDegenerateTris() const {224if (halfedge_.size() == 0 || faceNormal_.size() != NumTri()) return true;225return count_if(226countAt(0_uz), countAt(NumTri()),227CheckCCW({halfedge_, vertPos_, faceNormal_, -1 * epsilon_ / 2}));228}229230double Manifold::Impl::GetProperty(Property prop) const {231ZoneScoped;232if (IsEmpty()) return 0;233234auto Volume = [this](size_t tri) {235const vec3 v = vertPos_[halfedge_[3 * tri].startVert];236vec3 crossP = la::cross(vertPos_[halfedge_[3 * tri + 1].startVert] - v,237vertPos_[halfedge_[3 * tri + 2].startVert] - v);238return la::dot(crossP, v) / 6.0;239};240241auto Area = [this](size_t tri) {242const vec3 v = vertPos_[halfedge_[3 * tri].startVert];243return la::length(244la::cross(vertPos_[halfedge_[3 * tri + 1].startVert] - v,245vertPos_[halfedge_[3 * tri + 2].startVert] - v)) /2462.0;247};248249// Kahan summation250double value = 0;251double valueCompensation = 0;252for (size_t i = 0; i < NumTri(); ++i) {253const double value1 = prop == Property::SurfaceArea ? Area(i) : Volume(i);254const double t = value + value1;255valueCompensation += (value - t) + value1;256value = t;257}258value += valueCompensation;259return value;260}261262void Manifold::Impl::CalculateCurvature(int gaussianIdx, int meanIdx) {263ZoneScoped;264if (IsEmpty()) return;265if (gaussianIdx < 0 && meanIdx < 0) return;266Vec<double> vertMeanCurvature(NumVert(), 0);267Vec<double> vertGaussianCurvature(NumVert(), kTwoPi);268Vec<double> vertArea(NumVert(), 0);269Vec<double> degree(NumVert(), 0);270auto policy = autoPolicy(NumTri(), 1e4);271for_each(policy, countAt(0_uz), countAt(NumTri()),272CurvatureAngles({vertMeanCurvature, vertGaussianCurvature, vertArea,273degree, halfedge_, vertPos_, faceNormal_}));274for_each_n(policy, countAt(0), NumVert(),275[&vertMeanCurvature, &vertGaussianCurvature, &vertArea,276°ree](const int vert) {277const double factor = degree[vert] / (6 * vertArea[vert]);278vertMeanCurvature[vert] *= factor;279vertGaussianCurvature[vert] *= factor;280});281282const int oldNumProp = NumProp();283const int numProp = std::max(oldNumProp, std::max(gaussianIdx, meanIdx) + 1);284const Vec<double> oldProperties = properties_;285properties_ = Vec<double>(numProp * NumPropVert(), 0);286numProp_ = numProp;287288Vec<uint8_t> counters(NumPropVert(), 0);289for_each_n(policy, countAt(0_uz), NumTri(), [&](const size_t tri) {290for (const int i : {0, 1, 2}) {291const Halfedge& edge = halfedge_[3 * tri + i];292const int vert = edge.startVert;293const int propVert = edge.propVert;294295auto old = std::atomic_exchange(296reinterpret_cast<std::atomic<uint8_t>*>(&counters[propVert]),297static_cast<uint8_t>(1));298if (old == 1) continue;299300for (int p = 0; p < oldNumProp; ++p) {301properties_[numProp * propVert + p] =302oldProperties[oldNumProp * propVert + p];303}304305if (gaussianIdx >= 0) {306properties_[numProp * propVert + gaussianIdx] =307vertGaussianCurvature[vert];308}309if (meanIdx >= 0) {310properties_[numProp * propVert + meanIdx] = vertMeanCurvature[vert];311}312}313});314}315316/**317* Calculates the bounding box of the entire manifold, which is stored318* internally to short-cut Boolean operations. Ignores NaNs.319*/320void Manifold::Impl::CalculateBBox() {321bBox_.min =322reduce(vertPos_.begin(), vertPos_.end(),323vec3(std::numeric_limits<double>::infinity()), [](auto a, auto b) {324if (std::isnan(a.x)) return b;325if (std::isnan(b.x)) return a;326return la::min(a, b);327});328bBox_.max = reduce(vertPos_.begin(), vertPos_.end(),329vec3(-std::numeric_limits<double>::infinity()),330[](auto a, auto b) {331if (std::isnan(a.x)) return b;332if (std::isnan(b.x)) return a;333return la::max(a, b);334});335}336337/**338* Determines if all verts are finite. Checking just the bounding box dimensions339* is insufficient as it ignores NaNs.340*/341bool Manifold::Impl::IsFinite() const {342return transform_reduce(343vertPos_.begin(), vertPos_.end(), true,344[](bool a, bool b) { return a && b; },345[](auto v) { return la::all(la::isfinite(v)); });346}347348/**349* Checks that the input triVerts array has all indices inside bounds of the350* vertPos_ array.351*/352bool Manifold::Impl::IsIndexInBounds(VecView<const ivec3> triVerts) const {353ivec2 minmax = transform_reduce(354triVerts.begin(), triVerts.end(),355ivec2(std::numeric_limits<int>::max(), std::numeric_limits<int>::min()),356[](auto a, auto b) {357a[0] = std::min(a[0], b[0]);358a[1] = std::max(a[1], b[1]);359return a;360},361[](auto tri) {362return ivec2(std::min(tri[0], std::min(tri[1], tri[2])),363std::max(tri[0], std::max(tri[1], tri[2])));364});365366return minmax[0] >= 0 && minmax[1] < static_cast<int>(NumVert());367}368369struct MinDistanceRecorder {370using Local = double;371const Manifold::Impl &self, &other;372#if MANIFOLD_PAR == 1373tbb::combinable<double> store = tbb::combinable<double>(374[]() { return std::numeric_limits<double>::infinity(); });375Local& local() { return store.local(); }376double get() {377double result = std::numeric_limits<double>::infinity();378store.combine_each([&](double& val) { result = std::min(result, val); });379return result;380}381#else382double result = std::numeric_limits<double>::infinity();383Local& local() { return result; }384double get() { return result; }385#endif386387void record(int triOther, int tri, double& minDistance) {388std::array<vec3, 3> p;389std::array<vec3, 3> q;390391for (const int j : {0, 1, 2}) {392p[j] = self.vertPos_[self.halfedge_[3 * tri + j].startVert];393q[j] = other.vertPos_[other.halfedge_[3 * triOther + j].startVert];394}395minDistance = std::min(minDistance, DistanceTriangleTriangleSquared(p, q));396}397};398399/*400* Returns the minimum gap between two manifolds. Returns a double between401* 0 and searchLength.402*/403double Manifold::Impl::MinGap(const Manifold::Impl& other,404double searchLength) const {405ZoneScoped;406Vec<Box> faceBoxOther;407Vec<uint32_t> faceMortonOther;408409other.GetFaceBoxMorton(faceBoxOther, faceMortonOther);410411transform(faceBoxOther.begin(), faceBoxOther.end(), faceBoxOther.begin(),412[searchLength](const Box& box) {413return Box(box.min - vec3(searchLength),414box.max + vec3(searchLength));415});416417MinDistanceRecorder recorder{*this, other};418collider_.Collisions<false, Box, MinDistanceRecorder>(faceBoxOther.cview(),419recorder, false);420double minDistanceSquared =421std::min(recorder.get(), searchLength * searchLength);422return sqrt(minDistanceSquared);423};424425} // namespace manifold426427428