23#ifndef VCL_ALGORITHMS_MESH_UPDATE_CURVATURE_H
24#define VCL_ALGORITHMS_MESH_UPDATE_CURVATURE_H
26#include <vclib/algorithms/mesh/intersection.h>
27#include <vclib/algorithms/mesh/point_sampling.h>
28#include <vclib/algorithms/mesh/stat.h>
29#include <vclib/algorithms/mesh/update/normal.h>
31#include <vclib/algorithms/core.h>
32#include <vclib/mesh.h>
33#include <vclib/space/complex.h>
34#include <vclib/space/core.h>
40enum class PrincipalCurvatureAlgorithm { TAUBIN95, PCA };
42template<FaceMeshConcept MeshType, LoggerConcept LogType = NullLogger>
43void updatePrincipalCurvatureTaubin95(MeshType& m, LogType& log =
nullLogger)
45 requirePerVertexPrincipalCurvature(m);
46 requirePerVertexAdjacentFaces(m);
49 using VertexType = MeshType::VertexType;
50 using PositionType = VertexType::PositionType;
51 using NormalType = VertexType::NormalType;
52 using ScalarType = PositionType::ScalarType;
53 using FaceType = MeshType::FaceType;
58 const VertexType* vert;
63 log.log(0,
"Updating per vertex normals...");
65 updatePerVertexNormalsAngleWeighted(m);
66 normalizePerVertexNormals(m);
68 log.log(5,
"Computing per vertex curvature...");
70 log.startProgress(
"", m.vertexNumber(), 5, 5, 100);
73 std::vector<ScalarType> weights;
76 MeshPos<FaceType> pos(v.adjFace(0), &v);
77 const VertexType* firstVertex = pos.adjVertex();
78 const VertexType* tmpVertex;
79 float totalDoubleAreaSize = 0;
84 pos.nextEdgeAdjacentToV();
85 tmpVertex = pos.adjVertex();
87 adjV.isBorder = pos.isEdgeOnBorder();
88 adjV.vert = tmpVertex;
89 adjV.doubleArea =
faceArea(*pos.face()) * 2;
90 totalDoubleAreaSize += adjV.doubleArea;
92 }
while (tmpVertex != firstVertex);
97 if (vertices[i].isBorder) {
98 weights.push_back(vertices[i].doubleArea / totalDoubleAreaSize);
103 (vertices[i].doubleArea +
105 totalDoubleAreaSize);
107 assert(weights.back() < 1.0f);
111 Matrix33<ScalarType> Tp;
113 NormalType n = v.normal();
114 for (
int i = 0; i < 3; ++i)
115 Tp(i, i) = 1.0f - std::pow(n[i], 2);
116 Tp(0, 1) = Tp(1, 0) = -1.0f * (n[0] * n[1]);
117 Tp(1, 2) = Tp(2, 1) = -1.0f * (n[1] * n[2]);
118 Tp(0, 2) = Tp(2, 0) = -1.0f * (n[0] * n[2]);
122 Matrix33<ScalarType> tempMatrix;
125 PositionType edge = (v.position() -
vertices[i].vert->position());
127 (2.0f * (v.normal().dot(edge))) / edge.squaredNorm();
128 PositionType t = Tp * edge;
130 tempMatrix = t.outerProduct(t);
131 M += tempMatrix * weights[i] * curvature;
136 PositionType e1(1.0f, 0.0f, 0.0f);
137 if ((e1 - v.normal()).squaredNorm() > (e1 + v.normal()).squaredNorm())
145 tempMatrix = w.outerProduct(w);
146 Q -= tempMatrix * 2.0f;
149 Matrix33<ScalarType> QtMQ = (Q.transpose() * M * Q);
151 Eigen::Matrix<ScalarType, 1, 3> T1 = Q.col(1);
152 Eigen::Matrix<ScalarType, 1, 3> T2 = Q.col(2);
157 ScalarType alpha = QtMQ(1, 1) - QtMQ(2, 2);
158 ScalarType beta = QtMQ(2, 1);
162 std::sqrt(4.0f * std::pow(alpha, 2) + 16.0f * std::pow(beta, 2));
163 h[0] = (2.0f * alpha + delta) / (2.0f * beta);
164 h[1] = (2.0f * alpha - delta) / (2.0f * beta);
167 ScalarType bestC, bestS;
168 ScalarType minError = std::numeric_limits<ScalarType>::infinity();
169 for (uint i = 0; i < 2; i++) {
170 delta = std::sqrt(std::pow(h[i], 2) + 4.0f);
171 t[0] = (h[i] + delta) / 2.0f;
172 t[1] = (h[i] - delta) / 2.0f;
174 for (uint j = 0; j < 2; j++) {
175 ScalarType squaredT = std::pow(t[j], 2);
176 ScalarType denominator = 1.0f + squaredT;
178 s = (2.0f * t[j]) / denominator;
179 c = (1 - squaredT) / denominator;
181 ScalarType approximation =
182 c * s * alpha + (std::pow(c, 2) - std::pow(s, 2)) * beta;
183 ScalarType angleSimilarity =
184 std::abs(std::acos(c) / std::asin(s));
186 std::abs(1.0f - angleSimilarity) + std::abs(approximation);
187 if (error < minError) {
197 Eigen::Matrix2f minor2x2;
201 minor2x2(0, 0) = QtMQ(1, 1);
202 minor2x2(0, 1) = QtMQ(1, 2);
203 minor2x2(1, 0) = QtMQ(2, 1);
204 minor2x2(1, 1) = QtMQ(2, 2);
206 S(0, 0) = S(1, 1) = c;
210 Eigen::Matrix2f StMS = S.transpose() * minor2x2 * S;
213 ScalarType principalCurv1 = (3.0f * StMS(0, 0)) - StMS(1, 1);
214 ScalarType principalCurv2 = (3.0f * StMS(1, 1)) - StMS(0, 0);
216 Eigen::Matrix<ScalarType, 1, 3> principalDir1 = T1 * c - T2 * s;
217 Eigen::Matrix<ScalarType, 1, 3> principalDir2 = T1 * s + T2 * c;
219 v.principalCurvature().maxDir() = principalDir1;
220 v.principalCurvature().minDir() = principalDir2;
221 v.principalCurvature().maxValue() = principalCurv1;
222 v.principalCurvature().minValue() = principalCurv2;
224 log.progress(m.index(v));
228 log.log(100,
"Per vertex curvature computed.");
243template<FaceMeshConcept MeshType, LoggerConcept LogType = NullLogger>
244void updatePrincipalCurvaturePCA(
246 typename MeshType::VertexType::PositionType::ScalarType radius,
247 bool montecarloSampling =
true,
250 using VertexType = MeshType::VertexType;
251 using PositionType = VertexType::PositionType;
252 using ScalarType = PositionType::ScalarType;
253 using NormalType = VertexType::NormalType;
254 using FaceType = MeshType::FaceType;
256 using VGrid = StaticGrid3<VertexType*, ScalarType>;
257 using VGridIterator = VGrid::ConstIterator;
262 log.log(0,
"Updating per vertex normals...");
264 updatePerVertexNormalsAngleWeighted(m);
265 normalizePerVertexNormals(m);
267 log.log(0,
"Computing per vertex curvature...");
268 log.startProgress(
"", m.vertexNumber());
270 if (montecarloSampling) {
271 area = surfaceArea(m);
276 parallelFor(m.vertices(), [&](VertexType& v) {
278 Matrix33<ScalarType> A, eigenvectors;
279 PositionType bp, eigenvalues;
280 if (montecarloSampling) {
281 Sphere s(v.position(), radius);
282 std::vector<VGridIterator> vec = pGrid.valuesInSphere(s);
283 std::vector<PositionType> points;
284 points.reserve(vec.size());
285 for (const auto& it : vec) {
286 points.push_back(it->second->position());
288 A = covarianceMatrixOfPointCloud(points);
289 A *= area * area / 1000;
292 Sphere<ScalarType> sph(v.position(), radius);
293 MeshType tmpMesh = intersection(m, sph);
295 A = covarianceMatrixOfMesh(tmpMesh);
298 Eigen::SelfAdjointEigenSolver<Eigen::Matrix<ScalarType, 3, 3>> eig(A);
299 eigenvalues = PositionType(eig.eigenvalues());
300 eigenvectors = eig.eigenvectors();
305 ScalarType bestv = std::abs(
306 v.normal().dot(PositionType(eigenvectors.col(0).normalized())));
307 for (uint i = 1; i < 3; ++i) {
308 ScalarType prod = std::abs(
309 v.normal().dot(PositionType(eigenvectors.col(i).normalized())));
315 v.principalCurvature().maxDir() =
316 (eigenvectors.col((best + 1) % 3).normalized());
317 v.principalCurvature().minDir() =
318 (eigenvectors.col((best + 2) % 3).normalized());
320 Matrix33<ScalarType> rot;
322 angle = acos(v.principalCurvature().maxDir().dot(v.normal()));
324 rot = rotationMatrix<Matrix33<ScalarType>>(
325 PositionType(v.principalCurvature().maxDir().cross(v.normal())),
326 -(M_PI * 0.5 - angle));
328 v.principalCurvature().maxDir() = rot * v.principalCurvature().maxDir();
330 angle = acos(v.principalCurvature().minDir().dot(v.normal()));
332 rot = rotationMatrix<Matrix33<ScalarType>>(
333 PositionType(v.principalCurvature().minDir().cross(v.normal())),
334 -(M_PI * 0.5 - angle));
336 v.principalCurvature().minDir() = rot * v.principalCurvature().minDir();
339 const ScalarType r5 = std::pow(radius, 5);
340 const ScalarType r6 = r5 * radius;
341 v.principalCurvature().maxValue() =
343 (4.0 * M_PI * r5 + 15 * eigenvalues[(best + 2) % 3] -
344 45.0 * eigenvalues[(best + 1) % 3]) /
346 v.principalCurvature().minValue() =
348 (4.0 * M_PI * r5 + 15 * eigenvalues[(best + 1) % 3] -
349 45.0 * eigenvalues[(best + 2) % 3]) /
351 if (v.principalCurvature().maxValue() <
352 v.principalCurvature().minValue()) {
354 v.principalCurvature().minValue(),
355 v.principalCurvature().maxValue());
357 v.principalCurvature().minDir(),
358 v.principalCurvature().maxDir());
361 log.progress(m.index(v));
366 log.log(100,
"Per vertex curvature computed.");
369template<FaceMeshConcept MeshType, LoggerConcept LogType = NullLogger>
370void updatePrincipalCurvature(MeshType& m, LogType& log = nullLogger)
372 requirePerVertexPrincipalCurvature(m);
374 updatePrincipalCurvatureTaubin95(m, log);
377template<FaceMeshConcept MeshType, LoggerConcept LogType = NullLogger>
378void updatePrincipalCurvature(
380 PrincipalCurvatureAlgorithm alg = PrincipalCurvatureAlgorithm::TAUBIN95,
381 LogType& log = nullLogger)
383 using enum PrincipalCurvatureAlgorithm;
384 requirePerVertexPrincipalCurvature(m);
388 case TAUBIN95: updatePrincipalCurvatureTaubin95(m, log);
break;
391 updatePrincipalCurvaturePCA(m, radius,
true, log);
auto diagonal() const
Calculates the diagonal length of the box.
Definition box.h:246
Box()
The Empty constructor of a box, initializes a null box.
Definition box.h:65
PointT size() const
Computes the size of the box.
Definition box.h:267
NullLogger nullLogger
The nullLogger object is an object of type NullLogger that is used as default argument in the functio...
Definition null_logger.h:123
auto boundingBox(const PointType &p)
Compute the bounding box of a single point.
Definition bounding_box.h:59
auto faceArea(const FaceType &f)
Computes the area of a face. Works both for triangle and polygonal faces, and it is optimized in case...
Definition geometry.h:108
void requirePerFaceAdjacentFaces(const MeshType &m)
This function asserts that a Mesh has a FaceContainer, the Face has a AdjacentFaces Component,...
Definition face_requirements.h:1037
constexpr detail::VerticesView vertices
A view that allows to iterate over the Vertex elements of an object.
Definition vertex.h:92
constexpr detail::AddressOfView addrOf
The addrOf view applies the address-of operator & on the input view.
Definition pointers.h:120