Repository: root-project/root (C++)

Issue:
TGraph2D's Interpolate() returns 0
This issue is made as a follow-up of the discussion in: https://root-forum.cern.ch/t/tgraph2d-interpolate-returns-0/54172

The Interpolate() method on TGraph2D sometimes returns 0.0 on a perfectly interpolatable coordinates.
For example, this is how my graph looks like drawn with “colz” option:
<img src="https://user-images.githubusercontent.com/67898371/227203785-b6fd576d-7757-496f-807e-4375156e2def.png" width="500">


To reproduce the problem, please download the file attached in the above root forum post and execute:
```
root yield_graphs.root
graph_yield_nom->Interpolate(750, 500) // this returns a non-zero value
graph_yield_nom->Interpolate(760, 500) // this returns a non-zero value
graph_yield_nom->Interpolate(770, 500) // this returns zero
graph_yield_nom->Interpolate(780, 500) // this returns zero
graph_yield_nom->Interpolate(790, 500) // this returns a non-zero value
graph_yield_nom->Interpolate(800, 500) // this returns zero
```

The coordinates that return zero are within interpolation range as shown by the plot. So they should have returned non-zero values.



Fix (patch):
diff --git a/math/mathcore/inc/Math/Delaunay2D.h b/math/mathcore/inc/Math/Delaunay2D.h
index b40237f095bb4..198ca47b44cda 100644
--- a/math/mathcore/inc/Math/Delaunay2D.h
+++ b/math/mathcore/inc/Math/Delaunay2D.h
@@ -21,7 +21,7 @@
 //#define THREAD_SAFE
 
 
-#include "RtypesCore.h"
+//#include "RtypesCore.h"
 
 #include <map>
 #include <vector>
