FACT++  1.0
Interpolator2D.h
Go to the documentation of this file.
1 // **************************************************************************
15 // **************************************************************************
16 #ifndef FACT_Interpolator2D
17 #define FACT_Interpolator2D
18 
19 #include <float.h>
20 #include <math.h>
21 #include <vector>
22 #include <fstream>
23 
25 {
26 public:
27  struct vec
28  {
29  double x;
30  double y;
31 
32  vec(double _x=0, double _y=0) : x(_x), y(_y) { }
33 
34  vec orto() const { return vec(-y, x); }
35 
36  double dist(const vec &v) const { return hypot(x-v.x, y-v.y); }
37  double operator^(const vec &v) const { return x*v.y - y*v.x; }
38  vec operator-(const vec &v) const { return vec(x-v.x, y-v.y); }
39  vec operator+(const vec &v) const { return vec(x+v.x, y+v.y); }
40  vec operator/(double b) const { return vec(x/b, y/b); }
41  };
42 
43 
44  struct point : vec
45  {
46  unsigned int i;
47  point(unsigned int _i, const vec &_v) : vec(_v), i(_i) { }
48  point(unsigned int _i=0, double _x=0, double _y=0) : vec(_x, _y), i(_i) { }
49  };
50 
51  struct circle : point
52  {
53  point p[3];
54  double r;
55 
56  static bool sameSide(const vec &p1, const vec &p2, const vec &a, const vec &b)
57  {
58  return ((b-a)^(p1-a))*((b-a)^(p2-a)) > 0;
59  }
60 
61  bool isInsideTriangle(const vec &v) const
62  {
63  return sameSide(v, p[0], p[1], p[2]) && sameSide(v, p[1], p[0], p[2]) && sameSide(v, p[2], p[0], p[1]);
64  }
65 
66  bool isInsideCircle(const vec &v) const
67  {
68  return dist(v) < r;
69  }
70  };
71 
72  struct weight : point
73  {
75  double w[3];
76  };
77 
78 private:
79  std::vector<point> inputGrid;
80  std::vector<point> outputGrid;
81  std::vector<circle> circles;
82  std::vector<weight> weights;
83 
84  // --------------------------------------------------------------------------
85  //
89  //
91  {
92  circles.reserve(2*inputGrid.size());
93 
94  // Loop over all triplets of points
95  for (auto it0=inputGrid.cbegin(); it0<inputGrid.cend(); it0++)
96  {
97  for (auto it1=inputGrid.cbegin(); it1<it0; it1++)
98  {
99  for (auto it2=inputGrid.cbegin(); it2<it1; it2++)
100  {
101  // Calculate the circle through the three points
102 
103  // Vectors along the side of the corresponding triangle
104  const vec v1 = *it1 - *it0;
105  const vec v2 = *it2 - *it1;
106 
107  // Orthogonal vectors on the sides
108  const vec n1 = v1.orto();
109  const vec n2 = v2.orto();
110 
111  // Center point of two of the three sides
112  const vec p1 = (*it0 + *it1)/2;
113  const vec p2 = (*it1 + *it2)/2;
114 
115  // Calculate the crossing point of the two
116  // orthogonal vectors originating in the
117  // center of the sides.
118  const double denom = n1^n2;
119  if (denom==0)
120  continue;
121 
122  const vec x(n1.x, n2.x);
123  const vec y(n1.y, n2.y);
124 
125  const vec w(p1^(p1+n1), p2^(p2+n2));
126 
127  circle c;
128 
129  // This is the x and y coordinate of the circle
130  // through the three points and the circle's radius.
131  c.x = (x^w)/denom;
132  c.y = (y^w)/denom;
133  c.r = c.dist(*it1);
134 
135  // Check if any other grid point lays within this circle
136  auto it3 = inputGrid.cbegin();
137  for (; it3<inputGrid.cend(); it3++)
138  {
139  if (it3==it0 || it3==it1 || it3==it2)
140  continue;
141 
142  if (c.isInsideCircle(*it3))
143  break;
144  }
145 
146  // If a point was found inside, reject the circle
147  if (it3!=inputGrid.cend())
148  continue;
149 
150  // Store the three points of the triangle
151  c.p[0] = *it0;
152  c.p[1] = *it1;
153  c.p[2] = *it2;
154 
155  // Keep in list
156  circles.push_back(c);
157  }
158  }
159  }
160  }
161 
162  // --------------------------------------------------------------------------
163  //
171  //
173  {
174  weights.reserve(outputGrid.size());
175 
176  // Loop over all points in the output grid
177  for (auto ip=outputGrid.cbegin(); ip<outputGrid.cend(); ip++)
178  {
179  double mindd = DBL_MAX;
180 
181  auto mint = circles.cend();
182  auto minc = circles.cend();
183  auto mind = circles.cend();
184 
185  for (auto ic=circles.cbegin(); ic<circles.cend(); ic++)
186  {
187  // Check if point is inside the triangle
188  if (ic->isInsideTriangle(*ip))
189  {
190  if (mint==circles.cend() || ic->r<mint->r)
191  mint = ic;
192  }
193 
194  // If we have found such a triangle, no need to check for more
195  if (mint!=circles.cend())
196  continue;
197 
198  // maybe at least inside the circle
199  const double dd = ic->dist(*ip);
200  if (dd<ic->r)
201  {
202  if (minc==circles.cend() || ic->r<minc->r)
203  minc = ic;
204  }
205 
206  // If we found such a circle, no need to check for more
207  if (minc!=circles.cend())
208  continue;
209 
210  // then look for the closest circle center
211  if (dd<mindd)
212  {
213  mindd = dd;
214  mind = ic;
215  }
216  }
217 
218  // Choose the best of the three options
219  const auto it = mint==circles.cend() ? (minc==circles.cend() ? mind : minc) : mint;
220  if (it==circles.cend())
221  return false;
222 
223  // Calculate the bi-linear interpolation
224  const vec &p1 = it->p[0];
225  const vec &p2 = it->p[1];
226  const vec &p3 = it->p[2];
227 
228  const double dy23 = p2.y - p3.y;
229  const double dy31 = p3.y - p1.y;
230  const double dy12 = p1.y - p2.y;
231 
232  const double dx32 = p3.x - p2.x;
233  const double dx13 = p1.x - p3.x;
234  const double dx21 = p2.x - p1.x;
235 
236  const double dxy23 = p2^p3;
237  const double dxy31 = p3^p1;
238  const double dxy12 = p1^p2;
239 
240  const double det = dxy12 + dxy23 + dxy31;
241 
242  const double w1 = (dy23*ip->x + dx32*ip->y + dxy23)/det;
243  const double w2 = (dy31*ip->x + dx13*ip->y + dxy31)/det;
244  const double w3 = (dy12*ip->x + dx21*ip->y + dxy12)/det;
245 
246  // Store the original grid-point, the circle's parameters
247  // and the calculate weights
248  weight w;
249  w.x = ip->x;
250  w.y = ip->y;
251  w.c = *it;
252  w.w[0] = w1;
253  w.w[1] = w2;
254  w.w[2] = w3;
255 
256  weights.push_back(w);
257  }
258 
259  return true;
260  }
261 
262 public:
263  // --------------------------------------------------------------------------
264  //
266  //
268  {
269  }
270 
271  // --------------------------------------------------------------------------
272  //
283  //
284  Interpolator2D(int n, double *x, double *y)
285  {
286  SetInputGrid(n, x, y);
287  }
288 
289  Interpolator2D(const std::vector<Interpolator2D::vec> &v)
290  {
291  SetInputGrid(v);
292  }
293 
294  const std::vector<Interpolator2D::weight> getWeights() const { return weights; }
295  const std::vector<Interpolator2D::point> getInputGrid() const { return inputGrid; }
296  const std::vector<Interpolator2D::point> getOutputGrid() const { return outputGrid; }
297 
298  // --------------------------------------------------------------------------
299  //
309  //
310  static std::vector<Interpolator2D::vec> ReadGrid(const std::string &filename)
311  {
312  std::vector<Interpolator2D::vec> grid;
313 
314  std::ifstream fin(filename);
315  if (!fin.is_open())
316  return std::vector<Interpolator2D::vec>();
317 
318  while (1)
319  {
320  double x, y;
321  fin >> x;
322  fin >> y;
323  if (!fin)
324  break;
325 
326  grid.emplace_back(x, y);
327  }
328 
329  return fin.bad() ? std::vector<Interpolator2D::vec>() : grid;
330  }
331 
332  // --------------------------------------------------------------------------
333  //
346  //
347  void SetInputGrid(unsigned int n, double *x, double *y)
348  {
349  circles.clear();
350  weights.clear();
351  outputGrid.clear();
352 
353  inputGrid.clear();
354  inputGrid.reserve(n);
355  for (unsigned int i=0; i<n; i++)
356  inputGrid.emplace_back(i, x[i], y[i]);
357 
358  CalculateGrid();
359  }
360 
361  void SetInputGrid(const std::vector<Interpolator2D::vec> &v)
362  {
363  circles.clear();
364  weights.clear();
365  outputGrid.clear();
366 
367  inputGrid.clear();
368  inputGrid.reserve(v.size());
369  for (unsigned int i=0; i<v.size(); i++)
370  inputGrid.emplace_back(i, v[i]);
371 
372  CalculateGrid();
373  }
374 
375  /*
376  void SetInputGrid(const std::vector<Interpolator2D::point> &v)
377  {
378  circles.clear();
379  weights.clear();
380  outputGrid.clear();
381 
382  inputGrid.clear();
383  inputGrid.reserve(v.size());
384  for (unsigned int i=0; i<v.size(); i++)
385  inputGrid.emplace_back(v[i], i);
386 
387  CalculateGrid();
388  }*/
389 
390  bool ReadInputGrid(const std::string &filename)
391  {
392  const auto grid = ReadGrid(filename);
393  if (grid.empty())
394  return false;
395 
396  SetInputGrid(grid);
397  return true;
398  }
399 
400 
401  // --------------------------------------------------------------------------
402  //
418  //
419  bool SetOutputGrid(std::size_t n, double *x, double *y)
420  {
421  if (inputGrid.empty() && n==0)
422  return false;
423 
424  weights.clear();
425 
426  outputGrid.clear();
427  outputGrid.reserve(n);
428  for (std::size_t i=0; i<n; i++)
429  outputGrid.emplace_back(i, x[i], y[i]);
430 
431  return CalculateWeights();
432  }
433 
434  /*
435  bool SetOutputGrid(const std::vector<std::pair<double,double>> &v)
436  {
437  if (inputGrid.empty() || v.empty())
438  return false;
439 
440  weights.clear();
441 
442  outputGrid.clear();
443  outputGrid.reserve(v.size());
444  for (std::size_t i=0; i<v.size(); i++)
445  outputGrid.emplace_back(i, v[i].first, v[i].second);
446 
447  return CalculateWeights();
448  }*/
449 
450  bool SetOutputGrid(const std::vector<Interpolator2D::vec> &v)
451  {
452  if (inputGrid.empty())
453  return false;
454 
455  weights.clear();
456 
457  outputGrid.clear();
458  outputGrid.reserve(v.size());
459  for (std::size_t i=0; i<v.size(); i++)
460  outputGrid.emplace_back(i, v[i]);
461 
462  return CalculateWeights();
463  }
464 
465  bool ReadOutputGrid(const std::string &filename)
466  {
467  const auto grid = ReadGrid(filename);
468  if (grid.empty())
469  return false;
470 
471  return SetOutputGrid(grid);
472  }
473 
474 
475  // --------------------------------------------------------------------------
476  //
487  //
488  std::vector<double> Interpolate(const std::vector<double> &z) const
489  {
490  if (z.size()!=inputGrid.size())
491  return std::vector<double>();
492 
493  std::vector<double> rc;
494  rc.reserve(z.size());
495 
496  for (auto it=weights.cbegin(); it<weights.cend(); it++)
497  rc.push_back(z[it->c.p[0].i] * it->w[0] + z[it->c.p[1].i] * it->w[1] + z[it->c.p[2].i] * it->w[2]);
498 
499  return rc;
500  }
501 };
502 #endif
std::vector< double > Interpolate(const std::vector< double > &z) const
bool SetOutputGrid(std::size_t n, double *x, double *y)
bool isInsideTriangle(const vec &v) const
bool ReadInputGrid(const std::string &filename)
void CalculateGrid()
the weights used for the interpolation
int i
Definition: db_dim_client.c:21
Interpolator2D()
Default constructor. Does nothing.
bool ReadOutputGrid(const std::string &filename)
static bool sameSide(const vec &p1, const vec &p2, const vec &a, const vec &b)
const std::vector< Interpolator2D::point > getInputGrid() const
const std::vector< Interpolator2D::point > getOutputGrid() const
void SetInputGrid(unsigned int n, double *x, double *y)
vec operator+(const vec &v) const
Extra- and interpolate in 2D.
double dist(const vec &v) const
vec operator/(double b) const
std::vector< point > outputGrid
positions of the data points (e.g. sensors)
bool CalculateWeights()
point(unsigned int _i, const vec &_v)
Interpolator2D(int n, double *x, double *y)
void SetInputGrid(const std::vector< Interpolator2D::vec > &v)
vec operator-(const vec &v) const
std::vector< point > inputGrid
std::vector< circle > circles
positions at which inter-/extrapolated values should be provided
point(unsigned int _i=0, double _x=0, double _y=0)
std::vector< weight > weights
the calculated circles/triangles
const std::vector< Interpolator2D::weight > getWeights() const
Interpolator2D(const std::vector< Interpolator2D::vec > &v)
bool SetOutputGrid(const std::vector< Interpolator2D::vec > &v)
bool isInsideCircle(const vec &v) const
static std::vector< Interpolator2D::vec > ReadGrid(const std::string &filename)
vec(double _x=0, double _y=0)
double operator^(const vec &v) const