| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | #include <algorithm> |
| | #include <functional> |
| | #include <limits> |
| |
|
| | #include <QFuture> |
| | #include <QFutureWatcher> |
| | #include <QtConcurrentMap> |
| |
|
| | #include <Base/Sequencer.h> |
| | #include <Base/Tools.h> |
| |
|
| | |
| | #ifdef OPTIMIZE_CURVATURE |
| | # include <Eigen/Eigenvalues> |
| | #else |
| | # include <Mod/Mesh/App/WildMagic4/Wm4MeshCurvature.h> |
| | #endif |
| |
|
| | #include "Approximation.h" |
| | #include "Curvature.h" |
| | #include "Iterator.h" |
| | #include "MeshKernel.h" |
| | #include "Tools.h" |
| |
|
| |
|
| | using namespace MeshCore; |
| | namespace sp = std::placeholders; |
| |
|
| | MeshCurvature::MeshCurvature(const MeshKernel& kernel) |
| | : myKernel(kernel) |
| | , myMinPoints(20) |
| | , myRadius(0.5F) |
| | { |
| | mySegment.resize(kernel.CountFacets()); |
| | std::generate(mySegment.begin(), mySegment.end(), Base::iotaGen<FacetIndex>(0)); |
| | } |
| |
|
| | MeshCurvature::MeshCurvature(const MeshKernel& kernel, std::vector<FacetIndex> segm) |
| | : myKernel(kernel) |
| | , myMinPoints(20) |
| | , myRadius(0.5F) |
| | , mySegment(std::move(segm)) |
| | {} |
| |
|
| | void MeshCurvature::ComputePerFace(bool parallel) |
| | { |
| | myCurvature.clear(); |
| | MeshRefPointToFacets search(myKernel); |
| | FacetCurvature face(myKernel, search, myRadius, myMinPoints); |
| |
|
| | if (!parallel) { |
| | Base::SequencerLauncher seq("Curvature estimation", mySegment.size()); |
| | for (FacetIndex it : mySegment) { |
| | CurvatureInfo info = face.Compute(it); |
| | myCurvature.push_back(info); |
| | seq.next(); |
| | } |
| | } |
| | else { |
| | |
| | QFuture<CurvatureInfo> future |
| | = QtConcurrent::mapped(mySegment, std::bind(&FacetCurvature::Compute, &face, sp::_1)); |
| | |
| | QFutureWatcher<CurvatureInfo> watcher; |
| | watcher.setFuture(future); |
| | watcher.waitForFinished(); |
| | for (const auto& it : future) { |
| | myCurvature.push_back(it); |
| | } |
| | } |
| | } |
| |
|
| | #ifdef OPTIMIZE_CURVATURE |
| | namespace MeshCore |
| | { |
| | void GenerateComplementBasis(Eigen::Vector3f& rkU, Eigen::Vector3f& rkV, const Eigen::Vector3f& rkW) |
| | { |
| | float fInvLength; |
| |
|
| | if (fabs(rkW[0]) >= fabs(rkW[1])) { |
| | |
| | fInvLength = 1.0 / sqrt(rkW[0] * rkW[0] + rkW[2] * rkW[2]); |
| | rkU[0] = -rkW[2] * fInvLength; |
| | rkU[1] = 0.0; |
| | rkU[2] = +rkW[0] * fInvLength; |
| | rkV[0] = rkW[1] * rkU[2]; |
| | rkV[1] = rkW[2] * rkU[0] - rkW[0] * rkU[2]; |
| | rkV[2] = -rkW[1] * rkU[0]; |
| | } |
| | else { |
| | |
| | fInvLength = 1.0 / sqrt(rkW[1] * rkW[1] + rkW[2] * rkW[2]); |
| | rkU[0] = 0.0; |
| | rkU[1] = +rkW[2] * fInvLength; |
| | rkU[2] = -rkW[1] * fInvLength; |
| | rkV[0] = rkW[1] * rkU[2] - rkW[2] * rkU[1]; |
| | rkV[1] = -rkW[0] * rkU[2]; |
| | rkV[2] = rkW[0] * rkU[1]; |
| | } |
| | } |
| | } |
| |
|
| | void MeshCurvature::ComputePerVertex() |
| | { |
| | |
| | const MeshPointArray& pts = myKernel.GetPoints(); |
| |
|
| | MeshCore::MeshRefPointToFacets pt2f(myKernel); |
| | MeshCore::MeshRefPointToPoints pt2p(myKernel); |
| | unsigned long numPoints = myKernel.CountPoints(); |
| |
|
| | myCurvature.clear(); |
| | myCurvature.reserve(numPoints); |
| |
|
| | std::vector<Eigen::Vector3f> akNormal(numPoints); |
| | std::vector<Eigen::Vector3f> akVertex(numPoints); |
| | for (unsigned long i = 0; i < numPoints; i++) { |
| | Base::Vector3f n = pt2f.GetNormal(i); |
| | akNormal[i][0] = n.x; |
| | akNormal[i][1] = n.y; |
| | akNormal[i][2] = n.z; |
| | const Base::Vector3f& p = pts[i]; |
| | akVertex[i][0] = p.x; |
| | akVertex[i][1] = p.y; |
| | akVertex[i][2] = p.z; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | for (unsigned long i = 0; i < numPoints; i++) { |
| | Eigen::Matrix3f akDNormal; |
| | akDNormal.setZero(); |
| | Eigen::Matrix3f akWWTrn; |
| | akWWTrn.setZero(); |
| | Eigen::Matrix3f akDWTrn; |
| | akDWTrn.setZero(); |
| |
|
| | int iV0 = i; |
| | int iV1; |
| | const std::set<unsigned long>& nb = pt2p[i]; |
| | for (std::set<unsigned long>::const_iterator it = nb.begin(); it != nb.end(); ++it) { |
| | iV1 = *it; |
| |
|
| | |
| | |
| | Eigen::Vector3f kE = akVertex[iV1] - akVertex[iV0]; |
| | Eigen::Vector3f kW = kE - (kE.dot(akNormal[iV0])) * akNormal[iV0]; |
| | Eigen::Vector3f kD = akNormal[iV1] - akNormal[iV0]; |
| | for (int iRow = 0; iRow < 3; iRow++) { |
| | for (int iCol = 0; iCol < 3; iCol++) { |
| | akWWTrn(iRow, iCol) += 2 * kW[iRow] * kW[iCol]; |
| | akDWTrn(iRow, iCol) += 2 * kD[iRow] * kW[iCol]; |
| | } |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | for (int iRow = 0; iRow < 3; iRow++) { |
| | for (int iCol = 0; iCol < 3; iCol++) { |
| | akWWTrn(iRow, iCol) = 0.5 * akWWTrn(iRow, iCol) |
| | + akNormal[i][iRow] * akNormal[i][iCol]; |
| | akDWTrn(iRow, iCol) *= 0.5; |
| | } |
| | } |
| |
|
| | akDNormal = akDWTrn * akWWTrn.inverse(); |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | float minCurvature; |
| | float maxCurvature; |
| | Base::Vector3f minDirection; |
| | Base::Vector3f maxDirection; |
| |
|
| | Eigen::Vector3f kU, kV; |
| | Eigen::Vector3f kN = akNormal[i]; |
| | float len = kN.squaredNorm(); |
| | if (len == 0) { |
| | continue; |
| | } |
| | MeshCore::GenerateComplementBasis(kU, kV, kN); |
| |
|
| | |
| | |
| | |
| | float fS01 = kU.dot(akDNormal * kV); |
| | float fS10 = kV.dot(akDNormal * kU); |
| | float fSAvr = 0.5 * (fS01 + fS10); |
| | Eigen::Matrix2f kS; |
| | kS(0, 0) = kU.dot(akDNormal * kU); |
| | kS(0, 1) = fSAvr; |
| | kS(1, 0) = fSAvr; |
| | kS(1, 1) = kV.dot(akDNormal * kV); |
| |
|
| | |
| | float fTrace = kS(0, 0) + kS(1, 1); |
| | float fDet = kS(0, 0) * kS(1, 1) - kS(0, 1) * kS(1, 0); |
| | float fDiscr = fTrace * fTrace - (4.0) * fDet; |
| | float fRootDiscr = sqrt(fabs(fDiscr)); |
| | minCurvature = (0.5) * (fTrace - fRootDiscr); |
| | maxCurvature = (0.5) * (fTrace + fRootDiscr); |
| |
|
| | |
| | Eigen::Vector2f kW0(kS(0, 1), minCurvature - kS(0, 0)); |
| | Eigen::Vector2f kW1(minCurvature - kS(1, 1), kS(1, 0)); |
| | if (kW0.squaredNorm() >= kW1.squaredNorm()) { |
| | float len = kW0.squaredNorm(); |
| | if (len > 0 && len != 1) { |
| | kW0.normalize(); |
| | } |
| | Eigen::Vector3f v = kU * kW0[0] + kV * kW0[1]; |
| | minDirection.Set(v[0], v[1], v[2]); |
| | } |
| | else { |
| | float len = kW1.squaredNorm(); |
| | if (len > 0 && len != 1) { |
| | kW1.normalize(); |
| | } |
| | Eigen::Vector3f v = kU * kW1[0] + kV * kW1[1]; |
| | minDirection.Set(v[0], v[1], v[2]); |
| | } |
| |
|
| | kW0 = Eigen::Vector2f(kS(0, 1), maxCurvature - kS(0, 0)); |
| | kW1 = Eigen::Vector2f(maxCurvature - kS(1, 1), kS(1, 0)); |
| | if (kW0.squaredNorm() >= kW1.squaredNorm()) { |
| | float len = kW0.squaredNorm(); |
| | if (len > 0 && len != 1) { |
| | kW0.normalize(); |
| | } |
| | Eigen::Vector3f v = kU * kW0[0] + kV * kW0[1]; |
| | maxDirection.Set(v[0], v[1], v[2]); |
| | } |
| | else { |
| | float len = kW1.squaredNorm(); |
| | if (len > 0 && len != 1) { |
| | kW1.normalize(); |
| | } |
| | Eigen::Vector3f v = kU * kW1[0] + kV * kW1[1]; |
| | maxDirection.Set(v[0], v[1], v[2]); |
| | } |
| |
|
| | CurvatureInfo ci; |
| | ci.fMaxCurvature = maxCurvature; |
| | ci.cMaxCurvDir = maxDirection; |
| | ci.fMinCurvature = minCurvature; |
| | ci.cMinCurvDir = minDirection; |
| | myCurvature.push_back(ci); |
| | } |
| | } |
| | #else |
| | void MeshCurvature::ComputePerVertex() |
| | { |
| | myCurvature.clear(); |
| |
|
| | |
| | std::vector<Wm4::Vector3<double>> aPnts; |
| | aPnts.reserve(myKernel.CountPoints()); |
| | MeshPointIterator cPIt(myKernel); |
| | for (cPIt.Init(); cPIt.More(); cPIt.Next()) { |
| | Wm4::Vector3<double> cP(cPIt->x, cPIt->y, cPIt->z); |
| | aPnts.push_back(cP); |
| | } |
| |
|
| | |
| | std::vector<int> aIdx; |
| | aIdx.reserve(3 * myKernel.CountFacets()); |
| | const MeshFacetArray& raFts = myKernel.GetFacets(); |
| | for (const auto& it : raFts) { |
| | for (PointIndex point : it._aulPoints) { |
| | aIdx.push_back(int(point)); |
| | } |
| | } |
| |
|
| | |
| | if (myKernel.CountPoints() == 0 || myKernel.CountFacets() == 0) { |
| | return; |
| | } |
| |
|
| | |
| | Wm4::MeshCurvature<double> |
| | meshCurv(myKernel.CountPoints(), aPnts.data(), myKernel.CountFacets(), aIdx.data()); |
| |
|
| | |
| | const Wm4::Vector3<double>* aMaxCurvDir = meshCurv.GetMaxDirections(); |
| | const Wm4::Vector3<double>* aMinCurvDir = meshCurv.GetMinDirections(); |
| | const double* aMaxCurv = meshCurv.GetMaxCurvatures(); |
| | const double* aMinCurv = meshCurv.GetMinCurvatures(); |
| |
|
| | myCurvature.reserve(myKernel.CountPoints()); |
| | for (unsigned long i = 0; i < myKernel.CountPoints(); i++) { |
| | CurvatureInfo ci; |
| | ci.cMaxCurvDir = Base::Vector3f( |
| | (float)aMaxCurvDir[i].X(), |
| | (float)aMaxCurvDir[i].Y(), |
| | (float)aMaxCurvDir[i].Z() |
| | ); |
| | ci.cMinCurvDir = Base::Vector3f( |
| | (float)aMinCurvDir[i].X(), |
| | (float)aMinCurvDir[i].Y(), |
| | (float)aMinCurvDir[i].Z() |
| | ); |
| | ci.fMaxCurvature = (float)aMaxCurv[i]; |
| | ci.fMinCurvature = (float)aMinCurv[i]; |
| | myCurvature.push_back(ci); |
| | } |
| | } |
| | #endif |
| |
|
| | |
| |
|
| | namespace MeshCore |
| | { |
| | class FitPointCollector: public MeshCollector |
| | { |
| | public: |
| | explicit FitPointCollector(std::set<PointIndex>& ind) |
| | : indices(ind) |
| | {} |
| | void Append(const MeshCore::MeshKernel& kernel, FacetIndex index) override |
| | { |
| | PointIndex ulP1 {}, ulP2 {}, ulP3 {}; |
| | kernel.GetFacetPoints(index, ulP1, ulP2, ulP3); |
| | indices.insert(ulP1); |
| | indices.insert(ulP2); |
| | indices.insert(ulP3); |
| | } |
| |
|
| | private: |
| | std::set<PointIndex>& indices; |
| | }; |
| | } |
| |
|
| | |
| |
|
| | FacetCurvature::FacetCurvature( |
| | const MeshKernel& kernel, |
| | const MeshRefPointToFacets& search, |
| | float r, |
| | unsigned long pt |
| | ) |
| | : myKernel(kernel) |
| | , mySearch(search) |
| | , myMinPoints(pt) |
| | , myRadius(r) |
| | {} |
| |
|
| | CurvatureInfo FacetCurvature::Compute(FacetIndex index) const |
| | { |
| | Base::Vector3f rkDir0, rkDir1; |
| | Base::Vector3f rkNormal; |
| |
|
| | MeshGeomFacet face = myKernel.GetFacet(index); |
| | Base::Vector3f face_gravity = face.GetGravityPoint(); |
| | Base::Vector3f face_normal = face.GetNormal(); |
| | std::set<PointIndex> point_indices; |
| | FitPointCollector collect(point_indices); |
| |
|
| | float searchDist = myRadius; |
| | int attempts = 0; |
| | do { |
| | mySearch.Neighbours(index, searchDist, collect); |
| | if (point_indices.empty()) { |
| | break; |
| | } |
| | float min_points = myMinPoints; |
| | float use_points = point_indices.size(); |
| | searchDist = searchDist * std::sqrt(min_points / use_points); |
| | } while ((point_indices.size() < myMinPoints) && (attempts++ < 3)); |
| |
|
| | std::vector<Base::Vector3f> fitPoints; |
| | const MeshPointArray& verts = myKernel.GetPoints(); |
| | fitPoints.reserve(point_indices.size()); |
| | for (PointIndex it : point_indices) { |
| | fitPoints.push_back(verts[it] - face_gravity); |
| | } |
| |
|
| | float fMin {}, fMax {}; |
| | if (fitPoints.size() >= myMinPoints) { |
| | SurfaceFit surf_fit; |
| | surf_fit.AddPoints(fitPoints); |
| | surf_fit.Fit(); |
| | rkNormal = surf_fit.GetNormal(); |
| | double dMin {}, dMax {}, dDistance {}; |
| | if (surf_fit.GetCurvatureInfo(0.0, 0.0, 0.0, dMin, dMax, rkDir1, rkDir0, dDistance)) { |
| | fMin = (float)dMin; |
| | fMax = (float)dMax; |
| | } |
| | else { |
| | fMin = std::numeric_limits<float>::max(); |
| | fMax = std::numeric_limits<float>::max(); |
| | } |
| | } |
| | else { |
| | |
| | fMin = std::numeric_limits<float>::max(); |
| | fMax = std::numeric_limits<float>::max(); |
| | } |
| |
|
| | CurvatureInfo info; |
| | if (fMin < fMax) { |
| | info.fMaxCurvature = fMax; |
| | info.fMinCurvature = fMin; |
| | info.cMaxCurvDir = rkDir1; |
| | info.cMinCurvDir = rkDir0; |
| | } |
| | else { |
| | info.fMaxCurvature = fMin; |
| | info.fMinCurvature = fMax; |
| | info.cMaxCurvDir = rkDir0; |
| | info.cMinCurvDir = rkDir1; |
| | } |
| |
|
| | |
| | |
| | if (rkNormal * face_normal < 0.0) { |
| | |
| | |
| | std::swap(info.cMaxCurvDir, info.cMinCurvDir); |
| | std::swap(info.fMaxCurvature, info.fMinCurvature); |
| | info.fMaxCurvature *= (-1.0); |
| | info.fMinCurvature *= (-1.0); |
| | } |
| |
|
| | return info; |
| | } |
| |
|