QWT API (中文) 7.3.0
Qt绘图库 - 中文API文档
载入中...
搜索中...
未找到
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
8#include <vector>
9#include <algorithm>
10#include <limits>
11#include <cmath>
12#include <cassert>
13#include <stdexcept>
14
15#include "qwt_math.h"
16
88template< typename T,
89 typename XContainer = std::vector< T >,
90 typename YContainer = std::vector< T >,
91 typename DataColumn = std::vector< T >,
92 typename DataContainer = std::vector< DataColumn > >
94{
95public:
96 using value_type = T;
97 using x_container_type = XContainer;
98 using y_container_type = YContainer;
99 using data_column_type = DataColumn;
100 using data_container_type = DataContainer;
101 using size_type = typename DataColumn::size_type;
111 {
112 NearestNeighbour,
113 BilinearInterpolation,
114 BicubicInterpolation
115 };
116
123 : 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)
124 {
125 }
126
138 const y_container_type& yAxis,
140 ResampleMode mode = NearestNeighbour)
141 : m_xAxis(xAxis), m_yAxis(yAxis), m_data(data), m_mode(mode)
142 {
143 validate();
145 m_xMin = xAxis.front();
146 m_xMax = xAxis.back();
147 m_yMin = yAxis.front();
148 m_yMax = yAxis.back();
149 }
150
176 {
177 m_xAxis = xAxis;
178 m_yAxis = yAxis;
179 m_data = data;
180 validate();
182 m_xMin = xAxis.front();
183 m_xMax = xAxis.back();
184 m_yMin = yAxis.front();
185 m_yMax = yAxis.back();
186 }
187
195 T operator()(T x, T y) const
196 {
197 return value(x, y);
198 }
199
208 T operator[](const std::pair< T, T >& xy) const
209 {
210 return value(xy.first, xy.second);
211 }
219 T value(T x, T y) const
220 {
221 switch (m_mode) {
222 case NearestNeighbour:
223 return nearestNeighbour(x, y);
224 case BilinearInterpolation:
225 return bilinearInterpolation(x, y);
226 case BicubicInterpolation:
227 return bicubicInterpolation(x, y);
228 default:
229 throw std::runtime_error("Unknown resampling mode.");
230 }
231 }
232
239 {
240 m_mode = mode;
241 }
242
249 {
250 return m_mode;
251 }
252
257 size_type xSize() const
258 {
259 return m_xAxis.size();
260 }
261
266 size_type ySize() const
267 {
268 return m_yAxis.size();
269 }
270
275 std::pair< size_type, size_type > valueSize() const
276 {
277 return std::make_pair(xSize(), ySize());
278 }
279
285 T atX(size_type ix) const
286 {
287 return m_xAxis.at(ix);
288 }
289
295 T atY(size_type iy) const
296 {
297 return m_yAxis.at(iy);
298 }
299
305 T atValue(size_type ix, size_type iy) const
306 {
307 return m_data.at(ix).at(iy);
308 }
309
315 const x_container_type& xAxis() const
316 {
317 return m_xAxis;
318 }
319
325 const y_container_type& yAxis() const
326 {
327 return m_yAxis;
328 }
329
336 {
337 return m_data;
338 }
339
345 bool valid() const
346 {
347 if (m_xAxis.empty() || m_yAxis.empty() || m_data.empty()) {
348 return false;
349 }
350 if (m_xAxis.size() != m_data.size()) {
351 return false;
352 }
353 for (const auto& column : m_data) {
354 if (column.size() != m_yAxis.size()) {
355 return false;
356 }
357 }
358 if (!std::is_sorted(m_xAxis.begin(), m_xAxis.end())) {
359 return false;
360 }
361 if (!std::is_sorted(m_yAxis.begin(), m_yAxis.end())) {
362 return false;
363 }
364 return true;
365 }
366
370 void validate()
371 {
372 if (m_xAxis.empty() || m_yAxis.empty() || m_data.empty()) {
373 throw std::invalid_argument("Axes or data cannot be empty.");
374 }
375 if (m_data.size() != m_xAxis.size()) {
376 throw std::invalid_argument("Number of columns in data must match x-axis size.");
377 }
378 for (const auto& column : m_data) {
379 if (column.size() != m_yAxis.size()) {
380 throw std::invalid_argument("Number of rows in data must match y-axis size.");
381 }
382 }
383 if (!std::is_sorted(m_xAxis.begin(), m_xAxis.end())) {
384 std::sort(m_xAxis.begin(), m_xAxis.end());
385 }
386 if (!std::is_sorted(m_yAxis.begin(), m_yAxis.end())) {
387 std::sort(m_yAxis.begin(), m_yAxis.end());
388 }
389 }
390
391 T xMin() const
392 {
393 return m_xMin;
394 }
395 T xMax() const
396 {
397 return m_xMax;
398 }
399 T yMin() const
400 {
401 return m_yMin;
402 }
403 T yMax() const
404 {
405 return m_yMax;
406 }
407 T dataMin() const
408 {
409 return m_dataMin;
410 }
411 T dataMax() const
412 {
413 return m_dataMax;
414 }
415
416public:
417 // static pulic function
425 template< typename Container >
426 static size_type findClosestIndex(const Container& arr, T val)
427 {
428 auto it = std::lower_bound(arr.begin(), arr.end(), val);
429 if (it == arr.begin())
430 return 0;
431 if (it == arr.end())
432 return arr.size() - 1;
433 size_type idx = std::distance(arr.begin(), it);
434 return (std::abs(arr[ idx ] - val) < std::abs(arr[ idx - 1 ] - val)) ? idx : idx - 1;
435 }
436
444 template< typename Container >
445 static size_type findLowerIndex(const Container& arr, T val)
446 {
447 auto it = std::lower_bound(arr.begin(), arr.end(), val);
448 if (it == arr.begin())
449 return 0;
450 return std::distance(arr.begin(), it) - 1;
451 }
452
453 template< typename V >
454 static const V& clamp(const V& value, const V& lo, const V& hi)
455 {
456 return (value < lo) ? lo : (hi < value) ? hi : value;
457 }
458
459protected:
466 {
467 m_dataMin = std::numeric_limits< T >::max();
468 m_dataMax = std::numeric_limits< T >::lowest();
469 bool found = false;
470 for (const auto& column : m_data) {
471 for (const auto& val : column) {
472 if (qwt_is_nan_or_inf(val))
473 continue;
474 m_dataMin = std::min(m_dataMin, val);
475 m_dataMax = std::max(m_dataMax, val);
476 found = true;
477 }
478 }
479 if (!found) {
480 m_dataMin = std::numeric_limits< T >::quiet_NaN();
481 m_dataMax = std::numeric_limits< T >::quiet_NaN();
482 }
483 }
484
493 T nearestNeighbour(T x, T y) const
494 {
495 size_type xIdx = findClosestIndex(m_xAxis, x);
496 size_type yIdx = findClosestIndex(m_yAxis, y);
497 return m_data[ xIdx ][ yIdx ];
498 }
499
508 T bilinearInterpolation(T x, T y) const
509 {
510 size_type x0Idx = findLowerIndex(m_xAxis, x);
511 size_type x1Idx = x0Idx + 1;
512 size_type y0Idx = findLowerIndex(m_yAxis, y);
513 size_type y1Idx = y0Idx + 1;
514
515 T x0 = m_xAxis[ x0Idx ], x1 = m_xAxis[ x1Idx ];
516 T y0 = m_yAxis[ y0Idx ], y1 = m_yAxis[ y1Idx ];
517
518 T f00 = m_data[ x0Idx ][ y0Idx ];
519 T f10 = m_data[ x1Idx ][ y0Idx ];
520 T f01 = m_data[ x0Idx ][ y1Idx ];
521 T f11 = m_data[ x1Idx ][ y1Idx ];
522
523 T dx = (x - x0) / (x1 - x0);
524 T dy = (y - y0) / (y1 - y0);
525
526 return (1 - dx) * (1 - dy) * f00 + dx * (1 - dy) * f10 + (1 - dx) * dy * f01 + dx * dy * f11;
527 }
528
539 T bicubicInterpolation(T x, T y) const
540 {
541 // Find surrounding x indices
542 auto xIt = std::lower_bound(m_xAxis.begin(), m_xAxis.end(), x);
543 size_type x0, x1, x2, x3;
544 T xWeight;
545
546 if (xIt == m_xAxis.begin()) {
547 x0 = 0;
548 x1 = 0;
549 x2 = 1;
550 x3 = 2;
551 xWeight = 0.0;
552 } else if (xIt == m_xAxis.end()) {
553 x0 = m_xAxis.size() - 3;
554 x1 = m_xAxis.size() - 2;
555 x2 = m_xAxis.size() - 1;
556 x3 = m_xAxis.size() - 1;
557 xWeight = 1.0;
558 } else {
559 x0 = xIt - 2 - m_xAxis.begin();
560 if (x0 < 0)
561 x0 = 0;
562 x1 = xIt - 1 - m_xAxis.begin();
563 x2 = xIt - m_xAxis.begin();
564 x3 = xIt + 1 - m_xAxis.begin();
565 if (x3 >= m_xAxis.size())
566 x3 = m_xAxis.size() - 1;
567 xWeight = static_cast< T >(x - m_xAxis[ x1 ]) / (m_xAxis[ x2 ] - m_xAxis[ x1 ]);
568 }
569
570 // Find surrounding y indices
571 auto yIt = std::lower_bound(m_yAxis.begin(), m_yAxis.end(), y);
572 size_type y0, y1, y2, y3;
573 T yWeight;
574
575 if (yIt == m_yAxis.begin()) {
576 y0 = 0;
577 y1 = 0;
578 y2 = 1;
579 y3 = 2;
580 yWeight = 0.0;
581 } else if (yIt == m_yAxis.end()) {
582 y0 = m_yAxis.size() - 3;
583 y1 = m_yAxis.size() - 2;
584 y2 = m_yAxis.size() - 1;
585 y3 = m_yAxis.size() - 1;
586 yWeight = 1.0;
587 } else {
588 y0 = yIt - 2 - m_yAxis.begin();
589 if (y0 < 0)
590 y0 = 0;
591 y1 = yIt - 1 - m_yAxis.begin();
592 y2 = yIt - m_yAxis.begin();
593 y3 = yIt + 1 - m_yAxis.begin();
594 if (y3 >= m_yAxis.size())
595 y3 = m_yAxis.size() - 1;
596 yWeight = static_cast< T >(y - m_yAxis[ y1 ]) / (m_yAxis[ y2 ] - m_yAxis[ y1 ]);
597 }
598
599 // Hermite basis functions
600 auto h00 = [](T t) { return (1 + 2 * t) * (1 - t) * (1 - t); };
601 auto h10 = [](T t) { return t * (1 - t) * (1 - t); };
602 auto h01 = [](T t) { return t * t * (3 - 2 * t); };
603 auto h11 = [](T t) { return t * t * (t - 1); };
604
605 // Interpolate in x direction for each y position
606 T values[ 4 ][ 4 ];
607 for (int i = 0; i < 4; ++i) {
608 // Get the four y values for current x positions
609 T v[ 4 ];
610 v[ 0 ] = m_data[ x0 ][ y0 + i ];
611 v[ 1 ] = m_data[ x1 ][ y0 + i ];
612 v[ 2 ] = m_data[ x2 ][ y0 + i ];
613 v[ 3 ] = m_data[ x3 ][ y0 + i ];
614
615 // Interpolate in x direction
616 values[ 0 ][ i ] = h00(xWeight) * v[ 1 ]
617 + h10(xWeight) * (m_xAxis[ x2 ] - m_xAxis[ x1 ])
618 * ((v[ 2 ] - v[ 1 ]) / (m_xAxis[ x2 ] - m_xAxis[ x1 ])
619 + (v[ 2 ] - v[ 1 ] - (v[ 1 ] - v[ 0 ]) / (m_xAxis[ x1 ] - m_xAxis[ x0 ]))
620 / (m_xAxis[ x2 ] - m_xAxis[ x0 ]) * (m_xAxis[ x2 ] - m_xAxis[ x1 ]))
621 + h01(xWeight) * v[ 2 ]
622 + h11(xWeight) * (m_xAxis[ x2 ] - m_xAxis[ x1 ])
623 * ((v[ 2 ] - v[ 1 ]) / (m_xAxis[ x2 ] - m_xAxis[ x1 ])
624 + (v[ 3 ] - v[ 2 ] - (v[ 2 ] - v[ 1 ]) / (m_xAxis[ x2 ] - m_xAxis[ x1 ]))
625 / (m_xAxis[ x3 ] - m_xAxis[ x1 ]) * (m_xAxis[ x2 ] - m_xAxis[ x1 ]));
626 }
627
628 // Interpolate in y direction
629 T v[ 4 ];
630 v[ 0 ] = values[ 0 ][ 0 ];
631 v[ 1 ] = values[ 0 ][ 1 ];
632 v[ 2 ] = values[ 0 ][ 2 ];
633 v[ 3 ] = values[ 0 ][ 3 ];
634
635 return h00(yWeight) * v[ 1 ]
636 + h10(yWeight) * (m_yAxis[ y2 ] - m_yAxis[ y1 ])
637 * ((v[ 2 ] - v[ 1 ]) / (m_yAxis[ y2 ] - m_yAxis[ y1 ])
638 + (v[ 2 ] - v[ 1 ] - (v[ 1 ] - v[ 0 ]) / (m_yAxis[ y1 ] - m_yAxis[ y0 ]))
639 / (m_yAxis[ y2 ] - m_yAxis[ y0 ]) * (m_yAxis[ y2 ] - m_yAxis[ y1 ]))
640 + h01(yWeight) * v[ 2 ]
641 + h11(yWeight) * (m_yAxis[ y2 ] - m_yAxis[ y1 ])
642 * ((v[ 2 ] - v[ 1 ]) / (m_yAxis[ y2 ] - m_yAxis[ y1 ])
643 + (v[ 3 ] - v[ 2 ] - (v[ 2 ] - v[ 1 ]) / (m_yAxis[ y2 ] - m_yAxis[ y1 ]))
644 / (m_yAxis[ y3 ] - m_yAxis[ y1 ]) * (m_yAxis[ y2 ] - m_yAxis[ y1 ]));
645 }
646
647private:
648 x_container_type m_xAxis;
649 y_container_type m_yAxis;
666 data_container_type m_data;
667 ResampleMode m_mode;
668 T m_xMin, m_xMax, m_yMin, m_yMax, m_dataMin, m_dataMax;
669};
670
671#endif // QWT_GRID_DATA_HPP
A generic container class for storing 2D grid data and providing resampling methods.
Definition qwt_grid_data.hpp:94
T value(T x, T y) const
Query value at (x, y).
Definition qwt_grid_data.hpp:219
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:175
size_type xSize() const
Size of x-axis
Definition qwt_grid_data.hpp:257
std::pair< size_type, size_type > valueSize() const
Size of the value matrix
Definition qwt_grid_data.hpp:275
T atY(size_type iy) const
Value at y-axis index
Definition qwt_grid_data.hpp:295
size_type ySize() const
Size of y-axis
Definition qwt_grid_data.hpp:266
ResampleMode resampleMode() const
Get the current resampling mode.
Definition qwt_grid_data.hpp:248
QwtGridData()
Default constructor.
Definition qwt_grid_data.hpp:122
XContainer x_container_type
Type of the x-axis container
Definition qwt_grid_data.hpp:97
void validate()
Validate the data.
Definition qwt_grid_data.hpp:370
T value_type
Type of the stored values
Definition qwt_grid_data.hpp:96
void findValueRange()
Get the minimum & maximum value in the data matrix.
Definition qwt_grid_data.hpp:465
T operator[](const std::pair< T, T > &xy) const
operator []
Definition qwt_grid_data.hpp:208
const y_container_type & yAxis() const
Get the y-axis values.
Definition qwt_grid_data.hpp:325
void setResampleMode(ResampleMode mode)
Set the resampling mode.
Definition qwt_grid_data.hpp:238
T bilinearInterpolation(T x, T y) const
Bilinear interpolation.
Definition qwt_grid_data.hpp:508
T atValue(size_type ix, size_type iy) const
Value at matrix index
Definition qwt_grid_data.hpp:305
T nearestNeighbour(T x, T y) const
Nearest neighbor interpolation.
Definition qwt_grid_data.hpp:493
YContainer y_container_type
Type of the y-axis container
Definition qwt_grid_data.hpp:98
static size_type findClosestIndex(const Container &arr, T val)
Find the closest index in a sorted array.
Definition qwt_grid_data.hpp:426
const data_container_type & data() const
Get the data matrix.
Definition qwt_grid_data.hpp:335
const x_container_type & xAxis() const
Get the x-axis values.
Definition qwt_grid_data.hpp:315
DataColumn data_column_type
Type of a single column in the data matrix
Definition qwt_grid_data.hpp:99
T operator()(T x, T y) const
Operator to query value at (x, y).
Definition qwt_grid_data.hpp:195
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:137
bool valid() const
Check if the object is valid.
Definition qwt_grid_data.hpp:345
static size_type findLowerIndex(const Container &arr, T val)
Find the lower bound index in a sorted array.
Definition qwt_grid_data.hpp:445
ResampleMode
Enumeration for resampling methods.
Definition qwt_grid_data.hpp:111
T atX(size_type ix) const
Value at x-axis index
Definition qwt_grid_data.hpp:285
DataContainer data_container_type
Type of the data matrix
Definition qwt_grid_data.hpp:100
T bicubicInterpolation(T x, T y) const
Perform bicubic interpolation.
Definition qwt_grid_data.hpp:539