QWT 7.0.1
Loading...
Searching...
No Matches
qwt_grid_data.hpp
1/******************************************************************************
2 * Qwt Widget Library
3 * Copyright (C) 2024 ChenZongYan <czy.t@163.com>
4 *****************************************************************************/
5#ifndef QWT_GRID_DATA_HPP
6#define QWT_GRID_DATA_HPP
7#include <vector>
8#include <algorithm>
9#include <limits>
10#include <cmath>
11#include <cassert>
12#include <stdexcept>
13
89template< typename T,
90 typename XContainer = std::vector< T >,
91 typename YContainer = std::vector< T >,
92 typename DataColumn = std::vector< T >,
93 typename DataContainer = std::vector< DataColumn > >
95{
96public:
97 using value_type = T;
98 using x_container_type = XContainer;
99 using y_container_type = YContainer;
100 using data_column_type = DataColumn;
101 using data_container_type = DataContainer;
102 using size_type = typename DataColumn::size_type;
117 {
118 NearestNeighbour,
119 BilinearInterpolation,
120 BicubicInterpolation
121 };
122
129 : m_mode(NearestNeighbour), m_xMin(0.0), m_xMax(0.0), m_yMin(0.0), m_yMax(0.0), m_dataMax(0.0), m_dataMin(0.0)
130 {
131 }
132
146 const y_container_type& yAxis,
148 ResampleMode mode = NearestNeighbour)
149 : m_xAxis(xAxis), m_yAxis(yAxis), m_data(data), m_mode(mode)
150 {
151 validate();
153 m_xMin = xAxis.front();
154 m_xMax = xAxis.back();
155 m_yMin = yAxis.front();
156 m_yMax = yAxis.back();
157 }
158
187 {
188 m_xAxis = xAxis;
189 m_yAxis = yAxis;
190 m_data = data;
191 validate();
193 m_xMin = xAxis.front();
194 m_xMax = xAxis.back();
195 m_yMin = yAxis.front();
196 m_yMax = yAxis.back();
197 }
198
208 T operator()(T x, T y) const
209 {
210 return value(x, y);
211 }
212
221 T operator[](const std::pair< T, T >& xy) const
222 {
223 return value(xy.first, xy.second);
224 }
234 T value(T x, T y) const
235 {
236 switch (m_mode) {
237 case NearestNeighbour:
238 return nearestNeighbour(x, y);
239 case BilinearInterpolation:
240 return bilinearInterpolation(x, y);
241 case BicubicInterpolation:
242 return bicubicInterpolation(x, y);
243 default:
244 throw std::runtime_error("Unknown resampling mode.");
245 }
246 }
247
256 {
257 m_mode = mode;
258 }
259
268 {
269 return m_mode;
270 }
271
276 size_type xSize() const
277 {
278 return m_xAxis.size();
279 }
280
285 size_type ySize() const
286 {
287 return m_yAxis.size();
288 }
289
294 std::pair< size_type, size_type > valueSize() const
295 {
296 return std::make_pair(xSize(), ySize());
297 }
298
304 T atX(size_type ix) const
305 {
306 return m_xAxis.at(ix);
307 }
308
314 T atY(size_type iy) const
315 {
316 return m_yAxis.at(iy);
317 }
318
324 T atValue(size_type ix, size_type iy) const
325 {
326 return m_data.at(ix).at(iy);
327 }
328
336 const x_container_type& xAxis() const
337 {
338 return m_xAxis;
339 }
340
348 const y_container_type& yAxis() const
349 {
350 return m_yAxis;
351 }
352
361 {
362 return m_data;
363 }
364
372 bool valid() const
373 {
374 if (m_xAxis.empty() || m_yAxis.empty() || m_data.empty()) {
375 return false;
376 }
377 if (m_xAxis.size() != m_data.size()) {
378 return false;
379 }
380 for (const auto& column : m_data) {
381 if (column.size() != m_yAxis.size()) {
382 return false;
383 }
384 }
385 if (!std::is_sorted(m_xAxis.begin(), m_xAxis.end())) {
386 return false;
387 }
388 if (!std::is_sorted(m_yAxis.begin(), m_yAxis.end())) {
389 return false;
390 }
391 return true;
392 }
393
399 void validate()
400 {
401 if (m_xAxis.empty() || m_yAxis.empty() || m_data.empty()) {
402 throw std::invalid_argument("Axes or data cannot be empty.");
403 }
404 if (m_data.size() != m_xAxis.size()) {
405 throw std::invalid_argument("Number of columns in data must match x-axis size.");
406 }
407 for (const auto& column : m_data) {
408 if (column.size() != m_yAxis.size()) {
409 throw std::invalid_argument("Number of rows in data must match y-axis size.");
410 }
411 }
412 if (!std::is_sorted(m_xAxis.begin(), m_xAxis.end())) {
413 std::sort(m_xAxis.begin(), m_xAxis.end());
414 }
415 if (!std::is_sorted(m_yAxis.begin(), m_yAxis.end())) {
416 std::sort(m_yAxis.begin(), m_yAxis.end());
417 }
418 }
419
420 T xMin() const
421 {
422 return m_xMin;
423 }
424 T xMax() const
425 {
426 return m_xMax;
427 }
428 T yMin() const
429 {
430 return m_yMin;
431 }
432 T yMax() const
433 {
434 return m_yMax;
435 }
436 T dataMin() const
437 {
438 return m_dataMin;
439 }
440 T dataMax() const
441 {
442 return m_dataMax;
443 }
444
445public:
446 // static pulic function
456 template< typename Container >
457 static size_type findClosestIndex(const Container& arr, T val)
458 {
459 auto it = std::lower_bound(arr.begin(), arr.end(), val);
460 if (it == arr.begin())
461 return 0;
462 if (it == arr.end())
463 return arr.size() - 1;
464 size_type idx = std::distance(arr.begin(), it);
465 return (std::abs(arr[ idx ] - val) < std::abs(arr[ idx - 1 ] - val)) ? idx : idx - 1;
466 }
467
477 template< typename Container >
478 static size_type findLowerIndex(const Container& arr, T val)
479 {
480 auto it = std::lower_bound(arr.begin(), arr.end(), val);
481 if (it == arr.begin())
482 return 0;
483 return std::distance(arr.begin(), it) - 1;
484 }
485
486 template< typename V >
487 static const V& clamp(const V& value, const V& lo, const V& hi)
488 {
489 return (value < lo) ? lo : (hi < value) ? hi : value;
490 }
491
492protected:
501 {
502 m_dataMin = std::numeric_limits< T >::max();
503 m_dataMax = std::numeric_limits< T >::lowest();
504 for (const auto& column : m_data) {
505 for (const auto& val : column) {
506 m_dataMin = std::min(m_dataMin, val);
507 m_dataMax = std::max(m_dataMax, val);
508 }
509 }
510 }
511
521 T nearestNeighbour(T x, T y) const
522 {
523 size_type xIdx = findClosestIndex(m_xAxis, x);
524 size_type yIdx = findClosestIndex(m_yAxis, y);
525 return m_data[ xIdx ][ yIdx ];
526 }
527
537 T bilinearInterpolation(T x, T y) const
538 {
539 size_type x0Idx = findLowerIndex(m_xAxis, x);
540 size_type x1Idx = x0Idx + 1;
541 size_type y0Idx = findLowerIndex(m_yAxis, y);
542 size_type y1Idx = y0Idx + 1;
543
544 T x0 = m_xAxis[ x0Idx ], x1 = m_xAxis[ x1Idx ];
545 T y0 = m_yAxis[ y0Idx ], y1 = m_yAxis[ y1Idx ];
546
547 T f00 = m_data[ x0Idx ][ y0Idx ];
548 T f10 = m_data[ x1Idx ][ y0Idx ];
549 T f01 = m_data[ x0Idx ][ y1Idx ];
550 T f11 = m_data[ x1Idx ][ y1Idx ];
551
552 T dx = (x - x0) / (x1 - x0);
553 T dy = (y - y0) / (y1 - y0);
554
555 return (1 - dx) * (1 - dy) * f00 + dx * (1 - dy) * f10 + (1 - dx) * dy * f01 + dx * dy * f11;
556 }
557
571 T bicubicInterpolation(T x, T y) const
572 {
573 // Find surrounding x indices
574 auto xIt = std::lower_bound(m_xAxis.begin(), m_xAxis.end(), x);
575 size_type x0, x1, x2, x3;
576 T xWeight;
577
578 if (xIt == m_xAxis.begin()) {
579 x0 = 0;
580 x1 = 0;
581 x2 = 1;
582 x3 = 2;
583 xWeight = 0.0;
584 } else if (xIt == m_xAxis.end()) {
585 x0 = m_xAxis.size() - 3;
586 x1 = m_xAxis.size() - 2;
587 x2 = m_xAxis.size() - 1;
588 x3 = m_xAxis.size() - 1;
589 xWeight = 1.0;
590 } else {
591 x0 = xIt - 2 - m_xAxis.begin();
592 if (x0 < 0)
593 x0 = 0;
594 x1 = xIt - 1 - m_xAxis.begin();
595 x2 = xIt - m_xAxis.begin();
596 x3 = xIt + 1 - m_xAxis.begin();
597 if (x3 >= m_xAxis.size())
598 x3 = m_xAxis.size() - 1;
599 xWeight = static_cast< T >(x - m_xAxis[ x1 ]) / (m_xAxis[ x2 ] - m_xAxis[ x1 ]);
600 }
601
602 // Find surrounding y indices
603 auto yIt = std::lower_bound(m_yAxis.begin(), m_yAxis.end(), y);
604 size_type y0, y1, y2, y3;
605 T yWeight;
606
607 if (yIt == m_yAxis.begin()) {
608 y0 = 0;
609 y1 = 0;
610 y2 = 1;
611 y3 = 2;
612 yWeight = 0.0;
613 } else if (yIt == m_yAxis.end()) {
614 y0 = m_yAxis.size() - 3;
615 y1 = m_yAxis.size() - 2;
616 y2 = m_yAxis.size() - 1;
617 y3 = m_yAxis.size() - 1;
618 yWeight = 1.0;
619 } else {
620 y0 = yIt - 2 - m_yAxis.begin();
621 if (y0 < 0)
622 y0 = 0;
623 y1 = yIt - 1 - m_yAxis.begin();
624 y2 = yIt - m_yAxis.begin();
625 y3 = yIt + 1 - m_yAxis.begin();
626 if (y3 >= m_yAxis.size())
627 y3 = m_yAxis.size() - 1;
628 yWeight = static_cast< T >(y - m_yAxis[ y1 ]) / (m_yAxis[ y2 ] - m_yAxis[ y1 ]);
629 }
630
631 // Hermite basis functions
632 auto h00 = [](T t) { return (1 + 2 * t) * (1 - t) * (1 - t); };
633 auto h10 = [](T t) { return t * (1 - t) * (1 - t); };
634 auto h01 = [](T t) { return t * t * (3 - 2 * t); };
635 auto h11 = [](T t) { return t * t * (t - 1); };
636
637 // Interpolate in x direction for each y position
638 T values[ 4 ][ 4 ];
639 for (int i = 0; i < 4; ++i) {
640 // Get the four y values for current x positions
641 T v[ 4 ];
642 v[ 0 ] = m_data[ x0 ][ y0 + i ];
643 v[ 1 ] = m_data[ x1 ][ y0 + i ];
644 v[ 2 ] = m_data[ x2 ][ y0 + i ];
645 v[ 3 ] = m_data[ x3 ][ y0 + i ];
646
647 // Interpolate in x direction
648 values[ 0 ][ i ] = h00(xWeight) * v[ 1 ]
649 + h10(xWeight) * (m_xAxis[ x2 ] - m_xAxis[ x1 ])
650 * ((v[ 2 ] - v[ 1 ]) / (m_xAxis[ x2 ] - m_xAxis[ x1 ])
651 + (v[ 2 ] - v[ 1 ] - (v[ 1 ] - v[ 0 ]) / (m_xAxis[ x1 ] - m_xAxis[ x0 ]))
652 / (m_xAxis[ x2 ] - m_xAxis[ x0 ]) * (m_xAxis[ x2 ] - m_xAxis[ x1 ]))
653 + h01(xWeight) * v[ 2 ]
654 + h11(xWeight) * (m_xAxis[ x2 ] - m_xAxis[ x1 ])
655 * ((v[ 2 ] - v[ 1 ]) / (m_xAxis[ x2 ] - m_xAxis[ x1 ])
656 + (v[ 3 ] - v[ 2 ] - (v[ 2 ] - v[ 1 ]) / (m_xAxis[ x2 ] - m_xAxis[ x1 ]))
657 / (m_xAxis[ x3 ] - m_xAxis[ x1 ]) * (m_xAxis[ x2 ] - m_xAxis[ x1 ]));
658 }
659
660 // Interpolate in y direction
661 T v[ 4 ];
662 v[ 0 ] = values[ 0 ][ 0 ];
663 v[ 1 ] = values[ 0 ][ 1 ];
664 v[ 2 ] = values[ 0 ][ 2 ];
665 v[ 3 ] = values[ 0 ][ 3 ];
666
667 return h00(yWeight) * v[ 1 ]
668 + h10(yWeight) * (m_yAxis[ y2 ] - m_yAxis[ y1 ])
669 * ((v[ 2 ] - v[ 1 ]) / (m_yAxis[ y2 ] - m_yAxis[ y1 ])
670 + (v[ 2 ] - v[ 1 ] - (v[ 1 ] - v[ 0 ]) / (m_yAxis[ y1 ] - m_yAxis[ y0 ]))
671 / (m_yAxis[ y2 ] - m_yAxis[ y0 ]) * (m_yAxis[ y2 ] - m_yAxis[ y1 ]))
672 + h01(yWeight) * v[ 2 ]
673 + h11(yWeight) * (m_yAxis[ y2 ] - m_yAxis[ y1 ])
674 * ((v[ 2 ] - v[ 1 ]) / (m_yAxis[ y2 ] - m_yAxis[ y1 ])
675 + (v[ 3 ] - v[ 2 ] - (v[ 2 ] - v[ 1 ]) / (m_yAxis[ y2 ] - m_yAxis[ y1 ]))
676 / (m_yAxis[ y3 ] - m_yAxis[ y1 ]) * (m_yAxis[ y2 ] - m_yAxis[ y1 ]));
677 }
678
679private:
680 x_container_type m_xAxis;
681 y_container_type m_yAxis;
698 data_container_type m_data;
699 ResampleMode m_mode;
700 T m_xMin, m_xMax, m_yMin, m_yMax, m_dataMin, m_dataMax;
701};
702
703#endif // QWT_GRID_DATA_HPP
A generic container class for storing 2D grid data and providing resampling methods.
Definition qwt_grid_data.hpp:95
T value(T x, T y) const
Query value at (x, y).
Definition qwt_grid_data.hpp:234
void setValue(const x_container_type &xAxis, const y_container_type &yAxis, const data_container_type &data)
Set new x-axis, y-axis, and data matrix.
Definition qwt_grid_data.hpp:186
size_type xSize() const
x的尺寸
Definition qwt_grid_data.hpp:276
std::pair< size_type, size_type > valueSize() const
value矩阵的尺寸
Definition qwt_grid_data.hpp:294
T atY(size_type iy) const
y值对应的内容
Definition qwt_grid_data.hpp:314
size_type ySize() const
y的尺寸
Definition qwt_grid_data.hpp:285
ResampleMode resampleMode() const
Get the current resampling mode.
Definition qwt_grid_data.hpp:267
QwtGridData()
Default constructor.
Definition qwt_grid_data.hpp:128
XContainer x_container_type
Type of the x-axis container / x 轴容器的类型
Definition qwt_grid_data.hpp:98
void validate()
Validate the data.
Definition qwt_grid_data.hpp:399
T value_type
Type of the stored values / 存储值的类型
Definition qwt_grid_data.hpp:97
void findValueRange()
Get the minimum & maximum value in the data matrix.
Definition qwt_grid_data.hpp:500
T operator[](const std::pair< T, T > &xy) const
operator []
Definition qwt_grid_data.hpp:221
const y_container_type & yAxis() const
Get the y-axis values.
Definition qwt_grid_data.hpp:348
void setResampleMode(ResampleMode mode)
Set the resampling mode.
Definition qwt_grid_data.hpp:255
T bilinearInterpolation(T x, T y) const
Bilinear interpolation.
Definition qwt_grid_data.hpp:537
T atValue(size_type ix, size_type iy) const
value值对应的内容
Definition qwt_grid_data.hpp:324
T nearestNeighbour(T x, T y) const
Nearest neighbor interpolation.
Definition qwt_grid_data.hpp:521
YContainer y_container_type
Type of the y-axis container / y 轴容器的类型
Definition qwt_grid_data.hpp:99
static size_type findClosestIndex(const Container &arr, T val)
Find the closest index in a sorted array.
Definition qwt_grid_data.hpp:457
const data_container_type & data() const
Get the data matrix.
Definition qwt_grid_data.hpp:360
const x_container_type & xAxis() const
Get the x-axis values.
Definition qwt_grid_data.hpp:336
DataColumn data_column_type
Type of a single column in the data matrix / 数据矩阵中单列的类型
Definition qwt_grid_data.hpp:100
T operator()(T x, T y) const
Operator to query value at (x, y).
Definition qwt_grid_data.hpp:208
QwtGridData(const x_container_type &xAxis, const y_container_type &yAxis, const data_container_type &data, ResampleMode mode=NearestNeighbour)
Constructor with initial data.
Definition qwt_grid_data.hpp:145
bool valid() const
Check if the object is valid.
Definition qwt_grid_data.hpp:372
static size_type findLowerIndex(const Container &arr, T val)
Find the lower bound index in a sorted array.
Definition qwt_grid_data.hpp:478
ResampleMode
Enumeration for resampling methods.
Definition qwt_grid_data.hpp:117
T atX(size_type ix) const
x 值对应的内容
Definition qwt_grid_data.hpp:304
DataContainer data_container_type
Type of the data matrix / 数据矩阵的类型
Definition qwt_grid_data.hpp:101
T bicubicInterpolation(T x, T y) const
Perform bicubic interpolation.
Definition qwt_grid_data.hpp:571