VCG Library
curvature.h
1 /****************************************************************************
2 * VCGLib o o *
3 * Visual and Computer Graphics Library o o *
4 * _ O _ *
5 * Copyright(C) 2004-2016 \/)\/ *
6 * Visual Computing Lab /\/| *
7 * ISTI - Italian National Research Council | *
8 * \ *
9 * All rights reserved. *
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 * This program is distributed in the hope that it will be useful, *
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
19 * GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
20 * for more details. *
21 * *
22 ****************************************************************************/
23 
24 #ifndef VCGLIB_UPDATE_CURVATURE_
25 #define VCGLIB_UPDATE_CURVATURE_
26 
27 #include <vcg/space/index/grid_static_ptr.h>
28 #include <vcg/simplex/face/jumping_pos.h>
29 #include <vcg/complex/algorithms/update/normal.h>
30 #include <vcg/complex/algorithms/point_sampling.h>
31 #include <vcg/complex/algorithms/intersection.h>
32 #include <vcg/complex/algorithms/inertia.h>
33 #include <Eigen/Core>
34 
35 namespace vcg {
36 namespace tri {
37 
39 
41 
43 
47 template <class MeshType>
49 {
50 
51 public:
52  typedef typename MeshType::FaceType FaceType;
53  typedef typename MeshType::FacePointer FacePointer;
54  typedef typename MeshType::FaceIterator FaceIterator;
55  typedef typename MeshType::VertexIterator VertexIterator;
56  typedef typename MeshType::VertContainer VertContainer;
57  typedef typename MeshType::VertexType VertexType;
58  typedef typename MeshType::VertexPointer VertexPointer;
59  typedef vcg::face::VFIterator<FaceType> VFIteratorType;
60  typedef typename MeshType::CoordType CoordType;
61  typedef typename CoordType::ScalarType ScalarType;
62  typedef typename MeshType::VertexType::CurScalarType CurScalarType;
63  typedef typename MeshType::VertexType::CurVecType CurVecType;
64 
65 
66 private:
67  // aux data struct used by PrincipalDirections
68  struct AdjVertex {
69  VertexType * vert;
70  float doubleArea;
71  bool isBorder;
72  };
73 
74 
75 public:
77 
78 /*
79  Compute principal direction and magniuto of curvature as describe in the paper:
80  @InProceedings{bb33922,
81  author = "G. Taubin",
82  title = "Estimating the Tensor of Curvature of a Surface from a
83  Polyhedral Approximation",
84  booktitle = "International Conference on Computer Vision",
85  year = "1995",
86  pages = "902--907",
87  URL = "http://dx.doi.org/10.1109/ICCV.1995.466840",
88  bibsource = "http://www.visionbib.com/bibliography/describe440.html#TT32253",
89  */
90  static void PrincipalDirections(MeshType &m)
91  {
92  tri::RequireVFAdjacency(m);
95 
96  for (VertexIterator vi =m.vert.begin(); vi !=m.vert.end(); ++vi) {
97  if ( ! (*vi).IsD() && (*vi).VFp() != NULL) {
98 
99  VertexType * central_vertex = &(*vi);
100 
101  std::vector<float> weights;
102  std::vector<AdjVertex> vertices;
103 
104  vcg::face::JumpingPos<FaceType> pos((*vi).VFp(), central_vertex);
105 
106  // firstV is the first vertex of the 1ring neighboorhood
107  VertexType* firstV = pos.VFlip();
108  VertexType* tempV;
109  float totalDoubleAreaSize = 0.0f;
110 
111  // compute the area of each triangle around the central vertex as well as their total area
112  do
113  {
114  // this bring the pos to the next triangle counterclock-wise
115  pos.FlipF();
116  pos.FlipE();
117 
118  // tempV takes the next vertex in the 1ring neighborhood
119  tempV = pos.VFlip();
120  assert(tempV!=central_vertex);
121  AdjVertex v;
122 
123  v.isBorder = pos.IsBorder();
124  v.vert = tempV;
125  v.doubleArea = vcg::DoubleArea(*pos.F());
126  totalDoubleAreaSize += v.doubleArea;
127 
128  vertices.push_back(v);
129  }
130  while(tempV != firstV);
131 
132  // compute the weights for the formula computing matrix M
133  for (size_t i = 0; i < vertices.size(); ++i) {
134  if (vertices[i].isBorder) {
135  weights.push_back(vertices[i].doubleArea / totalDoubleAreaSize);
136  } else {
137  weights.push_back(0.5f * (vertices[i].doubleArea + vertices[(i-1)%vertices.size()].doubleArea) / totalDoubleAreaSize);
138  }
139  assert(weights.back() < 1.0f);
140  }
141 
142  // compute I-NN^t to be used for computing the T_i's
143  Matrix33<ScalarType> Tp;
144  for (int i = 0; i < 3; ++i)
145  Tp[i][i] = 1.0f - powf(central_vertex->cN()[i],2);
146  Tp[0][1] = Tp[1][0] = -1.0f * (central_vertex->N()[0] * central_vertex->cN()[1]);
147  Tp[1][2] = Tp[2][1] = -1.0f * (central_vertex->cN()[1] * central_vertex->cN()[2]);
148  Tp[0][2] = Tp[2][0] = -1.0f * (central_vertex->cN()[0] * central_vertex->cN()[2]);
149 
150  // for all neighbors vi compute the directional curvatures k_i and the T_i
151  // compute M by summing all w_i k_i T_i T_i^t
152  Matrix33<ScalarType> tempMatrix;
153  Matrix33<ScalarType> M;
154  M.SetZero();
155  for (size_t i = 0; i < vertices.size(); ++i) {
156  CoordType edge = (central_vertex->cP() - vertices[i].vert->cP());
157  float curvature = (2.0f * (central_vertex->cN().dot(edge)) ) / edge.SquaredNorm();
158  CoordType T = (Tp*edge).normalized();
159  tempMatrix.ExternalProduct(T,T);
160  M += tempMatrix * weights[i] * curvature ;
161  }
162 
163  // compute vector W for the Householder matrix
164  CoordType W;
165  CoordType e1(1.0f,0.0f,0.0f);
166  if ((e1 - central_vertex->cN()).SquaredNorm() > (e1 + central_vertex->cN()).SquaredNorm())
167  W = e1 - central_vertex->cN();
168  else
169  W = e1 + central_vertex->cN();
170  W.Normalize();
171 
172  // compute the Householder matrix I - 2WW^t
173  Matrix33<ScalarType> Q;
174  Q.SetIdentity();
175  tempMatrix.ExternalProduct(W,W);
176  Q -= tempMatrix * 2.0f;
177 
178  // compute matrix Q^t M Q
179  Matrix33<ScalarType> QtMQ = (Q.transpose() * M * Q);
180 
181 // CoordType bl = Q.GetColumn(0);
182  CoordType T1 = Q.GetColumn(1);
183  CoordType T2 = Q.GetColumn(2);
184 
185  // find sin and cos for the Givens rotation
186  float s,c;
187  // Gabriel Taubin hint and Valentino Fiorin impementation
188  float alpha = QtMQ[1][1]-QtMQ[2][2];
189  float beta = QtMQ[2][1];
190 
191  float h[2];
192  float delta = sqrtf(4.0f*powf(alpha, 2) +16.0f*powf(beta, 2));
193  h[0] = (2.0f*alpha + delta) / (2.0f*beta);
194  h[1] = (2.0f*alpha - delta) / (2.0f*beta);
195 
196  float t[2];
197  float best_c, best_s;
198  float min_error = std::numeric_limits<ScalarType>::infinity();
199  for (int i=0; i<2; i++)
200  {
201  delta = sqrtf(powf(h[i], 2) + 4.0f);
202  t[0] = (h[i]+delta) / 2.0f;
203  t[1] = (h[i]-delta) / 2.0f;
204 
205  for (int j=0; j<2; j++)
206  {
207  float squared_t = powf(t[j], 2);
208  float denominator = 1.0f + squared_t;
209  s = (2.0f*t[j]) / denominator;
210  c = (1-squared_t) / denominator;
211 
212  float approximation = c*s*alpha + (powf(c, 2) - powf(s, 2))*beta;
213  float angle_similarity = fabs(acosf(c)/asinf(s));
214  float error = fabs(1.0f-angle_similarity)+fabs(approximation);
215  if (error<min_error)
216  {
217  min_error = error;
218  best_c = c;
219  best_s = s;
220  }
221  }
222  }
223  c = best_c;
224  s = best_s;
225 
226  Eigen::Matrix2f minor2x2;
227  Eigen::Matrix2f S;
228 
229 
230  // diagonalize M
231  minor2x2(0,0) = QtMQ[1][1];
232  minor2x2(0,1) = QtMQ[1][2];
233  minor2x2(1,0) = QtMQ[2][1];
234  minor2x2(1,1) = QtMQ[2][2];
235 
236  S(0,0) = S(1,1) = c;
237  S(0,1) = s;
238  S(1,0) = -1.0f * s;
239 
240  Eigen::Matrix2f StMS = S.transpose() * minor2x2 * S;
241 
242  // compute curvatures and curvature directions
243  float Principal_Curvature1 = (3.0f * StMS(0,0)) - StMS(1,1);
244  float Principal_Curvature2 = (3.0f * StMS(1,1)) - StMS(0,0);
245 
246  CoordType Principal_Direction1 = T1 * c - T2 * s;
247  CoordType Principal_Direction2 = T1 * s + T2 * c;
248 
249  (*vi).PD1().Import(Principal_Direction1);
250  (*vi).PD2().Import(Principal_Direction2);
251  (*vi).K1() = Principal_Curvature1;
252  (*vi).K2() = Principal_Curvature2;
253  }
254  }
255  }
256 
257 
258 
259 
260  class AreaData
261  {
262  public:
263  float A;
264  };
265 
273  typedef vcg::GridStaticPtr <FaceType, ScalarType > MeshGridType;
274  typedef vcg::GridStaticPtr <VertexType, ScalarType > PointsGridType;
275 
276  static void PrincipalDirectionsPCA(MeshType &m, ScalarType r, bool pointVSfaceInt = true,vcg::CallBackPos * cb = NULL)
277  {
278  std::vector<VertexType*> closests;
279  std::vector<ScalarType> distances;
280  std::vector<CoordType> points;
281  VertexIterator vi;
282  ScalarType area;
283  MeshType tmpM;
284  typename std::vector<CoordType>::iterator ii;
288 
289  MeshGridType mGrid;
290  PointsGridType pGrid;
291 
292  // Fill the grid used
293  if(pointVSfaceInt)
294  {
295  area = Stat<MeshType>::ComputeMeshArea(m);
296  vcg::tri::SurfaceSampling<MeshType,vcg::tri::TrivialSampler<MeshType> >::Montecarlo(m,vs,1000 * area / (2*M_PI*r*r ));
297  vi = vcg::tri::Allocator<MeshType>::AddVertices(tmpM,m.vert.size());
298  for(size_t y = 0; y < m.vert.size(); ++y,++vi) (*vi).P() = m.vert[y].P();
299  pGrid.Set(tmpM.vert.begin(),tmpM.vert.end());
300  }
301  else
302  {
303  mGrid.Set(m.face.begin(),m.face.end());
304  }
305 
306  int jj = 0;
307  for(vi = m.vert.begin(); vi != m.vert.end(); ++vi)
308  {
309  vcg::Matrix33<ScalarType> A, eigenvectors;
310  vcg::Point3<ScalarType> bp, eigenvalues;
311 // int nrot;
312 
313  // sample the neighborhood
314  if(pointVSfaceInt)
315  {
316  vcg::tri::GetInSphereVertex<
317  MeshType,
318  PointsGridType,std::vector<VertexType*>,
319  std::vector<ScalarType>,
320  std::vector<CoordType> >(tmpM,pGrid, (*vi).cP(),r ,closests,distances,points);
321 
322  A.Covariance(points,bp);
323  A*=area*area/1000;
324  }
325  else{
326  IntersectionBallMesh<MeshType,ScalarType>( m ,vcg::Sphere3<ScalarType>((*vi).cP(),r),tmpM );
327  vcg::Point3<ScalarType> _bary;
329  }
330 
331 // Eigen::Matrix3f AA; AA=A;
332 // Eigen::SelfAdjointEigenSolver<Eigen::Matrix3f> eig(AA);
333 // Eigen::Vector3f c_val = eig.eigenvalues();
334 // Eigen::Matrix3f c_vec = eig.eigenvectors();
335 
336 // Jacobi(A, eigenvalues , eigenvectors, nrot);
337 
338  Eigen::Matrix3d AA;
339  A.ToEigenMatrix(AA);
340  Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> eig(AA);
341  Eigen::Vector3d c_val = eig.eigenvalues();
342  Eigen::Matrix3d c_vec = eig.eigenvectors(); // eigenvector are stored as columns.
343  eigenvectors.FromEigenMatrix(c_vec);
344  eigenvalues.FromEigenVector(c_val);
345 // EV.transposeInPlace();
346 // ev.FromEigenVector(c_val);
347 
348  // get the estimate of curvatures from eigenvalues and eigenvectors
349  // find the 2 most tangent eigenvectors (by finding the one closest to the normal)
350  int best = 0; ScalarType bestv = fabs( (*vi).cN().dot(eigenvectors.GetColumn(0).normalized()) );
351  for(int i = 1 ; i < 3; ++i){
352  ScalarType prod = fabs((*vi).cN().dot(eigenvectors.GetColumn(i).normalized()));
353  if( prod > bestv){bestv = prod; best = i;}
354  }
355 
356  (*vi).PD1().Import(eigenvectors.GetColumn( (best+1)%3).normalized());
357  (*vi).PD2().Import(eigenvectors.GetColumn( (best+2)%3).normalized());
358 
359  // project them to the plane identified by the normal
360  vcg::Matrix33<CurScalarType> rot;
361  CurVecType NN = CurVecType::Construct((*vi).N());
362  CurScalarType angle;
363  angle = acos((*vi).PD1().dot(NN));
364  rot.SetRotateRad( - (M_PI*0.5 - angle),(*vi).PD1()^NN);
365  (*vi).PD1() = rot*(*vi).PD1();
366  angle = acos((*vi).PD2().dot(NN));
367  rot.SetRotateRad( - (M_PI*0.5 - angle),(*vi).PD2()^NN);
368  (*vi).PD2() = rot*(*vi).PD2();
369 
370 
371  // copmutes the curvature values
372  const ScalarType r5 = r*r*r*r*r;
373  const ScalarType r6 = r*r5;
374  (*vi).K1() = (2.0/5.0) * (4.0*M_PI*r5 + 15*eigenvalues[(best+2)%3]-45.0*eigenvalues[(best+1)%3])/(M_PI*r6);
375  (*vi).K2() = (2.0/5.0) * (4.0*M_PI*r5 + 15*eigenvalues[(best+1)%3]-45.0*eigenvalues[(best+2)%3])/(M_PI*r6);
376  if((*vi).K1() < (*vi).K2()) { std::swap((*vi).K1(),(*vi).K2());
377  std::swap((*vi).PD1(),(*vi).PD2());
378  if (cb)
379  {
380  (*cb)(int(100.0f * (float)jj / (float)m.vn),"Vertices Analysis");
381  ++jj;
382  } }
383  }
384 
385 
386  }
387 
389 
400 static void MeanAndGaussian(MeshType & m)
401 {
402  tri::RequireFFAdjacency(m);
403  tri::RequirePerVertexCurvature(m);
404 
405  float area0, area1, area2, angle0, angle1, angle2;
406  FaceIterator fi;
407  VertexIterator vi;
408  typename MeshType::CoordType e01v ,e12v ,e20v;
409 
410  SimpleTempData<VertContainer, AreaData> TDAreaPtr(m.vert);
411  SimpleTempData<VertContainer, typename MeshType::CoordType> TDContr(m.vert);
412 
414  //Compute AreaMix in H (vale anche per K)
415  for(vi=m.vert.begin(); vi!=m.vert.end(); ++vi) if(!(*vi).IsD())
416  {
417  (TDAreaPtr)[*vi].A = 0.0;
418  (TDContr)[*vi] =typename MeshType::CoordType(0.0,0.0,0.0);
419  (*vi).Kh() = 0.0;
420  (*vi).Kg() = (float)(2.0 * M_PI);
421  }
422 
423  for(fi=m.face.begin();fi!=m.face.end();++fi) if( !(*fi).IsD())
424  {
425  // angles
426  angle0 = math::Abs(Angle( (*fi).P(1)-(*fi).P(0),(*fi).P(2)-(*fi).P(0) ));
427  angle1 = math::Abs(Angle( (*fi).P(0)-(*fi).P(1),(*fi).P(2)-(*fi).P(1) ));
428  angle2 = M_PI-(angle0+angle1);
429 
430  if((angle0 < M_PI/2) && (angle1 < M_PI/2) && (angle2 < M_PI/2)) // triangolo non ottuso
431  {
432  float e01 = SquaredDistance( (*fi).V(1)->cP() , (*fi).V(0)->cP() );
433  float e12 = SquaredDistance( (*fi).V(2)->cP() , (*fi).V(1)->cP() );
434  float e20 = SquaredDistance( (*fi).V(0)->cP() , (*fi).V(2)->cP() );
435 
436  area0 = ( e20*(1.0/tan(angle1)) + e01*(1.0/tan(angle2)) ) / 8.0;
437  area1 = ( e01*(1.0/tan(angle2)) + e12*(1.0/tan(angle0)) ) / 8.0;
438  area2 = ( e12*(1.0/tan(angle0)) + e20*(1.0/tan(angle1)) ) / 8.0;
439 
440  (TDAreaPtr)[(*fi).V(0)].A += area0;
441  (TDAreaPtr)[(*fi).V(1)].A += area1;
442  (TDAreaPtr)[(*fi).V(2)].A += area2;
443 
444  }
445  else // obtuse
446  {
447  if(angle0 >= M_PI/2)
448  {
449  (TDAreaPtr)[(*fi).V(0)].A += vcg::DoubleArea<typename MeshType::FaceType>((*fi)) / 4.0;
450  (TDAreaPtr)[(*fi).V(1)].A += vcg::DoubleArea<typename MeshType::FaceType>((*fi)) / 8.0;
451  (TDAreaPtr)[(*fi).V(2)].A += vcg::DoubleArea<typename MeshType::FaceType>((*fi)) / 8.0;
452  }
453  else if(angle1 >= M_PI/2)
454  {
455  (TDAreaPtr)[(*fi).V(0)].A += vcg::DoubleArea<typename MeshType::FaceType>((*fi)) / 8.0;
456  (TDAreaPtr)[(*fi).V(1)].A += vcg::DoubleArea<typename MeshType::FaceType>((*fi)) / 4.0;
457  (TDAreaPtr)[(*fi).V(2)].A += vcg::DoubleArea<typename MeshType::FaceType>((*fi)) / 8.0;
458  }
459  else
460  {
461  (TDAreaPtr)[(*fi).V(0)].A += vcg::DoubleArea<typename MeshType::FaceType>((*fi)) / 8.0;
462  (TDAreaPtr)[(*fi).V(1)].A += vcg::DoubleArea<typename MeshType::FaceType>((*fi)) / 8.0;
463  (TDAreaPtr)[(*fi).V(2)].A += vcg::DoubleArea<typename MeshType::FaceType>((*fi)) / 4.0;
464  }
465  }
466  }
467 
468 
469  for(fi=m.face.begin();fi!=m.face.end();++fi) if( !(*fi).IsD() )
470  {
471  angle0 = math::Abs(Angle( (*fi).P(1)-(*fi).P(0),(*fi).P(2)-(*fi).P(0) ));
472  angle1 = math::Abs(Angle( (*fi).P(0)-(*fi).P(1),(*fi).P(2)-(*fi).P(1) ));
473  angle2 = M_PI-(angle0+angle1);
474 
475  // Skip degenerate triangles.
476  if(angle0==0 || angle1==0 || angle1==0) continue;
477 
478  e01v = ( (*fi).V(1)->cP() - (*fi).V(0)->cP() ) ;
479  e12v = ( (*fi).V(2)->cP() - (*fi).V(1)->cP() ) ;
480  e20v = ( (*fi).V(0)->cP() - (*fi).V(2)->cP() ) ;
481 
482  TDContr[(*fi).V(0)] += ( e20v * (1.0/tan(angle1)) - e01v * (1.0/tan(angle2)) ) / 4.0;
483  TDContr[(*fi).V(1)] += ( e01v * (1.0/tan(angle2)) - e12v * (1.0/tan(angle0)) ) / 4.0;
484  TDContr[(*fi).V(2)] += ( e12v * (1.0/tan(angle0)) - e20v * (1.0/tan(angle1)) ) / 4.0;
485 
486  (*fi).V(0)->Kg() -= angle0;
487  (*fi).V(1)->Kg() -= angle1;
488  (*fi).V(2)->Kg() -= angle2;
489 
490 
491  for(int i=0;i<3;i++)
492  {
493  if(vcg::face::IsBorder((*fi), i))
494  {
495  CoordType e1,e2;
496  vcg::face::Pos<FaceType> hp(&*fi, i, (*fi).V(i));
497  vcg::face::Pos<FaceType> hp1=hp;
498 
499  hp1.FlipV();
500  e1=hp1.v->cP() - hp.v->cP();
501  hp1.FlipV();
502  hp1.NextB();
503  e2=hp1.v->cP() - hp.v->cP();
504  (*fi).V(i)->Kg() -= math::Abs(Angle(e1,e2));
505  }
506  }
507  }
508 
509  for(vi=m.vert.begin(); vi!=m.vert.end(); ++vi) if(!(*vi).IsD() /*&& !(*vi).IsB()*/)
510  {
511  if((TDAreaPtr)[*vi].A<=std::numeric_limits<ScalarType>::epsilon())
512  {
513  (*vi).Kh() = 0;
514  (*vi).Kg() = 0;
515  }
516  else
517  {
518  (*vi).Kh() = (((TDContr)[*vi].dot((*vi).cN())>0)?1.0:-1.0)*((TDContr)[*vi] / (TDAreaPtr) [*vi].A).Norm();
519  (*vi).Kg() /= (TDAreaPtr)[*vi].A;
520  }
521  }
522 }
523 
524 
526 
533  static float ComputeSingleVertexCurvature(VertexPointer v, bool norm = true)
534  {
535  VFIteratorType vfi(v);
536  float A = 0;
537 
538  v->Kh() = 0;
539  v->Kg() = 2 * M_PI;
540 
541  while (!vfi.End()) {
542  if (!vfi.F()->IsD()) {
543  FacePointer f = vfi.F();
544  CoordType nf = TriangleNormal(*f);
545  int i = vfi.I();
546  VertexPointer v0 = f->V0(i), v1 = f->V1(i), v2 = f->V2(i);
547 
548  float ang0 = math::Abs(Angle(v1->P() - v0->P(), v2->P() - v0->P() ));
549  float ang1 = math::Abs(Angle(v0->P() - v1->P(), v2->P() - v1->P() ));
550  float ang2 = M_PI - ang0 - ang1;
551 
552  float s01 = SquaredDistance(v1->P(), v0->P());
553  float s02 = SquaredDistance(v2->P(), v0->P());
554 
555  // voronoi cell of current vertex
556  if (ang0 >= M_PI/2)
557  A += (0.5f * DoubleArea(*f) - (s01 * tan(ang1) + s02 * tan(ang2)) / 8.0 );
558  else if (ang1 >= M_PI/2)
559  A += (s01 * tan(ang0)) / 8.0;
560  else if (ang2 >= M_PI/2)
561  A += (s02 * tan(ang0)) / 8.0;
562  else // non obctuse triangle
563  A += ((s02 / tan(ang1)) + (s01 / tan(ang2))) / 8.0;
564 
565  // gaussian curvature update
566  v->Kg() -= ang0;
567 
568  // mean curvature update
569  ang1 = math::Abs(Angle(nf, v1->N()));
570  ang2 = math::Abs(Angle(nf, v2->N()));
571  v->Kh() += ( (math::Sqrt(s01) / 2.0) * ang1 +
572  (math::Sqrt(s02) / 2.0) * ang2 );
573  }
574 
575  ++vfi;
576  }
577 
578  v->Kh() /= 4.0f;
579 
580  if(norm) {
581  if(A <= std::numeric_limits<float>::epsilon()) {
582  v->Kh() = 0;
583  v->Kg() = 0;
584  }
585  else {
586  v->Kh() /= A;
587  v->Kg() /= A;
588  }
589  }
590 
591  return A;
592  }
593 
594  static void PerVertex(MeshType & m)
595  {
596  tri::RequireVFAdjacency(m);
597 
598  for(VertexIterator vi = m.vert.begin(); vi != m.vert.end(); ++vi)
599  ComputeSingleVertexCurvature(&*vi,false);
600  }
601 
602 
603 
604 /*
605  Compute principal curvature directions and value with normal cycle:
606  @inproceedings{CohMor03,
607  author = {Cohen-Steiner, David and Morvan, Jean-Marie },
608  booktitle = {SCG '03: Proceedings of the nineteenth annual symposium on Computational geometry},
609  title - {Restricted delaunay triangulations and normal cycle}
610  year = {2003}
611 }
612  */
613 
614  static void PrincipalDirectionsNormalCycle(MeshType & m){
615  tri::RequireVFAdjacency(m);
616  tri::RequireFFAdjacency(m);
617 
618  typename MeshType::VertexIterator vi;
619 
620  for(vi = m.vert.begin(); vi != m.vert.end(); ++vi)
621  if(!((*vi).IsD())){
622  vcg::Matrix33<ScalarType> m33;m33.SetZero();
623  face::JumpingPos<typename MeshType::FaceType> p((*vi).VFp(),&(*vi));
624  p.FlipE();
625  typename MeshType::VertexType * firstv = p.VFlip();
626  assert(p.F()->V(p.VInd())==&(*vi));
627 
628  do{
629  if( p.F() != p.FFlip()){
630  Point3<ScalarType> normalized_edge = p.F()->V(p.F()->Next(p.VInd()))->cP() - (*vi).P();
631  ScalarType edge_length = normalized_edge.Norm();
632  normalized_edge/=edge_length;
633  Point3<ScalarType> n1 = NormalizedTriangleNormal(*(p.F()));
634  Point3<ScalarType> n2 = NormalizedTriangleNormal(*(p.FFlip()));
635  ScalarType n1n2 = (n1 ^ n2).dot(normalized_edge);
636  n1n2 = std::max(std::min( ScalarType(1.0),n1n2),ScalarType(-1.0));
637  ScalarType beta = math::Asin(n1n2);
638  m33[0][0] += beta*edge_length*normalized_edge[0]*normalized_edge[0];
639  m33[0][1] += beta*edge_length*normalized_edge[1]*normalized_edge[0];
640  m33[1][1] += beta*edge_length*normalized_edge[1]*normalized_edge[1];
641  m33[0][2] += beta*edge_length*normalized_edge[2]*normalized_edge[0];
642  m33[1][2] += beta*edge_length*normalized_edge[2]*normalized_edge[1];
643  m33[2][2] += beta*edge_length*normalized_edge[2]*normalized_edge[2];
644  }
645  p.NextFE();
646  }while(firstv != p.VFlip());
647 
648  if(m33.Determinant()==0.0){ // degenerate case
649  (*vi).K1() = (*vi).K2() = 0.0; continue;}
650 
651  m33[1][0] = m33[0][1];
652  m33[2][0] = m33[0][2];
653  m33[2][1] = m33[1][2];
654 
655  Eigen::Matrix3d it;
656  m33.ToEigenMatrix(it);
657  Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> eig(it);
658  Eigen::Vector3d c_val = eig.eigenvalues();
659  Eigen::Matrix3d c_vec = eig.eigenvectors();
660 
661  Point3<ScalarType> lambda;
662  Matrix33<ScalarType> vect;
663  vect.FromEigenMatrix(c_vec);
664  lambda.FromEigenVector(c_val);
665 
666  ScalarType bestNormal = 0;
667  int bestNormalIndex = -1;
668  for(int i = 0; i < 3; ++i)
669  {
670  float agreeWithNormal = fabs((*vi).N().Normalize().dot(vect.GetColumn(i)));
671  if( agreeWithNormal > bestNormal )
672  {
673  bestNormal= agreeWithNormal;
674  bestNormalIndex = i;
675  }
676  }
677  int maxI = (bestNormalIndex+2)%3;
678  int minI = (bestNormalIndex+1)%3;
679  if(fabs(lambda[maxI]) < fabs(lambda[minI])) std::swap(maxI,minI);
680 
681  (*vi).PD1().Import(vect.GetColumn(maxI));
682  (*vi).PD2().Import(vect.GetColumn(minI));
683  (*vi).K1() = lambda[2];
684  (*vi).K2() = lambda[1];
685  }
686  }
687 
688  static void PerVertexBasicRadialCrossField(MeshType &m, float anisotropyRatio = 1.0 )
689  {
690  tri::RequirePerVertexCurvatureDir(m);
691  CoordType c=m.bbox.Center();
692  float maxRad = m.bbox.Diag()/2.0f;
693 
694  for(size_t i=0;i<m.vert.size();++i) {
695  CoordType dd = m.vert[i].P()-c;
696  dd.Normalize();
697  m.vert[i].PD1().Import(dd^m.vert[i].N());
698  m.vert[i].PD1().Normalize();
699  m.vert[i].PD2().Import(m.vert[i].N()^CoordType::Construct(m.vert[i].PD1()));
700  m.vert[i].PD2().Normalize();
701  // Now the anisotropy
702  // the idea is that the ratio between the two direction is at most <anisotropyRatio>
703  // eg |PD1|/|PD2| < ratio
704  // and |PD1|^2 + |PD2|^2 == 1
705 
706  float q =Distance(m.vert[i].P(),c) / maxRad; // it is in the 0..1 range
707  const float minRatio = 1.0f/anisotropyRatio;
708  const float maxRatio = anisotropyRatio;
709  const float curRatio = minRatio + (maxRatio-minRatio)*q;
710  float pd1Len = sqrt(1.0/(1+curRatio*curRatio));
711  float pd2Len = curRatio * pd1Len;
712 // assert(fabs(curRatio - pd2Len/pd1Len)<0.0000001);
713 // assert(fabs(pd1Len*pd1Len + pd2Len*pd2Len - 1.0f)<0.0001);
714  m.vert[i].PD1() *= pd1Len;
715  m.vert[i].PD2() *= pd2Len;
716  }
717  }
718 
719 };
720 
721 } // end namespace tri
722 } // end namespace vcg
723 #endif