Qt - разница между drawPolyline и drawLine - PullRequest
0 голосов
/ 26 сентября 2018

В моем примере я преобразовал систему координат рисовальщика qt следующим образом:

 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));

линия рисуется правильно.

1 Ответ

0 голосов
/ 26 сентября 2018

Я сделал MCVE , чтобы связаться с проблемой.

Таким образом, я использовал гораздо меньшие масштабирования и не смог воспроизвести проблему.Затем я использовал масштабирование 1.0E14, как указано в OP.Внезапно исчезли чертежи.Понизив масштабирование, я выяснил, что (в моем случае) до 1E12 это работало довольно хорошо, но с более высокими масштабами линии начали исчезать.Играя с этим, коллега ушел и дал намек, что это могут быть просто проблемы с плавающей точкой.Мы коротко обсудили это и пришли к выводу:

  • умножение очень больших чисел на очень маленькие не проблема вообще.Он состоит из (целого) умножения мантисс и сложения показателей.
  • сложение / вычитание больших чисел с очень маленькими является проблемой, так как оба числа должны быть сделаны с равными показателями (по битам)мантиссы) для добавления / вычитания мантисс.

Таким образом, может произойти последнее, что приведет к стиранию значений, отличных от 0, в 0.

10 14 в двоичном виде число с 47 цифрами.Это близко к точности double с 53 битами мантиссы. Но : используемый механизм рендеринга QPainter может быть основан на OpenGL, где float является значением по умолчанию для многих вещей.float предоставляет только 23 бита для мантиссы!

Итак, подумав немного об этом, я нашел веские причины не рисовать с коэффициентом масштабирования 10 14 .

Чтобы продемонстрировать это, я сделал небольшой образец 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

snapshot of testQPainterDrawLine

Итак, наконец, я считаю, что это не имеет ничего общего с QPainter::drawLine() против QPainter::drawPolyline().Это просто слишком большое масштабирование, которое вызывает проблемы с плавающей запятой.Вот почему линии могут случайно (не отображаться).

Решение простое: значения должны быть масштабированы до их рисования с помощью QPainter, чтобы внутренние преобразования в QPainter происходили в величинах, близких к 0и 1.

...