From feae43c6fec35f85a96b76c4538b86e6210397dd Mon Sep 17 00:00:00 2001
From: Jean-Francois Remacle <jean-francois.remacle@uclouvain.be>
Date: Tue, 11 Feb 2014 10:41:21 +0000
Subject: [PATCH] 3D Delaunnay-ization of a set of points is now fully gmsh
 based

---
 Mesh/meshGRegionDelaunayInsertion.cpp | 159 +++++++++++++++-
 Mesh/meshGRegionDelaunayInsertion.h   |   2 +
 Numeric/CMakeLists.txt                |   2 +
 Numeric/HilbertCurve.cpp              | 264 ++++++++++++++++++++++++++
 Numeric/HilbertCurve.h                |   4 +
 5 files changed, 422 insertions(+), 9 deletions(-)
 create mode 100644 Numeric/HilbertCurve.cpp
 create mode 100644 Numeric/HilbertCurve.h

diff --git a/Mesh/meshGRegionDelaunayInsertion.cpp b/Mesh/meshGRegionDelaunayInsertion.cpp
index c34c4a8e0f..481854e1ba 100644
--- a/Mesh/meshGRegionDelaunayInsertion.cpp
+++ b/Mesh/meshGRegionDelaunayInsertion.cpp
@@ -18,6 +18,7 @@
 #include "MTriangle.h"
 #include "Numeric.h"
 #include "Context.h"
+#include "HilbertCurve.h"
 
 int MTet4::radiusNorm = 2;
 static double LIMIT_ = 1;
