Qt - drawPolyline 和 drawLine 之间的区别

Qt - difference between drawPolyline and drawLine

在我的示例中,我将qt painter的坐标系转换为以下方式:

 QTransform xform;
 xform.rotate(90);
 xform.scale(1, 1000000000000000);
 xform.translate(0, -std::abs(max));
 if (limit != 0)
     xform.scale(1, std::abs(max)/std::abs(limit));
 painter->setTransform(xform)

有这么大的规模是因为我的例子中的数据非常小。然后我为我的数据画图 - 它的值大约是 1e-14 并且一切都正确绘制。然后我想在我的两点指定的情节之外画几条线。

我应用 drawPolyLine 函数:

QPointF* line = new QPointF[2];
line[0].setX(0);
line[0].setY(0);
line[1].setX(0);
line[1].setY(std::abs(seismMax));
painter.drawPolyline(line, 2);

其中 seismMax 的数量级为 1e-14(作为我在图中的所有点)。然后画线。 然后我应用 drawLine(因为我只有两点):

painter.drawLine(QPointF(0, 0), QPointF(0, std::abs(seismMax)));

并且在这种情况下没有画线。我怀疑这两个函数处理坐标的方式可能不同。当我用 drawLine 以原始比例绘制线时(例如像这样):

 painter.drawLine(QPointF(0, 0), QPointF(0, 1000));

线条绘制正确。

我做了一个 MCVE 来联系这个问题。

因此,我使用了更小的缩放比例,无法重现该问题。然后,我使用了 OP 所述的缩放比例 1.0E14。突然,线条画消失了。降低缩放比例,我弄清楚了(在我的例子中)直到 1E12 它工作得很好但是随着更高的缩放比例线开始消失。在玩弄这个时,一位同事离开并暗示这可能只是浮点数问题。我们很快讨论了这个并得出结论:

  • 将非常大的数字与非常小的数字相乘根本不是问题。它由(整数)尾数相乘和指数相加组成。
  • addition/subtraction 的大数字和非常小的 的问题,因为两个数字必须用相等的指数(通过位移位尾数)到 add/subtract 尾数.

因此,后者可能会发生,将非 0 值擦除为 0。

1014 是二进制的 47 位数字。这接近具有 53 位尾数的 double 的精度。 但是QPainter使用的渲染引擎可能是基于OpenGL的,其中float是很​​多东西的默认值。 float尾数只提供23位!

所以,在稍微考虑一下之后,我找到了不使用 1014.

比例因子绘画的充分理由

为了证明这一点,我做了一个小样本 testQPainterDrawLine.cc:

#include <QtWidgets>

class Widget: public QWidget {
  public:
    const double scale; 
  public:
    Widget(double scale, QWidget *pQParent = nullptr):
      QWidget(pQParent),
      scale(scale)
    { }
    virtual ~Widget() = default;
    Widget(const Widget&) = delete;
    Widget& operator=(const Widget&) = delete;
  protected:
    virtual void paintEvent(QPaintEvent *pQEvent) override;
};

void Widget::paintEvent(QPaintEvent*)
{
  const double value = height() / scale;
  QPainter qPainter(this);
  qPainter.fillRect(0, 0, width(), height(), QColor(Qt::white));
  qPainter.drawRect(0, 0, width() - 1, height() - 1);
  QTransform xform;
  xform.scale(1, scale);
  qPainter.setTransform(xform);
  qPainter.setPen(QPen(QColor(Qt::red), 3.0));
  const double xL = 0.333 * width();
  qPainter.drawLine(QPointF(xL, 0.0), QPointF(xL, value));
  qPainter.setPen(QPen(QColor(Qt::blue), 3.0));
  const double xPL = 0.667 * width();
  QPointF qPts[] = { QPointF(xPL, 0.0), QPointF(xPL, value) };
  const int nPts = sizeof qPts / sizeof *qPts; 
  qPainter.drawPolyline(qPts, nPts);
}

int main(int argc, char **argv)
{
  qDebug() << "Qt Version:" << QT_VERSION_STR;
  QApplication app(argc, argv);
  QWidget qWin;
  QGridLayout qGrid;
  qGrid.setRowStretch(0, 0); qGrid.setRowStretch(1, 1);
  double scale = 1.0E11;
  for (int i = 0; i < 4; ++i, scale *= 10.0) {
    qGrid.addWidget(
      new QLabel(QString("Scale: %1").arg(scale)),
      0, i);
    qGrid.addWidget(new Widget(scale), 1, i);
  }
  qWin.setLayout(&qGrid);
  qWin.resize(1024, 256);
  qWin.show();
  return app.exec();
}

testQPainterDrawLine.pro:

SOURCES = testQPainterDrawLine.cc

QT = widgets

cygwin64 中编译和测试:

$ qmake-qt5 testQPainterDrawLine.pro

$ make

$ ./testQPainterDrawLine
Qt Version: 5.9.4

所以,最后,我相信,它与 QPainter::drawLine()QPainter::drawPolyline() 没有任何关系。这只是缩放太高并导致浮点问题。这就是线条可能会意外(消失)的原因。

解决方案很简单:在使用 QPainter 绘制值之前必须对其进行缩放,以便 QPainter 中的内部转换发生在更接近 0 和 1 的幅度上。