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 的幅度上。
在我的示例中,我将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 的幅度上。