From c091d76a9615259e015be8ce6852d97333ec1f95 Mon Sep 17 00:00:00 2001
From: Emilie Marchandise <emilie.marchandise@uclouvain.be>
Date: Thu, 16 Feb 2012 21:09:50 +0000
Subject: [PATCH] added MEANPLANE for harmonic map, works well :-)

---
 Geo/GFace.h              |   8 +--
 Geo/GFaceCompound.cpp    |  35 ++++++++++-
 Geo/GFaceCompound.h      |   5 +-
 Geo/SVector3.h           |  58 ++++++++++++++++++
 Mesh/BackgroundMesh.cpp  |   6 +-
 Mesh/CenterlineField.cpp |  54 +++-------------
 Numeric/Numeric.cpp      | 129 +++++++++++++++++++++++++++++++++++++++
 Numeric/Numeric.h        |  14 +++++
 8 files changed, 245 insertions(+), 64 deletions(-)

diff --git a/Geo/GFace.h b/Geo/GFace.h
index fabe4085a1..0d430ff287 100644
--- a/Geo/GFace.h
+++ b/Geo/GFace.h
@@ -16,6 +16,7 @@
 #include "SPoint2.h"
 #include "SVector3.h"
 #include "Pair.h"
+#include "Numeric.h"
 
 class MElement;
 class MTriangle;
@@ -24,13 +25,6 @@ class MPolygon;
 class ExtrudeParams;
 class GFaceCompound;
 
