Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
Delaunay2D.cxx
Go to the documentation of this file.
1// @(#)root/mathcore:$Id: Delaunay2D.h,v 1.00
2// Author: Daniel Funke, Lorenzo Moneta
3
4/*************************************************************************
5 * Copyright (C) 2015 ROOT Math Team *
6 * All rights reserved. *
7 * *
8 * For the licensing terms see $ROOTSYS/LICENSE. *
9 * For the list of contributors see $ROOTSYS/README/CREDITS. *
10 *************************************************************************/
11
12// Implementation file for class Delaunay2D
13
14#include "Math/Delaunay2D.h"
15#include "Rtypes.h"
16
17//#include <thread>
18
19// use the triangle library if we do not use CGAL
20#ifndef HAS_CGAL
21#include "triangle.h"
22#endif
23
24#include <algorithm>
25#include <stdlib.h>
26
27#include <iostream>
28#include <limits>
29
30namespace ROOT {
31
32 namespace Math {
33
34
35/// class constructor from array of data points
36Delaunay2D::Delaunay2D(int n, const double * x, const double * y, const double * z,
37 double xmin, double xmax, double ymin, double ymax)
38{
39 // Delaunay2D normal constructor
40
41 fX = x;
42 fY = y;
43 fZ = z;
44 fZout = 0.;
45 fNpoints = n;
46 fOffsetX = 0;
47 fOffsetY = 0;
48 fScaleFactorX = 0;
49 fScaleFactorY = 0;
50 fNdt = 0;
51 fXNmax = 0;
52 fXNmin = 0;
53 fYNmin = 0;
54 fYNmax = 0;
55
57
58}
59
60/// set the input points
61void Delaunay2D::SetInputPoints(int n, const double * x, const double * y, const double * z,
62 double xmin, double xmax, double ymin, double ymax) {
63
64
65 fInit = kFALSE;
66
67 if (n == 0 || !x || !y || !z ) return;
68
69 if (xmin >= xmax) {
70 xmin = *std::min_element(x, x+n);
71 xmax = *std::max_element(x, x+n);
72
73 ymin = *std::min_element(y, y+n);
74 ymax = *std::max_element(y, y+n);
75 }
76
77 fOffsetX = -(xmax+xmin)/2.;
78 fOffsetY = -(ymax+ymin)/2.;
79
80 if ( (xmax-xmin) != 0 ) {
81 fScaleFactorX = 1./(xmax-xmin);
82 fXNmax = Linear_transform(xmax, fOffsetX, fScaleFactorX); //xTransformer(xmax);
83 fXNmin = Linear_transform(xmin, fOffsetX, fScaleFactorX); //xTransformer(xmin);
84 } else {
85 // we can't find triangle in this case
86 fInit = true;
87 }
88
89 if (ymax-ymin != 0) {
90 fScaleFactorY = 1./(ymax-ymin);
91 fYNmax = Linear_transform(ymax, fOffsetY, fScaleFactorY); //yTransformer(ymax);
92 fYNmin = Linear_transform(ymin, fOffsetY, fScaleFactorY); //yTransformer(ymin);
93 } else {
94 fInit = true;
95 }
96 //printf("Normalized space extends from (%f,%f) to (%f,%f)\n", fXNmin, fYNmin, fXNmax, fYNmax);
97
98
99#ifndef HAS_CGAL
100 fXCellStep = 0.;
101 fYCellStep = 0.;
102#endif
103}
104
105//______________________________________________________________________________
106double Delaunay2D::Interpolate(double x, double y)
107{
108 // Return the interpolated z value corresponding to the given (x,y) point
109
110 // Initialise the Delaunay algorithm if needed.
111 // CreateTrianglesDataStructure computes fXoffset, fYoffset,
112 // fXScaleFactor and fYScaleFactor;
113 // needed in this function.
115
116 // handle case no triangles are found return default value (i.e. 0)
117 // to do: if points are aligned in one direction we could return in
118 // some case the 1d interpolated value
119 if (fNdt == 0) {
120 return fZout;
121 }
122
123 // Find the z value corresponding to the point (x,y).
124 double xx, yy;
125 xx = Linear_transform(x, fOffsetX, fScaleFactorX); //xx = xTransformer(x);
126 yy = Linear_transform(y, fOffsetY, fScaleFactorY); //yy = yTransformer(y);
127 double zz = DoInterpolateNormalized(xx, yy);
128
129 // the case of points on a regular grid (i.e. points on triangle edges) it is now handles in
130 // DoInterpolateNormalized
131
132 return zz;
133}
134
135//______________________________________________________________________________
137{
138
139 if (fInit)
140 return;
141 else
142 fInit = kTRUE;
143
144 // Function used internally only. It creates the data structures needed to
145 // compute the Delaunay triangles.
146
147 // Offset fX and fY so they average zero, and scale so the average
148 // of the X and Y ranges is one. The normalized version of fX and fY used
149 // in Interpolate.
150
151 DoNormalizePoints(); // call backend specific point normalization
152
153 DoFindTriangles(); // call backend specific triangle finding
154
155 fNdt = fTriangles.size();
156}
157
158// backend specific implementations
159
160#ifndef HAS_CGAL
161
162// Triangle implementation (default case)
163
164/// Triangle implementation for points normalization
166 for (Int_t n = 0; n < fNpoints; n++) {
169 }
170
171 //also initialize fXCellStep and FYCellStep
174}
175
176/// Triangle implementation for finding all the triangles
178
179 auto initStruct = [] (triangulateio & s) {
180 s.pointlist = nullptr; /* In / out */
181 s.pointattributelist = nullptr; /* In / out */
182 s.pointmarkerlist = nullptr; /* In / out */
183 s.numberofpoints = 0; /* In / out */
184 s.numberofpointattributes = 0; /* In / out */
185
186 s.trianglelist = nullptr; /* In / out */
187 s.triangleattributelist = nullptr; /* In / out */
188 s.trianglearealist = nullptr; /* In only */
189 s.neighborlist = nullptr; /* Out only */
190 s.numberoftriangles = 0; /* In / out */
191 s.numberofcorners = 0; /* In / out */
192 s.numberoftriangleattributes = 0; /* In / out */
193
194 s.segmentlist = nullptr; /* In / out */
195 s.segmentmarkerlist = nullptr; /* In / out */
196 s.numberofsegments = 0; /* In / out */
197
198 s.holelist = nullptr; /* In / pointer to array copied out */
199 s.numberofholes = 0; /* In / copied out */
200
201 s.regionlist = nullptr; /* In / pointer to array copied out */
202 s.numberofregions = 0; /* In / copied out */
203
204 s.edgelist = nullptr; /* Out only */
205 s.edgemarkerlist = nullptr; /* Not used with Voronoi diagram; out only */
206 s.normlist = nullptr; /* Used only with Voronoi diagram; out only */
207 s.numberofedges = 0; /* Out only */
208 };
209
210 auto freeStruct = [] (triangulateio & s) {
211 if(s.pointlist != nullptr) free(s.pointlist); /* In / out */
212 if(s.pointattributelist != nullptr) free(s.pointattributelist); /* In / out */
213 if(s.pointmarkerlist != nullptr) free(s.pointmarkerlist); /* In / out */
214
215 if(s.trianglelist != nullptr) free(s.trianglelist); /* In / out */
216 if(s.triangleattributelist != nullptr) free(s.triangleattributelist); /* In / out */
217 if(s.trianglearealist != nullptr) free(s.trianglearealist); /* In only */
218 if(s.neighborlist != nullptr) free(s.neighborlist); /* Out only */
219
220 if(s.segmentlist != nullptr) free(s.segmentlist); /* In / out */
221 if(s.segmentmarkerlist != nullptr) free(s.segmentmarkerlist); /* In / out */
222
223 if(s.holelist != nullptr) free(s.holelist); /* In / pointer to array copied out */
224
225 if(s.regionlist != nullptr) free(s.regionlist); /* In / pointer to array copied out */
226
227 if(s.edgelist != nullptr) free(s.edgelist); /* Out only */
228 if(s.edgemarkerlist != nullptr) free(s.edgemarkerlist); /* Not used with Voronoi diagram; out only */
229 if(s.normlist != nullptr) free(s.normlist); /* Used only with Voronoi diagram; out only */
230 };
231
232 struct triangulateio in, out;
233 initStruct(in); initStruct(out);
234
235 /* Define input points. */
236
238 in.pointlist = (REAL *) malloc(in.numberofpoints * 2 * sizeof(REAL));
239
240 for (Int_t i = 0; i < fNpoints; ++i) {
241 in.pointlist[2 * i] = fXN[i];
242 in.pointlist[2 * i + 1] = fYN[i];
243 }
244
245 triangulate((char *) "zQN", &in, &out, nullptr);
246
247 fTriangles.resize(out.numberoftriangles);
248 for(int t = 0; t < out.numberoftriangles; ++t){
249 Triangle tri;
250
251 auto transform = [&] (const unsigned int v) {
252 //each triangle as numberofcorners vertices ( = 3)
253 tri.idx[v] = out.trianglelist[t*out.numberofcorners + v];
254
255 //printf("triangle %u vertex %u: point %u/%i\n", t, v, tri.idx[v], out.numberofpoints);
256
257 //pointlist is [x0 y0 x1 y1 ...]
258 tri.x[v] = in.pointlist[tri.idx[v] * 2 + 0];
259
260 //printf("\t x: %f\n", tri.x[v]);
261
262 tri.y[v] = in.pointlist[tri.idx[v] * 2 + 1];
263
264 //printf("\t y: %f\n", tri.y[v]);
265 };
266
267 transform(0);
268 transform(1);
269 transform(2);
270
271 //see comment in header for CGAL fallback section
272 tri.invDenom = 1 / ( (tri.y[1] - tri.y[2])*(tri.x[0] - tri.x[2]) + (tri.x[2] - tri.x[1])*(tri.y[0] - tri.y[2]) );
273
274 fTriangles[t] = tri;
275
276 auto bx = std::minmax({tri.x[0], tri.x[1], tri.x[2]});
277 auto by = std::minmax({tri.y[0], tri.y[1], tri.y[2]});
278
279 unsigned int cellXmin = CellX(bx.first);
280 unsigned int cellXmax = CellX(bx.second);
281
282 unsigned int cellYmin = CellY(by.first);
283 unsigned int cellYmax = CellY(by.second);
284
285 for(unsigned int i = cellXmin; i <= cellXmax; ++i) {
286 for(unsigned int j = cellYmin; j <= cellYmax; ++j) {
287 //printf("(%u,%u) = %u\n", i, j, Cell(i,j));
288 fCells[Cell(i,j)].insert(t);
289 }
290 }
291 }
292
293 freeStruct(in); freeStruct(out);
294}
295
296/// Triangle implementation for interpolation
297/// Finds the Delaunay triangle that the point (xi,yi) sits in (if any) and
298/// calculate a z-value for it by linearly interpolating the z-values that
299/// make up that triangle.
300/// Relay that all the triangles have been found before
301/// see comment in class description (in Delaunay2D.h) for implementation details:
302/// finding barycentric coordinates and computing the interpolation
303double Delaunay2D::DoInterpolateNormalized(double xx, double yy)
304{
305
306 // compute barycentric coordinates of a point P(xx,yy,zz)
307 auto bayCoords = [&](const unsigned int t) -> std::tuple<double, double, double> {
308 double la = ((fTriangles[t].y[1] - fTriangles[t].y[2]) * (xx - fTriangles[t].x[2]) +
309 (fTriangles[t].x[2] - fTriangles[t].x[1]) * (yy - fTriangles[t].y[2])) *
310 fTriangles[t].invDenom;
311 double lb = ((fTriangles[t].y[2] - fTriangles[t].y[0]) * (xx - fTriangles[t].x[2]) +
312 (fTriangles[t].x[0] - fTriangles[t].x[2]) * (yy - fTriangles[t].y[2])) *
313 fTriangles[t].invDenom;
314
315 return std::make_tuple(la, lb, (1 - la - lb));
316 };
317
318 // function to test if a point with barycentric coordinates (a,b,c) is inside the triangle
319 // If the point is outside one or more of the coordinate are negative.
320 // If the point is on a triangle edge, one of the coordinate (the one not part of the edge) is zero.
321 // Due to numerical error, it can happen that if the point is at the edge the result is a small negative value.
322 // Use then a tolerance (of - eps) to still consider the point within the triangle
323 auto inTriangle = [](const std::tuple<double, double, double> &coords) -> bool {
324 constexpr double eps = -4 * std::numeric_limits<double>::epsilon();
325 return std::get<0>(coords) > eps && std::get<1>(coords) > eps && std::get<2>(coords) > eps;
326 };
327
328 int cX = CellX(xx);
329 int cY = CellY(yy);
330
331 if (cX < 0 || cX > fNCells || cY < 0 || cY > fNCells)
332 return fZout; // TODO some more fancy interpolation here
333
334 for (unsigned int t : fCells[Cell(cX, cY)]) {
335
336 auto coords = bayCoords(t);
337
338 // std::cout << "result of bayCoords " << std::get<0>(coords) <<
339 // " " << std::get<1>(coords) << " " << std::get<2>(coords) << std::endl;
340
341 if (inTriangle(coords)) {
342
343 // we found the triangle -> interpolate using the barycentric interpolation
344
345 return std::get<0>(coords) * fZ[fTriangles[t].idx[0]] + std::get<1>(coords) * fZ[fTriangles[t].idx[1]] +
346 std::get<2>(coords) * fZ[fTriangles[t].idx[2]];
347 }
348 }
349
350 // printf("Could not find a triangle for point (%f,%f)\n", xx, yy);
351
352 // no triangle found return standard value
353 return fZout;
354}
355
356#else //HAS_CGAL: case of using GCAL
357
358/// CGAL implementation of normalize points
359void Delaunay2D::DonormalizePoints() {
360 for (Int_t n = 0; n < fNpoints; n++) {
361 //Point p(xTransformer(fX[n]), yTransformer(fY[n]));
362 Point p(linear_transform(fX[n], fOffsetX, fScaleFactorX),
363 linear_transform(fY[n], fOffsetY, fScaleFactorY));
364
365 fNormalizedPoints.insert(std::make_pair(p, n));
366 }
367}
368
369/// CGAL implementation for finding triangles
371 fCGALdelaunay.insert(fNormalizedPoints.begin(), fNormalizedPoints.end());
372
373 std::transform(fCGALdelaunay.finite_faces_begin(),
374 fCGALdelaunay.finite_faces_end(), std::back_inserter(fTriangles),
375 [] (const Delaunay::Face face) -> Triangle {
376
377 Triangle tri;
378
379 auto transform = [&] (const unsigned int i) {
380 tri.x[i] = face.vertex(i)->point().x();
381 tri.y[i] = face.vertex(i)->point().y();
382 tri.idx[i] = face.vertex(i)->info();
383 };
384
385 transform(0);
386 transform(1);
387 transform(2);
388
389 return tri;
390
391 });
392}
393
394/// CGAL implementation for interpolation
395double Delaunay2D::DoInterpolateNormalized(double xx, double yy)
396{
397 // Finds the Delaunay triangle that the point (xi,yi) sits in (if any) and
398 // calculate a z-value for it by linearly interpolating the z-values that
399 // make up that triangle.
400
401 // initialise the Delaunay algorithm if needed
402 FindAllTriangles();
403
404 //coordinate computation
405 Point p(xx, yy);
406
407 std::vector<std::pair<Point, Coord_type> > coords;
408 auto nn = CGAL::natural_neighbor_coordinates_2(fCGALdelaunay, p,
409 std::back_inserter(coords));
410
411 //std::cout << std::this_thread::get_id() << ": Found " << coords.size() << " points" << std::endl;
412
413 if(!nn.third) //neighbor finding was NOT successful, return standard value
414 return fZout;
415
416 //printf("found neighbors %u\n", coords.size());
417
418 Coord_type res = CGAL::linear_interpolation(coords.begin(), coords.end(),
419 nn.second, Value_access(fNormalizedPoints, fZ));
420
421 //std::cout << std::this_thread::get_id() << ": Result " << res << std::endl;
422
423 return res;
424}
425#endif // HAS_GCAL
426
427} // namespace Math
428} // namespace ROOT
int Int_t
Definition RtypesCore.h:45
constexpr Bool_t kFALSE
Definition RtypesCore.h:101
constexpr Bool_t kTRUE
Definition RtypesCore.h:100
winID h TVirtualViewer3D TVirtualGLPainter p
float xmin
float ymin
float xmax
float ymax
#define free
Definition civetweb.c:1539
#define malloc
Definition civetweb.c:1536
int fNpoints
! Number of data points
Definition Delaunay2D.h:197
double fXNmin
! Minimum value of fXN
Definition Delaunay2D.h:203
std::set< unsigned int > fCells[(fNCells+1) *(fNCells+1)]
! grid cells with containing triangles
Definition Delaunay2D.h:231
int fNdt
! Number of Delaunay triangles found
Definition Delaunay2D.h:196
double DoInterpolateNormalized(double x, double y)
internal method to compute the interpolation
int CellY(double y) const
Definition Delaunay2D.h:241
double Interpolate(double x, double y)
Return the Interpolated z value corresponding to the given (x,y) point.
const double * fZ
! Pointer to Z array
Definition Delaunay2D.h:201
double fXNmax
! Maximum value of fXN
Definition Delaunay2D.h:204
double fZout
! Height for points lying outside the convex hull
Definition Delaunay2D.h:214
double fOffsetY
! Normalization offset Y
Definition Delaunay2D.h:209
Triangles fTriangles
! Triangles of Triangulation
Definition Delaunay2D.h:219
double Linear_transform(double x, double offset, double factor)
Definition Delaunay2D.h:172
double fYCellStep
! inverse denominator to calculate X cell = fNCells / (fYNmax - fYNmin)
Definition Delaunay2D.h:230
std::vector< double > fYN
! normalized Y
Definition Delaunay2D.h:226
Delaunay2D(int n, const double *x, const double *y, const double *z, double xmin=0, double xmax=0, double ymin=0, double ymax=0)
class constructor from array of data points
void FindAllTriangles()
Find all triangles.
static const int fNCells
! number of cells to divide the normalized space
Definition Delaunay2D.h:228
bool fInit
! True if FindAllTriangles() has been performed
Definition Delaunay2D.h:216
unsigned int Cell(unsigned int x, unsigned int y) const
Definition Delaunay2D.h:233
std::vector< double > fXN
! normalized X
Definition Delaunay2D.h:225
double fXCellStep
! inverse denominator to calculate X cell = fNCells / (fXNmax - fXNmin)
Definition Delaunay2D.h:229
double fYNmin
! Minimum value of fYN
Definition Delaunay2D.h:205
double fOffsetX
! Normalization offset X
Definition Delaunay2D.h:208
const double * fY
! Pointer to Y array
Definition Delaunay2D.h:200
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)
set the input points for building the graph
const double * fX
! Pointer to X array (managed externally)
Definition Delaunay2D.h:199
void DoNormalizePoints()
internal function to normalize the points.
double fYNmax
! Maximum value of fYN
Definition Delaunay2D.h:206
double fScaleFactorY
! Normalization factor Y
Definition Delaunay2D.h:212
double fScaleFactorX
! Normalization factor X
Definition Delaunay2D.h:211
void DoFindTriangles()
internal function to find the triangle use Triangle or CGAL if flag is set
int CellX(double x) const
Definition Delaunay2D.h:237
Double_t y[n]
Definition legend1.C:17
Double_t x[n]
Definition legend1.C:17
const Int_t n
Definition legend1.C:16
Namespace for new Math classes and functions.
This file contains a specialised ROOT message handler to test for diagnostic in unit tests.
int numberofpoints
Definition triangle.h:292
double * pointlist
Definition triangle.h:289
void triangulate(char *triswitches, struct triangulateio *in, struct triangulateio *out, struct triangulateio *vorout)
Definition triangle.c:15666
#define REAL
Definition triangle.h:277