Skip to main content
标签ad报错:该广告ID(9)不存在。
  主页 > Qt入门

一个Qt/C++ Demo,用于实现一个用于显示等高线图的控件效果

2023-04-23 浏览:
标签ad报错:该广告ID(7)不存在。
#pragma once

#include <QtWidgets>

class ContourPlotWidget : public QWidget {
public:
   explicit ContourPlotWidget(QWidget* parent = nullptr) :
       QWidget(parent),
       m_zValues(nullptr),
       m_nx(0),
       m_ny(0),
       m_nContours(10),
       m_minZ(0.0),
       m_maxZ(1.0),
       m_gridPen(QPen(Qt::gray, 1)),
       m_contourPens()
   {
       for (int i = 0; i < 10; ++i) {
           m_contourPens << QPen(QColor::fromHsvF(i / 10.0, 1.0, 1.0), 2);
       }
   }

   ~ContourPlotWidget() override {
       if (m_zValues) {
           delete[] m_zValues;
       }
   }

   void setZValues(int nx, int ny, const double* zValues) {
       if (m_zValues) {
           delete[] m_zValues;
       }
       m_zValues = new double[nx * ny];
       memcpy(m_zValues, zValues, nx * ny * sizeof(double));
       m_nx = nx;
       m_ny = ny;
       update();
   }

   void setNumContours(int nContours) {
       m_nContours = nContours;
       update();
   }

   void setRange(double minZ, double maxZ) {
       m_minZ = minZ;
       m_maxZ = maxZ;
       update();
   }