-struct mean_plane
-{
-  double plan[3][3];
-  double a, b, c, d;
-  double x, y, z;
-};
-
 struct surface_params
 {
   double radius, radius2, height, cx, cy, cz;
diff --git a/Geo/GFaceCompound.cpp b/Geo/GFaceCompound.cpp
index e88a83ff0c..a0806068da 100644
--- a/Geo/GFaceCompound.cpp
+++ b/Geo/GFaceCompound.cpp
@@ -41,6 +41,7 @@
 #include "GRbf.h"
 #include "Curvature.h"
 #include "MPoint.h"
+#include "Numeric.h"
 
 static void fixEdgeToValue(GEdge *ed, double value, dofManager<double> &myAssembler)
 {
@@ -210,7 +211,7 @@ static void computeCGKernelPolygon(std::map<MVertex*,SPoint3> &coordinates,
     vcg/=nbFinal;
   }
   else{
-    Msg::Debug("----> No Kernel for polygon: place point at CG polygon");
+    //Msg::Debug("----> No Kernel for polygon: place point at CG polygon");
     //place at CG polygon
     for(std::vector<MVertex*>::iterator it = cavV.begin() ; it != cavV.end() ; ++it){
       SPoint3 vsp = coordinates[*it];
@@ -644,10 +645,13 @@ bool GFaceCompound::parametrize() const
     
   // Laplace parametrization
   if (_mapping == HARMONIC){
-    Msg::Debug("Parametrizing surface %d with 'harmonic map'", tag()); 
+    Msg::Info("Parametrizing surface %d with 'harmonic map'", tag()); 
     fillNeumannBCS();
     parametrize(ITERU,HARMONIC); 
     parametrize(ITERV,HARMONIC);
+    printStuff(111);
+    checkOrientation(0, true);
+    printStuff(222);
   }
   // Multiscale Laplace parametrization
   else if (_mapping == MULTISCALE){
@@ -660,7 +664,7 @@ bool GFaceCompound::parametrize() const
   }
   // Conformal map parametrization
   else if (_mapping == CONFORMAL){
-    Msg::Debug("Parametrizing surface %d with 'conformal map'", tag());
+    Msg::Info("Parametrizing surface %d with 'conformal map'", tag());
     fillNeumannBCS();
     std::vector<MVertex *> vert;
     bool oriented, hasOverlap;
@@ -971,6 +975,7 @@ GFaceCompound::GFaceCompound(GModel *m, int tag, std::list<GFace*> &compound,
   
   getBoundingEdges();
   _type = UNITCIRCLE;
+  //_type = MEANPLANE;
  
   nbSplit = 0;
   fillTris.clear();
@@ -1096,6 +1101,30 @@ void GFaceCompound::parametrize(iterationStep step, typeOfMapping tom) const
       else if(step == ITERV) myAssembler.fixVertex(v, 0, 1, sin(theta));    
     }
   }
+  else if (_type == MEANPLANE){
+
+  std::vector<SPoint3> points, pointsProj, pointsUV;
+  SPoint3 ptCG(0.0, 0.0, 0.0);
+  for(unsigned int i = 0; i < _ordered.size(); i++){
+      MVertex *v = _ordered[i];
+      points.push_back(SPoint3(v->x(), v->y(), v->z()));
+      ptCG[0] += v->x();
+      ptCG[1] += v->y();
+      ptCG[2] += v->z();
+    }
+    ptCG /= points.size();
+    mean_plane meanPlane;
+    computeMeanPlaneSimple(points, meanPlane);
+    projectPointsToPlane(points, pointsProj, meanPlane);
+    transformPointsIntoOrthoBasis(pointsProj, pointsUV, ptCG, meanPlane);
+
+    for(unsigned int i = 0; i < pointsUV.size(); i++){
+      MVertex *v = _ordered[i];
+      if(step == ITERU) myAssembler.fixVertex(v, 0, 1, pointsUV[i][0]);
+      else if(step == ITERV) myAssembler.fixVertex(v, 0, 1, pointsUV[i][1]);    
+    }
+
+  }
   else{
     Msg::Error("Unknown type of parametrization");
     return;
diff --git a/Geo/GFaceCompound.h b/Geo/GFaceCompound.h
index 41a239b8f3..6f92c9691d 100644
--- a/Geo/GFaceCompound.h
+++ b/Geo/GFaceCompound.h
@@ -57,7 +57,7 @@ class GFaceCompound : public GFace {
  public:
   typedef enum {ITERU=0,ITERV=1,ITERD=2} iterationStep;
   typedef enum {HARMONIC=1,CONFORMAL=2, CONVEXCOMBINATION=3, MULTISCALE=4, RBF=5} typeOfMapping;
-  typedef enum {UNITCIRCLE, SQUARE} typeOfIsomorphism;
+  typedef enum {UNITCIRCLE, MEANPLANE} typeOfIsomorphism;
   void computeNormals(std::map<MVertex*, SVector3> &normals) const;
  protected:
   mutable std::set<MVertex *> ov;
@@ -134,6 +134,7 @@ class GFaceCompound : public GFace {
   mutable int nbSplit;
   int getNbSplit() const { return nbSplit; }
   int allowPartition() const{ return _allowPartition; }
+  void setType(typeOfIsomorphism type){ _type=type;}
  private:
   typeOfIsomorphism _type;
   mutable typeOfMapping _mapping;
@@ -147,7 +148,7 @@ class GFaceCompound : public GFace {
  public:
   typedef enum {ITERU=0,ITERV=1,ITERD=2} iterationStep;
   typedef enum {HARMONIC=1,CONFORMAL=2, CONVEXCOMBINATION=3, MULTISCALE=4, RBF=5} typeOfMapping;
-  typedef enum {UNITCIRCLE, SQUARE} typeOfIsomorphism;
+  typedef enum {UNITCIRCLE, MEANPLANE} typeOfIsomorphism;
  GFaceCompound(GModel *m, int tag, std::list<GFace*> &compound,
 	       std::list<GEdge*> &U0, typeOfMapping typ = HARMONIC, int allowPartition=1)
     : GFace(m, tag)
diff --git a/Geo/SVector3.h b/Geo/SVector3.h
index 54ca826d80..db3c6e86d2 100644
--- a/Geo/SVector3.h
+++ b/Geo/SVector3.h
@@ -9,6 +9,7 @@
 #include "SPoint3.h"
 #include <string>
 #include <stdio.h>
+#include "GmshMessage.h"
 
 // concrete class for vector of size 3
 class SVector3 {
@@ -71,6 +72,7 @@ class SVector3 {
 
   // Needed to allow the initialization of a SPoint3 from a SPoint3, a distance and a direction
   SPoint3 point() const{return P;}
+
 };
 
 inline double dot(const SVector3 &a, const SVector3 &b)
@@ -102,4 +104,60 @@ inline SVector3 operator+(const SVector3 &a,const SVector3 &b)
 inline SVector3 operator-(const SVector3 &a,const SVector3 &b)
 { return SVector3(a[0] - b[0], a[1] - b[1], a[2] - b[2]); }
 
+inline void buildOrthoBasis_naive(SVector3 &dir, SVector3 &dir1, SVector3 &dir2)
+{
+  dir.normalize();
+  if (dir[1]!=0.0 && dir[2]!=0.0){
+     dir1 = SVector3(1.0, 0.0, -dir[0]/dir[2]);
+     dir2 = SVector3 (dir[0]/dir[2], -(dir[0]*dir[0]+dir[2]*dir[2])/(dir[1]*dir[2]), 1.0);
+   }
+   else if (dir[0]!=0.0 && dir[2]!=0.0){
+     dir1 = SVector3(-dir[1]/dir[0], 1.0, 0.0);
+     dir2 = SVector3(1.0, dir[1]/dir[0], -(dir[1]*dir[1]+dir[0]*dir[0])/(dir[0]*dir[2]));
+   }
+   else if (dir[0]!=0.0 && dir[1]!=0.0){
+     dir1 = SVector3(0.0, -dir[2]/dir[1], 1.0);
+     dir2 = SVector3(-(dir[1]*dir[1]+dir[2]*dir[2])/(dir[0]*dir[1]), 1.0, dir[2]/dir[1]);
+   }
+   else if (dir[0]==0.0 && dir[1]==0.0){
+     dir1 = SVector3(0.0, 1.0, 0.0);
+     dir2 = SVector3(1.0, 0.0, 0.0);
+   }
+   else if (dir[1]==0.0 && dir[2]==0.0){
+     dir1 = SVector3(0.0, 1.0, 0.0);
+     dir2 = SVector3(0.0, 0.0, 1.0);
+   }
+   else if (dir[0]==0.0 && dir[2]==0.0){
+     dir1 = SVector3(1.0, 0.0, 0.0);
+     dir2 = SVector3(0.0, 0.0, 1.0);
+   }
+   else{
+       Msg::Error("Problem with computing orthoBasis");
+       Msg::Exit(1);
+   }
+   // printf("XYZ =%g %g %g r=%g dir0 = %g %g %g dir1 = %g %g %g dir2 =%g %g %g\n", 
+   // 	  x,y,z,d, dir[0], dir[1], dir[2], dir1[0], dir1[1], dir1[2],  dir2[0], dir2[1], dir2[2] );
+   // printf("0x1 =%g 1x2=%g 2x1=%g \n", dot(dir, dir1), dot(dir1,dir2), dot(dir2,dir));
+   dir1.normalize();
+   dir2.normalize();
+}
+
+inline void buildOrthoBasis(SVector3 &normal, SVector3 &tangent, SVector3 &binormal)
+{
+  //pick any Unit-Vector that's not parallel to normal:
+  normal.normalize();
+  if (normal[0] > normal[1] )
+    tangent = SVector3(0.0, 1.0, 0.0);
+  else
+    tangent = SVector3(1.0, 0.0, 0.0);
+
+  //build a binormal from tangent and normal:
+  binormal = crossprod(tangent, normal);
+  binormal.normalize();
+  
+  //and correct the tangent from the binormal and the normal.
+  tangent = crossprod(normal, binormal);
+  tangent.normalize();
+}
+
 #endif
diff --git a/Mesh/BackgroundMesh.cpp b/Mesh/BackgroundMesh.cpp
index fc8fffdf66..ce0cd09f6f 100644
--- a/Mesh/BackgroundMesh.cpp
+++ b/Mesh/BackgroundMesh.cpp
@@ -246,17 +246,13 @@ static double LC_MVertex_CURV(GEntity *ge, double U, double V)
     }
     break;
   }
-  
   double lc = Crv > 0 ? 2 * M_PI / Crv / CTX::instance()->mesh.minCircPoints : MAX_LC;
   return lc;
 }
 
 static SMetric3 LC_MVertex_CURV_ANISO(GEntity *ge, double U, double V)
 {
-  //std::cout << "I'm in LC_MVertex_CURV_ANISO" << std::endl;
-  //std::cout << "The dimension of the entity is: "<< ge->dim() << std::endl;
   switch(ge->dim()){
-  //std::cout << "The dimension of the entity is: "<< ge->dim() << std::endl;
   case 0: return metric_based_on_surface_curvature((const GVertex *)ge);
   case 1: return metric_based_on_surface_curvature((const GEdge *)ge, U);
   case 2: return metric_based_on_surface_curvature((const GFace *)ge, U, V);
@@ -397,7 +393,7 @@ SMetric3 BGM_MeshMetric(GEntity *ge,
   
   SMetric3 LC(1./(lc*lc));
   SMetric3 m = intersection(intersection (l4, LC),l3);
-  //  printf("%g %g %g %g %g %g\n",m(0,0),m(1,1),m(2,2),m(0,1),m(0,2),m(1,2));
+  //printf("%g %g %g %g %g %g\n",m(0,0),m(1,1),m(2,2),m(0,1),m(0,2),m(1,2));
   return m;
   //  return lc * CTX::instance()->mesh.lcFactor;
 }
diff --git a/Mesh/CenterlineField.cpp b/Mesh/CenterlineField.cpp
index edc5a94871..070c25392c 100644
--- a/Mesh/CenterlineField.cpp
+++ b/Mesh/CenterlineField.cpp
@@ -698,20 +698,14 @@ void Centerline::createSplitCompounds(){
     int num_gfc = NF+i+1;   
     Msg::Info("Parametrize Compound Surface (%d) = %d discrete face",
               num_gfc, pf->tag());
-    GFaceCompound::typeOfMapping typ = GFaceCompound::CONFORMAL;
+    //GFaceCompound::typeOfMapping typ = GFaceCompound::HARMONIC; 
+    GFaceCompound::typeOfMapping typ = GFaceCompound::CONFORMAL; 
     GFaceCompound *gfc = new GFaceCompound(current, num_gfc, f_compound, U0,
 					   typ, 0);
     gfc->meshAttributes.recombine = recombine;
     gfc->addPhysicalEntity(i+1);
     current->add(gfc);
-    //gfc->parametrize();
-    // for(unsigned int j = 0; j < pf->triangles.size(); ++j){
-    //   MTriangle *t = pf->triangles[j];
-    //   for(int k = 0; k < 3; k++){
-    // 	MVertex *v = t->getVertex(k);
-    // 	v->setEntity(pf);
-    //   }
-    //}
+
   }
 
 }
@@ -867,7 +861,7 @@ void Centerline::cutMesh(){
   for(unsigned int i = 0; i < edges.size(); i++){
     std::vector<MLine*> lines = edges[i].lines;
     double L = edges[i].length;
-    double R = edges[i].minRad;
+    double R = 0.5*(edges[i].minRad+edges[i].maxRad);
     double AR = L/R;
     printf("*** branch =%d \n", i, AR);
     if( AR > 4.5){
@@ -1044,48 +1038,14 @@ void  Centerline::operator() (double x, double y, double z, SMetric3 &metr, GEnt
    double lc = 2*M_PI*d/nbPoints; 
    SVector3  p0(nodes[index2[0]][0], nodes[index2[0]][1], nodes[index2[0]][2]);
    SVector3  p1(nodes[index2[1]][0], nodes[index2[1]][1], nodes[index2[1]][2]);
-   SVector3 dir = p1-p0;
-   dir.normalize();
+   SVector3 dir0 = p1-p0;
    SVector3 dir1, dir2;
-   if (dir[1]!=0.0 && dir[2]!=0.0){
-     dir1 = SVector3(1.0, 0.0, -dir[0]/dir[2]);
-     dir2 = SVector3 (dir[0]/dir[2], -(dir[0]*dir[0]+dir[2]*dir[2])/(dir[1]*dir[2]), 1.0);
-   }
-   else if (dir[0]!=0.0 && dir[2]!=0.0){
-     dir1 = SVector3(-dir[1]/dir[0], 1.0, 0.0);
-     dir2 = SVector3(1.0, dir[1]/dir[0], -(dir[1]*dir[1]+dir[0]*dir[0])/(dir[0]*dir[2]));
-   }
-   else if (dir[0]!=0.0 && dir[1]!=0.0){
-     dir1 = SVector3(0.0, -dir[2]/dir[1], 1.0);
-     dir2 = SVector3(-(dir[1]*dir[1]+dir[2]*dir[2])/(dir[0]*dir[1]), 1.0, dir[2]/dir[1]);
-   }
-   else if (dir[0]==0.0 && dir[1]==0.0){
-     dir1 = SVector3(0.0, 1.0, 0.0);
-     dir2 = SVector3(1.0, 0.0, 0.0);
-   }
-   else if (dir[1]==0.0 && dir[2]==0.0){
-     dir1 = SVector3(0.0, 1.0, 0.0);
-     dir2 = SVector3(0.0, 0.0, 1.0);
-   }
-   else if (dir[0]==0.0 && dir[2]==0.0){
-     dir1 = SVector3(1.0, 0.0, 0.0);
-     dir2 = SVector3(0.0, 0.0, 1.0);
-   }
-   else {printf("ARGHH EMI DO STHG \n"); exit(1);}
-   // printf("XYZ =%g %g %g r=%g dir0 = %g %g %g dir1 = %g %g %g dir2 =%g %g %g\n", 
-   // 	  x,y,z,d, dir[0], dir[1], dir[2], dir1[0], dir1[1], dir1[2],  dir2[0], dir2[1], dir2[2] );
-   // printf("0x1 =%g 1x2=%g 2x1=%g \n", dot(dir, dir1), dot(dir1,dir2), dot(dir2,dir));
-   dir1.normalize();
-   dir2.normalize();
-   
-   //dir = SVector3(1.0, 0.0, 0.0);
-   //dir1 = SVector3(0.0, 1.0, 0.0);
-   //dir2 = SVector3(0.0, 0.0, 1.0);
+   buildOrthoBasis(dir0,dir1,dir2);
    
    double lcA = 4.*lc;
    double lam1 = 1./(lcA*lcA);
    double lam2 = 1./(lc*lc);
-   metr = SMetric3(lam1,lam2,lam2, dir, dir1, dir2);
+   metr = SMetric3(lam1,lam2,lam2, dir0, dir1, dir2);
 
    delete[]index2;
    delete[]dist2; 
diff --git a/Numeric/Numeric.cpp b/Numeric/Numeric.cpp
index 18192ca5a5..f1eabcea63 100644
--- a/Numeric/Numeric.cpp
+++ b/Numeric/Numeric.cpp
@@ -1226,3 +1226,132 @@ int intersection_segments(SPoint3 &p1, SPoint3 &p2,
 	    x[1] >= 0.0 && x[1] <= 1.);
   }
 }
+
+void computeMeanPlaneSimple(const std::vector<SPoint3> &points, mean_plane &meanPlane)
+{
+
+  double xm = 0., ym = 0., zm = 0.;
+  int ndata = points.size();
+  int na = 3;
+  for(int i = 0; i < ndata; i++) {
+    xm += points[i].x();
+    ym += points[i].y();
+    zm += points[i].z();
+  }
+  xm /= (double)ndata;
+  ym /= (double)ndata;
+  zm /= (double)ndata;
+
+  fullMatrix<double> U(ndata, na), V(na, na);
+  fullVector<double> sigma(na);
+  for(int i = 0; i < ndata; i++) {
+    U(i, 0) = points[i].x() - xm;
+    U(i, 1) = points[i].y() - ym;
+    U(i, 2) = points[i].z() - zm;
+  }
+  U.svd(V, sigma);
+  double res[4], svd[3];
+  svd[0] = sigma(0);
+  svd[1] = sigma(1);
+  svd[2] = sigma(2);
+  int min;
+  if(fabs(svd[0]) < fabs(svd[1]) && fabs(svd[0]) < fabs(svd[2]))
+    min = 0;
+  else if(fabs(svd[1]) < fabs(svd[0]) && fabs(svd[1]) < fabs(svd[2]))
+    min = 1;
+  else
+    min = 2;
+  res[0] = V(0, min);
+  res[1] = V(1, min);
+  res[2] = V(2, min);
+  norme(res);
+
+  double ex[3], t1[3], t2[3];
+
+  ex[0] = ex[1] = ex[2] = 0.0;
+  if(res[0] == 0.)
+    ex[0] = 1.0;
+  else if(res[1] == 0.)
+    ex[1] = 1.0;
+  else
+    ex[2] = 1.0;
+
+  prodve(res, ex, t1);
+  norme(t1);
+  prodve(t1, res, t2);
+  norme(t2);
+
+  res[3] = (xm * res[0] + ym * res[1] + zm * res[2]);
+
+  for(int i = 0; i < 3; i++)
+    meanPlane.plan[0][i] = t1[i];
+  for(int i = 0; i < 3; i++)
+    meanPlane.plan[1][i] = t2[i];
+  for(int i = 0; i < 3; i++)
+    meanPlane.plan[2][i] = res[i];
+
+  meanPlane.a = res[0];
+  meanPlane.b = res[1];
+  meanPlane.c = res[2];
+  meanPlane.d = -res[3];//BUG HERE 
+
+  meanPlane.x = meanPlane.y = meanPlane.z = 0.;
+  if(fabs(meanPlane.a) >= fabs(meanPlane.b) &&
+     fabs(meanPlane.a) >= fabs(meanPlane.c) ){
+    meanPlane.x = meanPlane.d / meanPlane.a;
+  }
+  else if(fabs(meanPlane.b) >= fabs(meanPlane.a) &&
+          fabs(meanPlane.b) >= fabs(meanPlane.c)){
+    meanPlane.y = meanPlane.d / meanPlane.b;
+  }
+  else{
+    meanPlane.z = meanPlane.d / meanPlane.c;
+  }
+
+  }
+
+void projectPointToPlane(const SPoint3 &pt, SPoint3 &ptProj, const mean_plane &meanPlane)
+{
+
+  double u  = pt.x();
+  double v  = pt.y();
+  double w  = pt.z();
+  double a = meanPlane.a;
+  double b = meanPlane.b;
+  double c = meanPlane.c;
+  double d = meanPlane.d;
+  double t0 = -(a*u+b*v+c*w+d)/(a*a+b*b+c*c);
+
+  ptProj[0] =  u + a*t0;
+  ptProj[1] =  v + b*t0;
+  ptProj[2] =  w + c*t0;
+  
+}
+
+void projectPointsToPlane(const std::vector<SPoint3> &pts, std::vector<SPoint3> &ptsProj, const mean_plane &meanPlane)
+{
+
+  ptsProj.resize(pts.size());
+  for (int i= 0; i< pts.size(); i++){
+    projectPointToPlane(pts[i],ptsProj[i], meanPlane); 
+  }
+
+}
+
+void transformPointsIntoOrthoBasis(const std::vector<SPoint3> &ptsProj,  std::vector<SPoint3> &pointsUV, 
+				   const SPoint3 &ptCG, const mean_plane &meanPlane)
+{
+
+  pointsUV.resize(ptsProj.size());
+  SVector3 normal(meanPlane.a, meanPlane.b, meanPlane.c);
+  SVector3 tangent, binormal;
+  buildOrthoBasis(normal, tangent, binormal);
+
+  for (int i= 0; i< ptsProj.size(); i++){
+    SVector3 pp(ptsProj[i][0]-ptCG[0],ptsProj[i][1]-ptCG[1],ptsProj[i][2]-ptCG[2]) ;
+    pointsUV[i][0] = dot(pp, tangent); 
+    pointsUV[i][1] = dot(pp, binormal); 
+    pointsUV[i][2] = dot(pp, normal); 
+  }
+  
+}
diff --git a/Numeric/Numeric.h b/Numeric/Numeric.h
index b066b30a05..10ea14891b 100644
--- a/Numeric/Numeric.h
+++ b/Numeric/Numeric.h
@@ -15,6 +15,13 @@
 #define myhypot(a,b) (sqrt((a)*(a)+(b)*(b)))
 #define sign(x)      (((x)>=0)?1:-1)
 
+struct mean_plane
+{
+  double plan[3][3];
+  double a, b, c, d;
+  double x, y, z;
+};
+
 double myatan2(double a, double b);
 double myasin(double a);
 double myacos(double a);
@@ -128,4 +135,11 @@ int intersection_segments (SPoint3 &p1, SPoint3 &p2,
 			   SPoint3 &q1, SPoint3 &q2, 
 			   double x[2]);
 
+//tools for projection onto plane
+void computeMeanPlaneSimple(const std::vector<SPoint3> &points, mean_plane &meanPlane); 
+void projectPointToPlane(const SPoint3 &pt, SPoint3 &ptProj, const mean_plane &meanPlane);
+void projectPointsToPlane(const std::vector<SPoint3> &pts, std::vector<SPoint3> &ptsProj, const mean_plane &meanPlane);
+void transformPointsIntoOrthoBasis(const std::vector<SPoint3> &ptsProj,  std::vector<SPoint3> &pointsUV, 
+				   const SPoint3 &ptCG, const mean_plane &meanPlane);
+
 #endif
-- 
GitLab