@@ -330,15 +331,6 @@ void nonrecurFindCavity(std::list<faceXtet> & shell,
                      MVertex *v ,
                      MTet4 *t)
 {
-  // Msg::Info("tet %d %d %d %d",t->tet()->getVertex(0)->getNum(),
-  //     t->tet()->getVertex(1)->getNum(),
-  //     t->tet()->getVertex(2)->getNum(),
-  //     t->tet()->getVertex(3)->getNum());
-
-  // invariant : this one has to be inserted in the cavity
-  // consider this tet deleted
-  // remove its reference to its neighbors
-
   std::stack<MTet4*> _stack;
   _stack.push(t);
   while(!_stack.empty()){
@@ -1612,3 +1604,152 @@ void bowyerWatsonFrontalLayers(GRegion *gr, bool hex)
   MTet4::radiusNorm = 2;
   LIMIT_ = 1;
 }
+
+
+///// do a 3D delaunay mesh assuming a set of vertices
+
+static void initialCube (std::vector<MVertex*> &v,
+			 MVertex *box[8],
+			 std::vector<MTet4*> &t){
+  SBoundingBox3d bbox ;
+  for (size_t i=0;i<v.size();i++){
+    MVertex *pv = v[i];
+    bbox += SPoint3(pv->x(),pv->y(),pv->z());    
+  }
+  bbox *= 1.3;
+  box[0] = new MVertex (bbox.min().x(),bbox.min().y(),bbox.min().z());
+  box[1] = new MVertex (bbox.max().x(),bbox.min().y(),bbox.min().z());
+  box[2] = new MVertex (bbox.max().x(),bbox.max().y(),bbox.min().z());
+  box[3] = new MVertex (bbox.min().x(),bbox.max().y(),bbox.min().z());
+  box[4] = new MVertex (bbox.min().x(),bbox.min().y(),bbox.max().z());
+  box[5] = new MVertex (bbox.max().x(),bbox.min().y(),bbox.max().z());
+  box[6] = new MVertex (bbox.max().x(),bbox.max().y(),bbox.max().z());
+  box[7] = new MVertex (bbox.min().x(),bbox.max().y(),bbox.max().z());
+  std::vector<MTetrahedron*> t_box;
+  MTetrahedron *t0 = new MTetrahedron (box[2],box[7],box[3],box[1]);
+  MTetrahedron *t1 = new MTetrahedron (box[0],box[7],box[1],box[3]);
+  MTetrahedron *t2 = new MTetrahedron (box[6],box[1],box[7],box[2]);
+  MTetrahedron *t3 = new MTetrahedron (box[0],box[1],box[7],box[4]);
+  MTetrahedron *t4 = new MTetrahedron (box[1],box[4],box[5],box[7]);
+  MTetrahedron *t5 = new MTetrahedron (box[1],box[7],box[5],box[6]);
+  t.push_back(new MTet4(t0,0.0));
+  t.push_back(new MTet4(t1,0.0));
+  t.push_back(new MTet4(t2,0.0));
+  t.push_back(new MTet4(t3,0.0));
+  t.push_back(new MTet4(t4,0.0));
+  t.push_back(new MTet4(t5,0.0));
+  connectTets(t);
+}
+
+int intersect_line_triangle(double X[3], double Y[3], double Z[3] ,
+                            double P[3], double N[3], double);
+
+static MTet4* search4Tet (MTet4 *t, MVertex *v, int _size) {
+  if (t->inCircumSphere(v)) return t;
+  int ITER = 0;
+  SPoint3 p2 (v->x(),v->y(),v->z());
+  while (1){
+    SPoint3 p1 = t->tet()->barycenter();
+    int found = -1;
+    for (int i = 0; i < 4; i++){
+      MTet4 *neigh = t->getNeigh(i);
+      if (neigh){
+	faceXtet fxt (t, i);
+	double X[3] = {fxt.v[0]->x(),fxt.v[1]->x(),fxt.v[2]->x()};
+	double Y[3] = {fxt.v[0]->y(),fxt.v[1]->y(),fxt.v[2]->y()};
+	double Z[3] = {fxt.v[0]->z(),fxt.v[1]->z(),fxt.v[2]->z()};
+	//	printf("%d %d\n",i,intersect_line_triangle(X,Y,Z,p1,p2-p1));
+	if (intersect_line_triangle(X,Y,Z,p1,p1-p2, 1.e-12) > 0) {
+	  found = i;
+	  break;
+	}
+      }
+    }
+    if (found < 0){
+      return 0;      
+    }
+    t = t->getNeigh(found);
+    if (t->inCircumSphere(v)) {
+      //      printf("FOUND\n");
+      return t;
+    }
+    if (ITER++ > _size) break;
+  }
+  return 0;
+}
+
+
+MTet4 * getTetToBreak (MVertex *v, std::vector<MTet4*> &t, int &NB_GLOBAL_SEARCH){
+  // last inserted is used as starting point
+  // we know it is not deleted
+  MTet4 *start = t[t.size() - 1];
+  start = search4Tet (start,v,(int)t.size());
+  if (start)return start;
+  NB_GLOBAL_SEARCH++;
+  for (size_t i = 0;i<t.size();i++){
+    if (!t[i]->isDeleted() && t[i]->inCircumSphere(v))return t[i];
+  }  
+  return 0;
+}
+
+bool tetOnBox (MTetrahedron *t, MVertex *box[8]){
+  for (size_t i = 0;i<4;i++)
+    for (size_t j = 0;j<8;j++)
+      if (t->getVertex(i) == box[j])return true;
+  return false;
+}
+
+void delaunayMeshIn3D(std::vector<MVertex*> &v, std::vector<MTetrahedron*> &result)
+{
+  std::vector<MTet4*> t;
+  MVertex *box[8];
+  initialCube (v,box,t);
+
+  int NB_GLOBAL_SEARCH = 0;
+  SortHilbert(v);
+  clock_t t1 = clock();
+
+  for (size_t i=0;i<v.size();i++){
+    MVertex *pv = v[i];    
+    MTet4 * found = getTetToBreak (pv,t,NB_GLOBAL_SEARCH);
+    std::list<faceXtet> shell;
+    std::list<MTet4*> cavity;
+    recurFindCavity(shell, cavity, pv, found);
+    std::vector<MTet4*> extended_cavity;
+    std::list<faceXtet>::iterator it = shell.begin();
+    while (it != shell.end()){
+      MTetrahedron *tr = new MTetrahedron(it->getVertex(0), 
+					  it->getVertex(1), 
+					  it->getVertex(2), pv);
+      MTet4 *t4 = new MTet4(tr, 0.0);
+      t.push_back(t4);
+      extended_cavity.push_back(t4);
+      MTet4 *otherSide = it->t1->getNeigh(it->i1);
+      if (otherSide)
+	extended_cavity.push_back(otherSide);
+      ++it;
+    }
+    //    printf("connecting %i tets\n",extended_cavity.size());
+    connectTets_vector(extended_cavity.begin(),extended_cavity.end());
+  }
+
+  clock_t t2 = clock();
+  printf("%d global searches among %d CPU = %g\n",NB_GLOBAL_SEARCH,v.size(),(double)(t2-t1)/CLOCKS_PER_SEC);
+
+
+  //  FILE *f = fopen ("tet.pos","w");
+  //  fprintf(f,"View \"\"{\n");
+  for (size_t i = 0;i<t.size();i++){
+    if (t[i]->isDeleted() || tetOnBox (t[i]->tet(),box)) delete t[i]->tet();
+    else {
+      result.push_back(t[i]->tet());
+      //      t[i]->tet()->writePOS (f, false,false,true,false,false,false);
+    }
+    delete t[i];
+  }
+  //  fprintf(f,"};\n");
+  //  fclose(f);
+}
+
+
+
diff --git a/Mesh/meshGRegionDelaunayInsertion.h b/Mesh/meshGRegionDelaunayInsertion.h
index 15a467cd8a..9360b51439 100644
--- a/Mesh/meshGRegionDelaunayInsertion.h
+++ b/Mesh/meshGRegionDelaunayInsertion.h
@@ -173,6 +173,8 @@ class MTet4
 
 void connectTets(std::list<MTet4*> &);
 void connectTets(std::vector<MTet4*> &);
+// IN --> Vertices ----  OUT --> Tets
+void delaunayMeshIn3D(std::vector<MVertex*> &, std::vector<MTetrahedron*> &);
 void insertVerticesInRegion(GRegion *gr, int maxVert = 2000000000, bool _classify = true);
 void bowyerWatsonFrontalLayers(GRegion *gr, bool hex);
 GRegion *getRegionFromBoundingFaces(GModel *model,
diff --git a/Numeric/CMakeLists.txt b/Numeric/CMakeLists.txt
index a3382e8d92..ae0f52d721 100644
--- a/Numeric/CMakeLists.txt
+++ b/Numeric/CMakeLists.txt
@@ -7,6 +7,7 @@ set(SRC
   Numeric.cpp
     fullMatrix.cpp
   BasisFactory.cpp
+  discreteFrechetDistance.cpp
     nodalBasis.cpp
 	polynomialBasis.cpp
 	pyramidalBasis.cpp
@@ -27,6 +28,7 @@ set(SRC
     GaussQuadraturePyr.cpp
     GaussLegendreSimplex.cpp
   GaussJacobi1D.cpp
+  HilbertCurve.cpp
  robustPredicates.cpp
   mathEvaluator.cpp
   Iso.cpp
diff --git a/Numeric/HilbertCurve.cpp b/Numeric/HilbertCurve.cpp
new file mode 100644
index 0000000000..a8f11067d6
--- /dev/null
+++ b/Numeric/HilbertCurve.cpp
@@ -0,0 +1,264 @@
+#include "SBoundingBox3d.h"
+#include "MVertex.h"
+
+
+struct HilbertSort
+{
+// The code for generating table transgc
+// from: http://graphics.stanford.edu/~seander/bithacks.html.    
+  int transgc[8][3][8];
+  int tsb1mod3[8];
+  int maxDepth;
+  int Limit;
+  SBoundingBox3d bbox ;
+  void ComputeGrayCode(int n);
+  int Split(MVertex** vertices,
+	    int arraysize,int GrayCode0,int GrayCode1,
+	    double BoundingBoxXmin, double BoundingBoxXmax, 
+	    double BoundingBoxYmin, double BoundingBoxYmax, 
+	    double BoundingBoxZmin, double BoundingBoxZmax);
+  void Sort(MVertex** vertices, int arraysize, int e, int d, 
+	   double BoundingBoxXmin, double BoundingBoxXmax, double BoundingBoxYmin, double BoundingBoxYmax, 
+	   double BoundingBoxZmin, double BoundingBoxZmax, int depth);
+  HilbertSort (int m = 0, int l=1) : maxDepth(m),Limit(l)
+  {
+    ComputeGrayCode(3);
+  }
+  void MultiscaleSortHilbert(MVertex** vertices, int arraysize, 
+			     int threshold, double ratio, int *depth)
+  {
+    int middle;
+    
+    middle = 0;
+    if (arraysize >= threshold) {
+      (*depth)++;
+      middle = arraysize * ratio;
+      MultiscaleSortHilbert(vertices, middle, threshold, ratio, depth);
+    }
+    Sort (&(vertices[middle]),arraysize - middle,0,0,
+	  bbox.min().x(),bbox.max().x(),
+	  bbox.min().y(),bbox.max().y(),
+	  bbox.min().z(),bbox.max().z(),0);
+  }
+  
+  void Apply (std::vector<MVertex*> &v)
+  {
+    for (size_t i=0;i<v.size();i++){
+      MVertex *pv = v[i];
+      bbox += SPoint3(pv->x(),pv->y(),pv->z());    
+    }
+    bbox *= 1.01;
+    MVertex**pv = &v[0];
+    int depth;
+    MultiscaleSortHilbert(pv, v.size(), 128, 0.125,&depth);
+  }
+};
+
+
+void HilbertSort::ComputeGrayCode(int n)
+{
+  int gc[8], N, mask, travel_bit;
+  int e, d, f, k, g;
+  int v, c;
+  int i;
+
+  N = (n == 2) ? 4 : 8;
+  mask = (n == 2) ? 3 : 7;
+
+  // Generate the Gray code sequence.
+  for (i = 0; i < N; i++) {
+    gc[i] = i ^ (i >> 1);
+  }
+
+  for (e = 0; e < N; e++) {
+    for (d = 0; d < n; d++) {
+      // Calculate the end point (f).
+      f = e ^ (1 << d);  // Toggle the d-th bit of 'e'.
+      // travel_bit = 2**p, the bit we want to travel. 
+      travel_bit = e ^ f;
+      for (i = 0; i < N; i++) {
+        // // Rotate gc[i] left by (p + 1) % n bits.
+        k = gc[i] * (travel_bit * 2);
+        g = ((k | (k / N)) & mask);
+        // Calculate the permuted Gray code by xor with the start point (e).
+        transgc[e][d][i] = (g ^ e);
+      }
+      //      assert(transgc[e][d][0] == e);
+      //      assert(transgc[e][d][N - 1] == f);
+    } // d
+  } // e
+
+  // Count the consecutive '1' bits (trailing) on the right.
+  tsb1mod3[0] = 0;
+  for (i = 1; i < N; i++) {
+    v = ~i; // Count the 0s.
+    v = (v ^ (v - 1)) >> 1; // Set v's trailing 0s to 1s and zero rest
+    for (c = 0; v; c++) {
+      v >>= 1;
+    }
+    tsb1mod3[i] = c % n;
+  }
+}
+
+
+int HilbertSort::Split(MVertex** vertices,
+		       int arraysize,int GrayCode0,int GrayCode1,
+		       double BoundingBoxXmin, double BoundingBoxXmax, double BoundingBoxYmin, double BoundingBoxYmax, 
+		       double BoundingBoxZmin, double BoundingBoxZmax)
+{
+  MVertex* swapvert;
+  int axis, d;
+  double split;
+  int i, j;
+
+
+  // Find the current splitting axis. 'axis' is a value 0, or 1, or 2, which 
+  //   correspoding to x-, or y- or z-axis.
+  axis = (GrayCode0 ^ GrayCode1) >> 1; 
+
+  // Calulate the split position along the axis.
+  if (axis == 0) {
+    split = 0.5 * (BoundingBoxXmin + BoundingBoxXmax);
+  } else if (axis == 1) {
+    split = 0.5 * (BoundingBoxYmin + BoundingBoxYmax);
+  } else { // == 2
+    split = 0.5 * (BoundingBoxZmin + BoundingBoxZmax);
+  }
+
+  // Find the direction (+1 or -1) of the axis. If 'd' is +1, the direction
+  //   of the axis is to the positive of the axis, otherwise, it is -1.
+  d = ((GrayCode0 & (1<<axis)) == 0) ? 1 : -1;
+
+
+  // Partition the vertices into left- and right-arrays such that left points
+  //   have Hilbert indices lower than the right points.
+  i = 0;
+  j = arraysize - 1;
+
+  // Partition the vertices into left- and right-arrays.
+  if (d > 0) {
+    do {
+      for (; i < arraysize; i++) {      
+        if (vertices[i]->point()[axis] >= split) break;
+      }
+      for (; j >= 0; j--) {
+        if (vertices[j]->point()[axis] < split) break;
+      }
+      // Is the partition finished?
+      if (i == (j + 1)) break;
+      // Swap i-th and j-th vertices.
+      swapvert = vertices[i];
+      vertices[i] = vertices[j];
+      vertices[j] = swapvert;
+      // Continue patitioning the array;
+    } while (true);
+  } else {
+    do {
+      for (; i < arraysize; i++) {      
+        if (vertices[i]->point()[axis] <= split) break;
+      }
+      for (; j >= 0; j--) {
+        if (vertices[j]->point()[axis] > split) break;
+      }
+      // Is the partition finished?
+      if (i == (j + 1)) break;
+      // Swap i-th and j-th vertices.
+      swapvert = vertices[i];
+      vertices[i] = vertices[j];
+      vertices[j] = swapvert;
+      // Continue patitioning the array;
+    } while (true);
+  }
+
+  return i;
+}
+
+// The sorting code is inspired by Tetgen 1.5
+
+void HilbertSort::Sort(MVertex** vertices, int arraysize, int e, int d, 
+		       double BoundingBoxXmin, double BoundingBoxXmax, double BoundingBoxYmin, double BoundingBoxYmax, 
+		       double BoundingBoxZmin, double BoundingBoxZmax, int depth)
+{
+  double x1, x2, y1, y2, z1, z2;
+  int p[9], w, e_w, d_w, k, ei, di;
+  int n = 3, mask = 7;
+
+  p[0] = 0;
+  p[8] = arraysize;
+
+  p[4] = Split(vertices, p[8], transgc[e][d][3], transgc[e][d][4], 
+	       BoundingBoxXmin, BoundingBoxXmax, BoundingBoxYmin, BoundingBoxYmax, BoundingBoxZmin, BoundingBoxZmax);
+  p[2] = Split(vertices, p[4], transgc[e][d][1], transgc[e][d][2], 
+	       BoundingBoxXmin, BoundingBoxXmax, BoundingBoxYmin, BoundingBoxYmax, BoundingBoxZmin, BoundingBoxZmax);
+  p[1] = Split(vertices, p[2], transgc[e][d][0], transgc[e][d][1], 
+	       BoundingBoxXmin, BoundingBoxXmax, BoundingBoxYmin, BoundingBoxYmax, BoundingBoxZmin, BoundingBoxZmax);
+  p[3] = Split(&(vertices[p[2]]), p[4] - p[2], 
+	       transgc[e][d][2], transgc[e][d][3], 
+	       BoundingBoxXmin, BoundingBoxXmax, BoundingBoxYmin, BoundingBoxYmax, BoundingBoxZmin, BoundingBoxZmax) + p[2];
+  p[6] = Split(&(vertices[p[4]]), p[8] - p[4], 
+	       transgc[e][d][5], transgc[e][d][6], 
+	       BoundingBoxXmin, BoundingBoxXmax, BoundingBoxYmin, BoundingBoxYmax, BoundingBoxZmin, BoundingBoxZmax) + p[4];
+  p[5] = Split(&(vertices[p[4]]), p[6] - p[4], 
+	       transgc[e][d][4], transgc[e][d][5], 
+	       BoundingBoxXmin, BoundingBoxXmax, BoundingBoxYmin, BoundingBoxYmax, BoundingBoxZmin, BoundingBoxZmax) + p[4];
+  p[7] = Split(&(vertices[p[6]]), p[8] - p[6], 
+	       transgc[e][d][6], transgc[e][d][7], 
+	       BoundingBoxXmin, BoundingBoxXmax, BoundingBoxYmin, BoundingBoxYmax, BoundingBoxZmin, BoundingBoxZmax) + p[6];
+
+  if (maxDepth > 0) {
+    if ((depth + 1) == maxDepth) {
+      return;
+    }
+  }
+
+  // Recursively sort the points in sub-boxes.
+  for (w = 0; w < 8; w++) {
+    if ((p[w+1] - p[w]) > Limit) {
+      if (w == 0) {
+        e_w = 0;
+      } else {
+        k = 2 * ((w - 1) / 2); 
+        e_w = k ^ (k >> 1); 
+      }
+      k = e_w;
+      e_w = ((k << (d+1)) & mask) | ((k >> (n-d-1)) & mask);
+      ei = e ^ e_w;
+      if (w == 0) {
+        d_w = 0;
+      } else {
+        d_w = ((w % 2) == 0) ? tsb1mod3[w - 1] : tsb1mod3[w];
+      }
+      di = (d + d_w + 1) % n;
+      if (transgc[e][d][w] & 1) {
+        x1 = 0.5 * (BoundingBoxXmin + BoundingBoxXmax);
+        x2 = BoundingBoxXmax;
+      } else {
+        x1 = BoundingBoxXmin;
+        x2 = 0.5 * (BoundingBoxXmin + BoundingBoxXmax);
+      }
+      if (transgc[e][d][w] & 2) { // y-axis
+        y1 = 0.5 * (BoundingBoxYmin + BoundingBoxYmax);
+        y2 = BoundingBoxYmax;
+      } else {
+        y1 = BoundingBoxYmin;
+        y2 = 0.5 * (BoundingBoxYmin + BoundingBoxYmax);
+      }
+      if (transgc[e][d][w] & 4) { // z-axis
+        z1 = 0.5 * (BoundingBoxZmin + BoundingBoxZmax);
+        z2 = BoundingBoxZmax;
+      } else {
+        z1 = BoundingBoxZmin;
+        z2 = 0.5 * (BoundingBoxZmin + BoundingBoxZmax);
+      }
+      Sort(&(vertices[p[w]]), p[w+1] - p[w], ei, di, 
+                    x1, x2, y1, y2, z1, z2, depth+1);
+    } 
+  } 
+}
+
+
+
+void SortHilbert (std::vector<MVertex*>& v){
+  HilbertSort h;
+  h.Apply(v);
+}
diff --git a/Numeric/HilbertCurve.h b/Numeric/HilbertCurve.h
new file mode 100644
index 0000000000..b413f587ab
--- /dev/null
+++ b/Numeric/HilbertCurve.h
@@ -0,0 +1,4 @@
+#ifndef _HILBERT_CURVE_
+#define _HILBERT_CURVE_
+void SortHilbert (std::vector<MVertex*>&);
+#endif
-- 
GitLab