source: trunk/Mars/mcore/Interpolator2D.h@ 17121

Last change on this file since 17121 was 17121, checked in by tbretz, 11 years ago
A class for a bi-linear inter- and extrapolation.
File size: 11.8 KB
Line 
1// **************************************************************************
2/** @class Interpolator2D
3
4@brief Extra- and interpolate in 2D
5
6This class implements a kind of Delaunay triangulation. It calculated the
7Voronoi points and the corresponding Delaunay triangles. Within each
8triangle a bi-linear interpolation is provided.
9
10A special selection criterion is applied for points outside the grid,
11so that extrapolation is possible. Note that extrapolation of far away
12points (as in the 1D case) is not recommended.
13
14*/
15// **************************************************************************
16#ifndef FACT_Interpolator2D
17#define FACT_Interpolator2D
18
19#include <vector>
20
21class Interpolator2D
22{
23public:
24 struct vec
25 {
26 double x;
27 double y;
28
29 vec(double _x=0, double _y=0) : x(_x), y(_y) { }
30
31 vec orto() const { return vec(-y, x); }
32
33 double dist(const vec &v) const { return hypot(x-v.x, y-v.y); }
34 double operator^(const vec &v) const { return x*v.y - y*v.x; }
35 vec operator-(const vec &v) const { return vec(x-v.x, y-v.y); }
36 vec operator+(const vec &v) const { return vec(x+v.x, y+v.y); }
37 vec operator/(double b) const { return vec(x/b, y/b); }
38 };
39
40
41 struct point : vec
42 {
43 unsigned int i;
44 point(unsigned int _i=0, double _x=0, double _y=0) : vec(_x, _y), i(_i) { }
45 };
46
47 struct circle : point
48 {
49 point p[3];
50 double r;
51
52 static bool sameSide(const vec &p1, const vec &p2, const vec &a, const vec &b)
53 {
54 return ((b-a)^(p1-a))*((b-a)^(p2-a)) > 0;
55 }
56
57 bool isInsideTriangle(const vec &v) const
58 {
59 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]);
60 }
61
62 bool isInsideCircle(const vec &v) const
63 {
64 return dist(v) < r;
65 }
66 };
67
68 struct weight : point
69 {
70 circle c;
71 double w[3];
72 };
73
74private:
75 std::vector<point> inputGrid; /// positions of the data points (e.g. sensors)
76 std::vector<point> outputGrid; /// positions at which inter-/extrapolated values should be provided
77 std::vector<circle> circles; /// the calculated circles/triangles
78 std::vector<weight> weights; /// the weights used for the interpolation
79
80 // --------------------------------------------------------------------------
81 //
82 //! Calculate the collection of circles/triangles which describe the
83 //! input grid. This is the collection of circles which are calculated
84 //! from any three points and do not contain any other point of the grid.
85 //
86 void CalculateGrid()
87 {
88 circles.reserve(2*inputGrid.size());
89
90 // Loop over all triplets of points
91 for (auto it0=inputGrid.cbegin(); it0<inputGrid.cend(); it0++)
92 {
93 for (auto it1=inputGrid.cbegin(); it1<it0; it1++)
94 {
95 for (auto it2=inputGrid.cbegin(); it2<it1; it2++)
96 {
97 // Calculate the circle through the three points
98
99 // Vectors along the side of the corresponding triangle
100 const vec v1 = *it1 - *it0;
101 const vec v2 = *it2 - *it1;
102
103 // Orthogonal vectors on the sides
104 const vec n1 = v1.orto();
105 const vec n2 = v2.orto();
106
107 // Center point of two of the three sides
108 const vec p1 = (*it0 + *it1)/2;
109 const vec p2 = (*it1 + *it2)/2;
110
111 // Calculate the crossing point of the two
112 // orthogonal vectors originating in the
113 // center of the sides.
114 const double denom = n1^n2;
115 if (denom==0)
116 continue;
117
118 const vec x(n1.x, n2.x);
119 const vec y(n1.y, n2.y);
120
121 const vec w(p1^(p1+n1), p2^(p2+n2));
122
123 circle c;
124
125 // This is the x and y coordinate of the circle
126 // through the three points and the circle's radius.
127 c.x = (x^w)/denom;
128 c.y = (y^w)/denom;
129 c.r = c.dist(*it1);
130
131 // Check if any other grid point lays within this circle
132 auto it3 = inputGrid.cbegin();
133 for (; it3<inputGrid.cend(); it3++)
134 {
135 if (it3==it0 || it3==it1 || it3==it2)
136 continue;
137
138 if (c.isInsideCircle(*it3))
139 break;
140 }
141
142 // If a point was found inside, reject the circle
143 if (it3!=inputGrid.cend())
144 continue;
145
146 // Store the three points of the triangle
147 c.p[0] = *it0;
148 c.p[1] = *it1;
149 c.p[2] = *it2;
150
151 // Keep in list
152 circles.push_back(c);
153 }
154 }
155 }
156 }
157
158 // --------------------------------------------------------------------------
159 //
160 //! Calculate the weights corresponding to the points in the output grid.
161 //! Weights are calculated by bi-linear interpolation. For interpolation,
162 //! the triangle which contains the point and has the smallest radius
163 //! is searched. If this is not available in case of extrapolation,
164 //! the condition is relaxed and requires only the circle to contain
165 //! the point. If such circle is not available, the circle with the
166 //! closest center is chosen.
167 //
168 bool CalculateWeights()
169 {
170 weights.reserve(outputGrid.size());
171
172 // Loop over all points in the output grid
173 for (auto ip=outputGrid.cbegin(); ip<outputGrid.cend(); ip++)
174 {
175 double mindd = FLT_MAX;
176
177 auto mint = circles.cend();
178 auto minc = circles.cend();
179 auto mind = circles.cend();
180
181 for (auto ic=circles.cbegin(); ic<circles.cend(); ic++)
182 {
183 // Check if point is inside the triangle
184 if (ic->isInsideTriangle(*ip))
185 {
186 if (mint==circles.cend() || ic->r<mint->r)
187 mint = ic;
188 }
189
190 // If we have found such a triangle, no need to check for more
191 if (mint!=circles.cend())
192 continue;
193
194 // maybe at least inside the circle
195 const double dd = ic->dist(*ip);
196 if (dd<ic->r)
197 {
198 if (minc==circles.cend() || ic->r<minc->r)
199 minc = ic;
200 }
201
202 // If we found such a circle, no need to check for more
203 if (minc!=circles.cend())
204 continue;
205
206 // then look for the closest circle center
207 if (dd<mindd)
208 {
209 mindd = dd;
210 mind = ic;
211 }
212 }
213
214 // Choose the best of the three options
215 const auto it = mint==circles.cend() ? (minc==circles.cend() ? mind : minc) : mint;
216 if (it==circles.cend())
217 return false;
218
219 // Calculate the bi-linear interpolation
220 const vec &p1 = it->p[0];
221 const vec &p2 = it->p[1];
222 const vec &p3 = it->p[2];
223
224 const double dy23 = p2.y - p3.y;
225 const double dy31 = p3.y - p1.y;
226 const double dy12 = p1.y - p2.y;
227
228 const double dx32 = p3.x - p2.x;
229 const double dx13 = p1.x - p3.x;
230 const double dx21 = p2.x - p1.x;
231
232 const double dxy23 = p2^p3;
233 const double dxy31 = p3^p1;
234 const double dxy12 = p1^p2;
235
236 const double det = dxy12 + dxy23 + dxy31;
237
238 const double w1 = (dy23*ip->x + dx32*ip->y + dxy23)/det;
239 const double w2 = (dy31*ip->x + dx13*ip->y + dxy31)/det;
240 const double w3 = (dy12*ip->x + dx21*ip->y + dxy12)/det;
241
242 // Store the original grid-point, the circle's parameters
243 // and the calculate weights
244 weight w;
245 w.x = ip->x;
246 w.y = ip->y;
247 w.c = *it;
248 w.w[0] = w1;
249 w.w[1] = w2;
250 w.w[2] = w3;
251
252 weights.push_back(w);
253 }
254
255 return true;
256 }
257
258public:
259 // --------------------------------------------------------------------------
260 //
261 //! Default constructor. Does nothing.
262 //
263 Interpolator2D()
264 {
265 }
266
267 // --------------------------------------------------------------------------
268 //
269 //! Initialize the input grid (the points at which values are known).
270 //!
271 //! @param n
272 //! number of data points
273 //!
274 //! @param x
275 //! x coordinates of data points
276 //!
277 //! @param n
278 //! y coordinates of data points
279 //
280 Interpolator2D(int n, double *x, double *y)
281 {
282 SetInputGrid(n, x, y);
283 }
284
285 const std::vector<Interpolator2D::weight> getWeights() const { return weights; }
286 const std::vector<Interpolator2D::point> getInputGrid() const { return inputGrid; }
287 const std::vector<Interpolator2D::point> getOutputGrid() const { return outputGrid; }
288
289 // --------------------------------------------------------------------------
290 //
291 //! Set a new input grid (the points at which values are known).
292 //! Invalidates the output grid and the calculated weights.
293 //! Calculates the triangles corresponding to the new grid.
294 //!
295 //! @param n
296 //! number of data points
297 //!
298 //! @param x
299 //! x coordinates of data points
300 //!
301 //! @param n
302 //! y coordinates of data points
303 //
304 void SetInputGrid(int n, double *x, double *y)
305 {
306 circles.clear();
307 weights.clear();
308 outputGrid.clear();
309
310 inputGrid.clear();
311 inputGrid.reserve(n);
312 for (int i=0; i<n; i++)
313 inputGrid.emplace_back(i, x[i], y[i]);
314
315 CalculateGrid();
316 }
317
318 // --------------------------------------------------------------------------
319 //
320 //! Set a new output grid (the points at which you want interpolated
321 //! or extrapolated values). Calculates new weights.
322 //!
323 //! @param n
324 //! number of points
325 //!
326 //! @param x
327 //! x coordinates of points
328 //!
329 //! @param n
330 //! y coordinates of points
331 //!
332 //! @returns
333 //! false if the calculation of the weights failed, true in
334 //! case of success
335 //
336 bool SetOutputGrid(int n, double *x, double *y)
337 {
338 if (inputGrid.empty())
339 return false;
340
341 weights.clear();
342
343 outputGrid.clear();
344 outputGrid.reserve(n);
345 for (int i=0; i<n; i++)
346 outputGrid.emplace_back(i, x[i], y[i]);
347
348 return CalculateWeights();
349 }
350
351 // --------------------------------------------------------------------------
352 //
353 //! Perform interpolation.
354 //!
355 //! @param z
356 //! Values at the coordinates of the input grid. The order
357 //! must be identical.
358 //!
359 //! @returns
360 //! A vector<double> is returned with the interpolated values in the
361 //! same order than the putput grid. If the provided vector does
362 //! not match the size of the inputGrid, an empty vector is returned.
363 //
364 std::vector<double> Interpolate(const vector<double> &z) const
365 {
366 if (z.size()!=inputGrid.size())
367 return std::vector<double>();
368
369 std::vector<double> rc;
370 rc.reserve(z.size());
371
372 for (auto it=weights.cbegin(); it<weights.cend(); it++)
373 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]);
374
375 return rc;
376 }
377};
378#endif
Note: See TracBrowser for help on using the repository browser.