   void setGridPen(const QPen& pen) {
       m_gridPen = pen;
       update();
   }

protected:
   void paintEvent(QPaintEvent* event) override {
       Q_UNUSED(event);

       QPainter painter(this);
       painter.setRenderHint(QPainter::Antialiasing, true);

       // 计算坐标系的边界和范围
       QRect clientRect = rect().adjusted(5, 5, -5, -5);
       double xmin = 0.0;
       double xmax = 1.0;
       double ymin = 0.0;
       double ymax = 1.0;
       if (m_zValues) {
           xmin = 0.0;
           xmax = m_nx - 1;
           ymin = 0.0;
           ymax = m_ny - 1;
       }
       double xrange = xmax - xmin;
       double yrange = ymax - ymin;

       // 绘制网格线
       painter.setPen(m_gridPen);
       for (int i = 0; i < m_nx; ++i) {
           double x = mapX(i, xmin, xmax, clientRect.left(), clientRect.right());
           painter.drawLine(QPointF(x, clientRect.top()), QPointF(x, clientRect.bottom()));
       }
       for (int j = 0; j < m_ny; ++j) {
           double y = mapY(j, ymin, ymax, clientRect.top(), clientRect.bottom());
           painter.drawLine(QPointF(clientRect.left(), y), QPointF(clientRect.right(), y));
       }

       // 绘制等高线
       if (m_zValues) {
           QVector<QVector<QPointF>> contours = calculateContours();

           for (int i = 0; i < contours.size(); ++i) {
               painter.setPen(m_contourPens.at(i % m_contourPens.size()));

               for (int j = 0; j < contours.at(i).size() - 1; ++j) {
                   double x1 = mapX(contours.at(i).at(j).x(), xmin, xmax, clientRect.left(), clientRect.right());
                   double y1 = mapY(contours.at(i).at(j).y(), ymin, ymax, clientRect.top(), clientRect.bottom());
                   double x2 = mapX(contours.at(i).at(j + 1).x(), xmin, xmax, clientRect.left(), clientRect.right());
                   double y2 = mapY(contours.at(i).at(j + 1).y(), ymin, ymax, clientRect.top(), clientRect.bottom());

                   painter.drawLine(QPointF(x1, y1), QPointF(x2, y2));
               }
           }
       }
   }

private:
   QVector<QVector<QPointF>> calculateContours() const {
       QVector<double> levels(m_nContours);
       double step = (m_maxZ - m_minZ) / (m_nContours - 1);
       for (int i = 0; i < m_nContours; ++i) {
           levels[i] = m_minZ + i * step;
       }

       QVector<QVector<QPointF>> contours(m_nContours);

       for (int levelIndex = 0; levelIndex < m_nContours; ++levelIndex) {
           QVector<QPointF>& contour = contours[levelIndex];

           for (int i = 0; i < m_nx - 1; ++i) {
               for (int j = 0; j < m_ny - 1; ++j) {
                   // 计算四个顶点的值和中心点的值
                   double v[5] = {
                       m_zValues[i + j * m_nx],
                       m_zValues[(i + 1) + j * m_nx],
                       m_zValues[i + (j + 1) * m_nx],
                       m_zValues[(i + 1) + (j + 1) * m_nx],
                       (m_zValues[i + j * m_nx] + m_zValues[(i + 1) + j * m_nx] +
                        m_zValues[i + (j + 1) * m_nx] + m_zValues[(i + 1) + (j + 1) * m_nx]) / 4.0
                   };

                   // 判断是否跨越等高线
                   int mask = ((v[0] < levels[levelIndex]) << 0) |
                              ((v[1] < levels[levelIndex]) << 1) |
                              ((v[2] < levels[levelIndex]) << 2) |
                              ((v[3] < levels[levelIndex]) << 3);

                   double x[4], y[4];
                   x[0] = i;
                   y[0] = j;
                   x[1] = i + 1;
                   y[1] = j;
                   x[2] = i;
                   y[2] = j + 1;
                   x[3] = i + 1;
                   y[3] = j + 1;

                   switch (mask) {
                   case 1: // 0001 -> 1000
                       interpolate(levels[levelIndex], v[0], v[1], x[0], y[0], x[1], y[1], contour);
                       break;
                   case 2: // 0010 -> 0100
                       interpolate(levels[levelIndex], v[1], v[3], x[1], y[1], x[3], y[3], contour);
                       break;
                   case 3: // 0011 -> 1100
                       interpolate(levels[levelIndex], v[0], v[3], x[0], y[0], x[3], y[3], contour);
                       break;
                   case 4: // 0100 -> 0010
                       interpolate(levels[levelIndex], v[2], v[3], x[2], y[2], x[3], y[3], contour);
                       break;
                   case 5: // 0101 -> 1010
                       interpolate(levels[levelIndex], v[0], v[2], x[0], y[0], x[2], y[2], contour);
                       interpolate(levels[levelIndex], v[1], v[3], x[1], y[1], x[3], y[3], contour);
                       break;
                   case 6: // 0110 -> 0101
                       interpolate(levels[levelIndex], v[1], v[2], x[1], y[1], x[2], y[2], contour);
                       interpolate(levels[levelIndex], v[0], v[3], x[0], y[0], x[3, y[3], contour);
                       break;
                       case 7: // 0111 -> 1000
                       interpolate(levels[levelIndex], v[0], v[1], x[0], y[0], x[1], y[1], contour);
                       break;
                       case 8: // 1000 -> 0001
                       interpolate(levels[levelIndex], v[0], v[1], x[0], y[0], x[1], y[1], contour);
                       break;
                       case 9: // 1001 -> 0010
                       interpolate(levels[levelIndex], v[1], v[3], x[1], y[1], x[3], y[3], contour);
                       interpolate(levels[levelIndex], v[0], v[2], x[0], y[0], x[2], y[2], contour);
                       break;
                       case 10: // 1010 -> 0101
                       interpolate(levels[levelIndex], v[0], v[2], x[0], y[0], x[2], y[2], contour);
                       interpolate(levels[levelIndex], v[1], v[3], x[1], y[1], x[3], y[3], contour);
                       break;
                       case 11: // 1011 -> 1100
                       interpolate(levels[levelIndex], v[0], v[3], x[0], y[0], x[3], y[3], contour);
                       break;
                       case 12: // 1100 -> 0011
                       interpolate(levels[levelIndex], v[0], v[3], x[0], y[0], x[3], y[3], contour);
                       break;
                       case 13: // 1101 -> 0100
                       interpolate(levels[levelIndex], v[2], v[3], x[2], y[2], x[3], y[3], contour);
                       interpolate(levels[levelIndex], v[0], v[1], x[0], y[0], x[1], y[1], contour);
                       break;
                       case 14: // 1110 -> 1001
                       interpolate(levels[levelIndex], v[1], v[2], x[1], y[1], x[2], y[2], contour);
                       interpolate(levels[levelIndex], v[0], v[2], x[0], y[0], x[2], y[2], contour);
                       break;
                       }
                       }
                       }
                       }
                       
                           return contours;
                       }
                       
                       void interpolate(double level, double z0, double z1, double x0, double y0, double x1, double y1, QVector<QPointF>& contour) const {
                           if (qFuzzyIsNull(z0 - z1)) {
                               return;
                           }
                           double ratio = (level - z0) / (z1 - z0);
                           double x = x0 + ratio * (x1 - x0);
                           double y = y0 + ratio * (y1 - y0);
                           contour.append(QPointF(x, y));
                       }
                       
                       double mapX(double x, double xmin, double xmax, double left, double right) const {
                           return left + (x - xmin) / (xmax - xmin) * (right - left);
                       }
                       
                       double mapY(double y, double ymin, double ymax, double top, double bottom) const {
                           return bottom - (y - ymin) / (ymax - ymin) * (bottom - top);
                       }
                       
                       double* m_zValues;
                       int m_nx;
                       int m_ny;
                       int m_nContours;
                       double m_minZ;
                       double m_maxZ;
                       QPen m_gridPen;
                       QVector<QPen> m_contourPens;
                       
                       };