@@ -64,6 +64,50 @@ namespace ROOT {
 
    See [http://www.cs.cmu.edu/~quake/triangle.html]
 
+   After having found the triangles using the above library,  barycentric coordinates are used
+   to test whether a point is inside a triangle (inTriangle test) and for interpolation.
+   All this below is implemented in the DoInterpolateNormalized function.
+
+   Given triangle ABC and point P, P can be expressed by
+
+     P.x = la * A.x + lb * B.x + lc * C.x
+     P.y = la * A.y + lb * B.y + lc * C.y
+
+   with lc = 1 - la - lb
+
+     P.x = la * A.x + lb * B.x + (1-la-lb) * C.x
+     P.y = la * A.y + lb * B.y + (1-la-lb) * C.y
+
+   Rearranging yields
+
+     la * (A.x - C.x) + lb * (B.x - C.x) = P.x - C.x
+     la * (A.y - C.y) + lb * (B.y - C.y) = P.y - C.y
+
+   Thus
+
+     la = ( (B.y - C.y)*(P.x - C.x) + (C.x - B.x)*(P.y - C.y) ) / ( (B.y - C.y)*(A.x - C.x) + (C.x - B.x)*(A.y - C.y) )
+     lb = ( (C.y - A.y)*(P.x - C.x) + (A.x - C.x)*(P.y - C.y) ) / ( (B.y - C.y)*(A.x - C.x) + (C.x - B.x)*(A.y - C.y) )
+     lc = 1 - la - lb
+
+   We save the inverse denominator to speedup computation
+
+     invDenom = 1 / ( (B.y - C.y)*(A.x - C.x) + (C.x - B.x)*(A.y - C.y) )
+
+     P is in triangle (including edges if
+
+     0 <= [la, lb, lc] <= 1
+
+     The interpolation of P.z is
+
+     P.z = la * A.z + lb * B.z + lc * C.z
+
+   To speed up localisation of points (to see to which triangle belong) a grid is layed over the internal coordinate space.
+   A reference to triangle ABC is added to _all_ grid cells that include ABC's bounding box.
+   The size of the grid is defined to be 25x25
+
+   Optionally (if the compiler macro `HAS_GCAL` is defined ) the triangle findings and interpolation can be computed
+   using the GCAL library. This is however not supported when using the class within ROOT
+
    \ingroup MathCore
  */
 
@@ -73,12 +117,12 @@ class Delaunay2D  {
 public:
 
    struct Triangle {
-      double x[3];
-      double y[3];
-      UInt_t idx[3];
+      double x[3];           // x of triangle vertices
+      double y[3];           // y of triangle vertices
+      unsigned int idx[3];   // point corresponding to vertices
 
       #ifndef HAS_CGAL
-      double invDenom; //see comment below in CGAL fall back section
+      double invDenom; // cached inv denominator for computing barycentric coordinates (see above)
       #endif
    };
 
@@ -92,16 +136,17 @@ class Delaunay2D  {
    /// set the input points for building the graph
    void SetInputPoints(int n, const double *x, const double * y, const double * z, double xmin=0, double xmax=0, double ymin=0, double ymax=0);
 
-   /// Return the Interpolated z value corresponding to the given (x,y) point
+   /// Return the Interpolated z value corresponding to the given (x,y) point.
    /// Note that in case no Delaunay triangles are found, for example when the
-   /// points are aligned, then a default value of zero is always return
+   /// points are aligned, then a default value of zero is always return.
+   /// See the class documentation for  how the interpolation is computed.
    double  Interpolate(double x, double y);
 
    /// Find all triangles
    void      FindAllTriangles();
 
    /// return the number of triangles
-   Int_t     NumberOfTriangles() const {return fNdt;}
+   int    NumberOfTriangles() const {return fNdt;}
 
    double  XMin() const {return fXNmin;}
    double  XMax() const {return fXNmax;}
@@ -128,7 +173,8 @@ class Delaunay2D  {
       return (x+offset)*factor;
    }
 
-   /// internal function to normalize the points
+   /// internal function to normalize the points.
+   /// See the class documentation for the details on how it is computed.
    void DoNormalizePoints();
 
    /// internal function to find the triangle
@@ -147,8 +193,8 @@ class Delaunay2D  {
 
 protected:
 
-   Int_t       fNdt;           ///<! Number of Delaunay triangles found
-   Int_t       fNpoints;       ///<! Number of data points
+   int         fNdt;           ///<! Number of Delaunay triangles found
+   int         fNpoints;       ///<! Number of data points
 
    const double   *fX;         ///<! Pointer to X array (managed externally)
    const double   *fY;         ///<! Pointer to Y array
@@ -167,21 +213,38 @@ class Delaunay2D  {
 
    double    fZout;            ///<! Height for points lying outside the convex hull
 
-#ifdef THREAD_SAFE
+   bool      fInit;            ///<! True if FindAllTriangles() has been performed
 
-   enum class Initialization : char {UNINITIALIZED, INITIALIZING, INITIALIZED};
-   std::atomic<Initialization> fInit; ///<! Indicate initialization state
 
-#else
-   Bool_t      fInit;          ///<! True if FindAllTriangles() has been performed
-#endif
+   Triangles   fTriangles;     ///<! Triangles of Triangulation
 
+#ifndef HAS_CGAL
 
-   Triangles   fTriangles;     ///<! Triangles of Triangulation
+   //using triangle library
 
-#ifdef HAS_CGAL
+   std::vector<double> fXN; ///<! normalized X
+   std::vector<double> fYN; ///<! normalized Y
+
+   static const int fNCells = 25; ///<! number of cells to divide the normalized space
+   double fXCellStep; ///<! inverse denominator to calculate X cell = fNCells / (fXNmax - fXNmin)
+   double fYCellStep; ///<! inverse denominator to calculate X cell = fNCells / (fYNmax - fYNmin)
+   std::set<unsigned int> fCells[(fNCells+1)*(fNCells+1)]; ///<! grid cells with containing triangles
+
+   inline unsigned int Cell(unsigned int x, unsigned int y) const {
+      return x*(fNCells+1) + y;
+   }
 
-   //Functor class for accessing the function values/gradients
+   inline int CellX(double x) const {
+      return (x - fXNmin) * fXCellStep;
+   }
+
+   inline int CellY(double y) const {
+      return (y - fYNmin) * fYCellStep;
+   }
+
+#else // HAS_CGAL
+      // case of using GCAL
+      //Functor class for accessing the function values/gradients
       template< class PointWithInfoMap, typename ValueType >
       struct Data_access : public std::unary_function< typename PointWithInfoMap::key_type,
                 std::pair<ValueType, bool> >
@@ -215,71 +278,6 @@ class Delaunay2D  {
    Delaunay fCGALdelaunay; //! CGAL delaunay triangulation object
    PointWithInfoMap fNormalizedPoints; //! Normalized function values
 
-#else // HAS_CGAL
-   //fallback to triangle library
-
-   /* Using barycentric coordinates for inTriangle test and interpolation
-    *
-    * Given triangle ABC and point P, P can be expressed by
-    *
-    * P.x = la * A.x + lb * B.x + lc * C.x
-    * P.y = la * A.y + lb * B.y + lc * C.y
-    *
-    * with lc = 1 - la - lb
-    *
-    * P.x = la * A.x + lb * B.x + (1-la-lb) * C.x
-    * P.y = la * A.y + lb * B.y + (1-la-lb) * C.y
-    *
-    * Rearranging yields
-    *
-    * la * (A.x - C.x) + lb * (B.x - C.x) = P.x - C.x
-    * la * (A.y - C.y) + lb * (B.y - C.y) = P.y - C.y
-    *
-    * Thus
-    *
-    * la = ( (B.y - C.y)*(P.x - C.x) + (C.x - B.x)*(P.y - C.y) ) / ( (B.y - C.y)*(A.x - C.x) + (C.x - B.x)*(A.y - C.y) )
-    * lb = ( (C.y - A.y)*(P.x - C.x) + (A.x - C.x)*(P.y - C.y) ) / ( (B.y - C.y)*(A.x - C.x) + (C.x - B.x)*(A.y - C.y) )
-    * lc = 1 - la - lb
-    *
-    * We save the inverse denominator to speedup computation
-    *
-    * invDenom = 1 / ( (B.y - C.y)*(A.x - C.x) + (C.x - B.x)*(A.y - C.y) )
-    *
-    * P is in triangle (including edges if
-    *
-    * 0 <= [la, lb, lc] <= 1
-    *
-    * The interpolation of P.z is
-    *
-    * P.z = la * A.z + lb * B.z + lc * C.z
-    *
-    */
-
-   std::vector<double> fXN; ///<! normalized X
-   std::vector<double> fYN; ///<! normalized Y
-
-   /* To speed up localisation of points a grid is layed over normalized space
-    *
-    * A reference to triangle ABC is added to _all_ grid cells that include ABC's bounding box
-    */
-
-   static const int fNCells = 25; ///<! number of cells to divide the normalized space
-   double fXCellStep; ///<! inverse denominator to calculate X cell = fNCells / (fXNmax - fXNmin)
-   double fYCellStep; ///<! inverse denominator to calculate X cell = fNCells / (fYNmax - fYNmin)
-   std::set<UInt_t> fCells[(fNCells+1)*(fNCells+1)]; ///<! grid cells with containing triangles
-
-   inline unsigned int Cell(UInt_t x, UInt_t y) const {
-      return x*(fNCells+1) + y;
-   }
-
-   inline int CellX(double x) const {
-      return (x - fXNmin) * fXCellStep;
-   }
-
-   inline int CellY(double y) const {
-      return (y - fYNmin) * fYCellStep;
-   }
-
 #endif //HAS_CGAL
 
 
diff --git a/math/mathcore/src/Delaunay2D.cxx b/math/mathcore/src/Delaunay2D.cxx
index d9f3f9c3bad5d..67ef5854508b6 100644
--- a/math/mathcore/src/Delaunay2D.cxx
+++ b/math/mathcore/src/Delaunay2D.cxx
@@ -16,15 +16,16 @@
 
 //#include <thread>
 
-// in case we do not use CGAL
+// use the triangle library if we do not use CGAL
 #ifndef HAS_CGAL
-// use the triangle library
 #include "triangle.h"
 #endif
 
 #include <algorithm>
 #include <stdlib.h>
 
+#include <iostream>
+
 namespace ROOT {
 
    namespace Math {
@@ -60,11 +61,7 @@ void Delaunay2D::SetInputPoints(int n, const double * x, const double * y, const
                            double xmin, double xmax, double ymin, double ymax) {
 
 
-#ifdef THREAD_SAFE
-   fInit         = Initialization::UNINITIALIZED;
-#else
    fInit         = kFALSE;
-#endif
 
    if (n == 0 || !x || !y || !z ) return;
 
@@ -128,9 +125,8 @@ double Delaunay2D::Interpolate(double x, double y)
    yy = Linear_transform(y, fOffsetY, fScaleFactorY); //yy = yTransformer(y);
    double zz = DoInterpolateNormalized(xx, yy);
 
-   // Wrong zeros may appear when points sit on a regular grid.
-   // The following line try to avoid this problem.
-   if (zz==0) zz = DoInterpolateNormalized(xx+0.0001, yy);
+   // the case of points on a regular grid (i.e. points on triangle edges) it is now handles in
+   // DoInterpolateNormalized
 
    return zz;
 }
@@ -139,119 +135,32 @@ double Delaunay2D::Interpolate(double x, double y)
 void Delaunay2D::FindAllTriangles()
 {
 
-#ifdef THREAD_SAFE
-   //treat the common case first
-   if(fInit.load(std::memory_order::memory_order_relaxed) == Initialization::INITIALIZED)
+   if (fInit)
       return;
+   else
+      fInit = kTRUE;
 
-   Initialization cState = Initialization::UNINITIALIZED;
-   if(fInit.compare_exchange_strong(cState, Initialization::INITIALIZING,
-                                    std::memory_order::memory_order_release, std::memory_order::memory_order_relaxed))
-   {
-      // the value of fInit was indeed UNINIT, we replaced it atomically with initializing
-      // performing the initialzing now
-#else
-      if (fInit) return; else fInit = kTRUE;
-#endif
-
-      // Function used internally only. It creates the data structures needed to
-      // compute the Delaunay triangles.
-
-      // Offset fX and fY so they average zero, and scale so the average
-      // of the X and Y ranges is one. The normalized version of fX and fY used
-      // in Interpolate.
+   // Function used internally only. It creates the data structures needed to
+   // compute the Delaunay triangles.
 
-      DoNormalizePoints(); // call backend specific point normalization
+   // Offset fX and fY so they average zero, and scale so the average
+   // of the X and Y ranges is one. The normalized version of fX and fY used
+   // in Interpolate.
 
-      DoFindTriangles(); // call backend specific triangle finding
+   DoNormalizePoints(); // call backend specific point normalization
 
-      fNdt = fTriangles.size();
-
-#ifdef THREAD_SAFE
-      fInit = Initialization::INITIALIZED;
-   } else while(cState != Initialization::INITIALIZED) {
-         //the value of fInit was NOT UNINIT, so we have to spin until we reach INITIALEZED
-         cState = fInit.load(std::memory_order::memory_order_relaxed);
-      }
-#endif
+   DoFindTriangles(); // call backend specific triangle finding
 
+   fNdt = fTriangles.size();
 }
 
-
 // backend specific implementations
 
-#ifdef HAS_CGAL
-/// CGAL implementation of normalize points
-void Delaunay2D::DonormalizePoints() {
-   for (Int_t n = 0; n < fNpoints; n++) {
-      //Point p(xTransformer(fX[n]), yTransformer(fY[n]));
-      Point p(linear_transform(fX[n], fOffsetX, fScaleFactorX),
-              linear_transform(fY[n], fOffsetY, fScaleFactorY));
-
-      fNormalizedPoints.insert(std::make_pair(p, n));
-   }
-}
-
-/// CGAL implementation for finding triangles
-void Delaunay2D::DoFindTriangles() {
-   fCGALdelaunay.insert(fNormalizedPoints.begin(), fNormalizedPoints.end());
-
-   std::transform(fCGALdelaunay.finite_faces_begin(),
-                  fCGALdelaunay.finite_faces_end(), std::back_inserter(fTriangles),
-                  [] (const Delaunay::Face face) -> Triangle {
-
-                     Triangle tri;
-
-                     auto transform = [&] (const unsigned int i) {
-                        tri.x[i] = face.vertex(i)->point().x();
-                        tri.y[i] = face.vertex(i)->point().y();
-                        tri.idx[i] = face.vertex(i)->info();
-                     };
-
-                     transform(0);
-                     transform(1);
-                     transform(2);
-
-                     return tri;
-
-                  });
-}
-
-/// CGAL implementation for interpolation
-double Delaunay2D::DoInterpolateNormalized(double xx, double yy)
-{
-   // Finds the Delaunay triangle that the point (xi,yi) sits in (if any) and
-   // calculate a z-value for it by linearly interpolating the z-values that
-   // make up that triangle.
-
-   // initialise the Delaunay algorithm if needed
-    FindAllTriangles();
-
-   //coordinate computation
-   Point p(xx, yy);
-
-   std::vector<std::pair<Point, Coord_type> > coords;
-   auto nn = CGAL::natural_neighbor_coordinates_2(fCGALdelaunay, p,
-                                                  std::back_inserter(coords));
-
-   //std::cout << std::this_thread::get_id() << ": Found " << coords.size() << " points" << std::endl;
-
-   if(!nn.third) //neighbor finding was NOT successfull, return standard value
-      return fZout;
-
-   //printf("found neighbors %u\n", coords.size());
-
-   Coord_type res = CGAL::linear_interpolation(coords.begin(), coords.end(),
-                                               nn.second, Value_access(fNormalizedPoints, fZ));
-
-   //std::cout << std::this_thread::get_id() << ": Result " << res << std::endl;
-
-   return res;
-}
+#ifndef HAS_CGAL
 
-#else // HAS_CGAL
+// Triangle implementation (default case)
 
-/// Triangle implementation for normalizing the points
+/// Triangle implementation for points normalization
 void Delaunay2D::DoNormalizePoints() {
    for (Int_t n = 0; n < fNpoints; n++) {
       fXN.push_back(Linear_transform(fX[n], fOffsetX, fScaleFactorX));
@@ -387,84 +296,132 @@ void Delaunay2D::DoFindTriangles() {
 /// Finds the Delaunay triangle that the point (xi,yi) sits in (if any) and
 /// calculate a z-value for it by linearly interpolating the z-values that
 /// make up that triangle.
+/// Relay that all the triangles have been found before
+/// see comment in class description (in Delaunay2D.h) for implementation details:
+/// finding barycentric cordinates and computing the interpolation
 double Delaunay2D::DoInterpolateNormalized(double xx, double yy)
 {
 
-   // relay that ll the triangles have been found
-   ///  FindAllTriangles();
-
-    //see comment in header for CGAL fallback section
-    auto bayCoords = [&] (const unsigned int t) -> std::tuple<double, double, double> {
-       double la = ( (fTriangles[t].y[1] - fTriangles[t].y[2])*(xx - fTriangles[t].x[2])
-                    + (fTriangles[t].x[2] - fTriangles[t].x[1])*(yy - fTriangles[t].y[2]) ) * fTriangles[t].invDenom;
-       double lb = ( (fTriangles[t].y[2] - fTriangles[t].y[0])*(xx - fTriangles[t].x[2])
-                    + (fTriangles[t].x[0] - fTriangles[t].x[2])*(yy - fTriangles[t].y[2]) ) * fTriangles[t].invDenom;
+   // compute barycentric coordinates of a point P(xx,yy,zz)
+   auto bayCoords = [&](const unsigned int t) -> std::tuple<double, double, double> {
+      double la = ((fTriangles[t].y[1] - fTriangles[t].y[2]) * (xx - fTriangles[t].x[2]) +
+                   (fTriangles[t].x[2] - fTriangles[t].x[1]) * (yy - fTriangles[t].y[2])) *
+                  fTriangles[t].invDenom;
+      double lb = ((fTriangles[t].y[2] - fTriangles[t].y[0]) * (xx - fTriangles[t].x[2]) +
+                   (fTriangles[t].x[0] - fTriangles[t].x[2]) * (yy - fTriangles[t].y[2])) *
+                  fTriangles[t].invDenom;
 
-       return std::make_tuple(la, lb, (1 - la - lb));
-    };
+      return std::make_tuple(la, lb, (1 - la - lb));
+   };
 
-    auto inTriangle = [] (const std::tuple<double, double, double> & coords) -> bool {
-       return std::get<0>(coords) >= 0 && std::get<1>(coords) >= 0 && std::get<2>(coords) >= 0;
-    };
+   // function to test if a point with barycentric coordinates (a,b,c) is inside the triangle
+   // If the point is outside one or more of the coordinate are negative.
+   // If the point is on a triangle edge, one of the coordinate (the one not part of the edge) is zero.
+   // Due to numerical error, it can happen that if the point is at the edge the result is a small negative value.
+   // Use then a tolerance (of - eps) to still consider the point within the triangle
+   auto inTriangle = [](const std::tuple<double, double, double> &coords) -> bool {
+      constexpr double eps = -4 * std::numeric_limits<double>::epsilon();
+      return std::get<0>(coords) > eps && std::get<1>(coords) > eps && std::get<2>(coords) > eps;
+   };
 
    int cX = CellX(xx);
    int cY = CellY(yy);
 
-   if(cX < 0 || cX > fNCells || cY < 0 || cY > fNCells)
-      return fZout; //TODO some more fancy interpolation here
+   if (cX < 0 || cX > fNCells || cY < 0 || cY > fNCells)
+      return fZout; // TODO some more fancy interpolation here
 
-    for(unsigned int t : fCells[Cell(cX, cY)]){
-       auto coords = bayCoords(t);
+   for (unsigned int t : fCells[Cell(cX, cY)]) {
 
-       if(inTriangle(coords)){
-          //we found the triangle -> interpolate using the barycentric interpolation
-          return std::get<0>(coords) * fZ[fTriangles[t].idx[0]]
-                 + std::get<1>(coords) * fZ[fTriangles[t].idx[1]]
-               + std::get<2>(coords) * fZ[fTriangles[t].idx[2]];
+      auto coords = bayCoords(t);
 
-       }
-    }
+      // std::cout << "result of bayCoords " << std::get<0>(coords) <<
+      //     "  " << std::get<1>(coords)  << "   " << std::get<2>(coords) << std::endl;
 
-    //debugging
+      if (inTriangle(coords)) {
 
-    /*
-    for(unsigned int t = 0; t < fNdt; ++t) {
-       auto coords = bayCoords(t);
+         // we found the triangle -> interpolate using the barycentric interpolation
 
-       if(inTriangle(coords)){
+         return std::get<0>(coords) * fZ[fTriangles[t].idx[0]] + std::get<1>(coords) * fZ[fTriangles[t].idx[1]] +
+                std::get<2>(coords) * fZ[fTriangles[t].idx[2]];
+      }
+   }
 
-          //brute force found a triangle -> grid not
-          printf("Found triangle %u for (%f,%f) -> (%u,%u)\n", t, xx,yy, cX, cY);
-          printf("Triangles in grid cell: ");
-          for(unsigned int x : fCells[Cell(cX, cY)])
-             printf("%u ", x);
-          printf("\n");
+   // printf("Could not find a triangle for point (%f,%f)\n", xx, yy);
 
-          printf("Triangle %u is in cells: ", t);
-          for(unsigned int i = 0; i <= fNCells; ++i)
-             for(unsigned int j = 0; j <= fNCells; ++j)
-                if(fCells[Cell(i,j)].count(t))
-                   printf("(%u,%u) ", i, j);
-          printf("\n");
-          for(unsigned int i = 0; i < 3; ++i)
-             printf("\tpoint %u (%u): (%f,%f) -> (%u,%u)\n", i, fTriangles[t].idx[i], fTriangles[t].x[i], fTriangles[t].y[i], CellX(fTriangles[t].x[i]), CellY(fTriangles[t].y[i]));
+   // no triangle found return standard value
+   return fZout;
+}
 
-          //we found the triangle -> interpolate using the barycentric interpolation
-          return std::get<0>(coords) * fZ[fTriangles[t].idx[0]]
-                 + std::get<1>(coords) * fZ[fTriangles[t].idx[1]]
-                 + std::get<2>(coords) * fZ[fTriangles[t].idx[2]];
+#else //HAS_CGAL: case of using GCAL
 
-       }
-    }
+/// CGAL implementation of normalize points
+void Delaunay2D::DonormalizePoints() {
+   for (Int_t n = 0; n < fNpoints; n++) {
+      //Point p(xTransformer(fX[n]), yTransformer(fY[n]));
+      Point p(linear_transform(fX[n], fOffsetX, fScaleFactorX),
+              linear_transform(fY[n], fOffsetY, fScaleFactorY));
 
-    printf("Could not find a triangle for point (%f,%f)\n", xx, yy);
-    */
+      fNormalizedPoints.insert(std::make_pair(p, n));
+   }
+}
 
-    //no triangle found return standard value
-   return fZout;
+/// CGAL implementation for finding triangles
+void Delaunay2D::DoFindTriangles() {
+   fCGALdelaunay.insert(fNormalizedPoints.begin(), fNormalizedPoints.end());
+
+   std::transform(fCGALdelaunay.finite_faces_begin(),
+                  fCGALdelaunay.finite_faces_end(), std::back_inserter(fTriangles),
+                  [] (const Delaunay::Face face) -> Triangle {
+
+                     Triangle tri;
+
+                     auto transform = [&] (const unsigned int i) {
+                        tri.x[i] = face.vertex(i)->point().x();
+                        tri.y[i] = face.vertex(i)->point().y();
+                        tri.idx[i] = face.vertex(i)->info();
+                     };
+
+                     transform(0);
+                     transform(1);
+                     transform(2);
+
+                     return tri;
+
+                  });
 }
-#endif //HAS_CGAL
+
+/// CGAL implementation for interpolation
+double Delaunay2D::DoInterpolateNormalized(double xx, double yy)
+{
+   // Finds the Delaunay triangle that the point (xi,yi) sits in (if any) and
+   // calculate a z-value for it by linearly interpolating the z-values that
+   // make up that triangle.
+
+   // initialise the Delaunay algorithm if needed
+    FindAllTriangles();
+
+   //coordinate computation
+   Point p(xx, yy);
+
+   std::vector<std::pair<Point, Coord_type> > coords;
+   auto nn = CGAL::natural_neighbor_coordinates_2(fCGALdelaunay, p,
+                                                  std::back_inserter(coords));
+
+   //std::cout << std::this_thread::get_id() << ": Found " << coords.size() << " points" << std::endl;
+
+   if(!nn.third) //neighbor finding was NOT successfull, return standard value
+      return fZout;
+
+   //printf("found neighbors %u\n", coords.size());
+
+   Coord_type res = CGAL::linear_interpolation(coords.begin(), coords.end(),
+                                               nn.second, Value_access(fNormalizedPoints, fZ));
+
+   //std::cout << std::this_thread::get_id() << ": Result " << res << std::endl;
+
+   return res;
+}
+#endif // HAS_GCAL
 
 } // namespace Math
 } // namespace ROOT
-