Use more numerically robust algorithm to compute QBezier::pointAt().
authorAlan Alpert <alan.alpert@nokia.com>
Tue, 2 Aug 2011 01:26:24 +0000 (11:26 +1000)
committerQt by Nokia <qt-info@nokia.com>
Fri, 5 Aug 2011 08:55:30 +0000 (10:55 +0200)
QBezier::pointAt() could potentially return values outside the bezier's
bounds, even when the bezier was a straight horizontal line. For
example, with y = 0.5, it would produce y=0.5 or y=0.49999999999999 for
different values of t, which when rounded cause jittering in a QML
PathView.

Task-number: QTBUG-17007
Task-number: QTBUG-18133
Cherry-pick-of: 8b66982ec7b4b5d2071931c288973dce73dc9875
Change-Id: I4ecac7b9085aaaaaaaaaaaaaaaaaaaaaa7d7b0bc
Reviewed-on: http://codereview.qt.nokia.com/2467
Reviewed-by: Samuel Rødal <samuel.rodal@nokia.com>
src/gui/painting/qbezier_p.h
tests/auto/qpainterpath/tst_qpainterpath.cpp

index 399dd89..f1f7eb1 100644 (file)
@@ -162,27 +162,27 @@ inline void QBezier::coefficients(qreal t, qreal &a, qreal &b, qreal &c, qreal &
 
 inline QPointF QBezier::pointAt(qreal t) const
 {
-#if 1
-    qreal a, b, c, d;
-    coefficients(t, a, b, c, d);
-    return QPointF(a*x1 + b*x2 + c*x3 + d*x4, a*y1 + b*y2 + c*y3 + d*y4);
-#else
     // numerically more stable:
+    qreal x, y;
+
     qreal m_t = 1. - t;
-    qreal a = x1*m_t + x2*t;
-    qreal b = x2*m_t + x3*t;
-    qreal c = x3*m_t + x4*t;
-    a = a*m_t + b*t;
-    b = b*m_t + c*t;
-    qreal x = a*m_t + b*t;
-    qreal a = y1*m_t + y2*t;
-    qreal b = y2*m_t + y3*t;
-    qreal c = y3*m_t + y4*t;
-    a = a*m_t + b*t;
-    b = b*m_t + c*t;
-    qreal y = a*m_t + b*t;
+    {
+        qreal a = x1*m_t + x2*t;
+        qreal b = x2*m_t + x3*t;
+        qreal c = x3*m_t + x4*t;
+        a = a*m_t + b*t;
+        b = b*m_t + c*t;
+        x = a*m_t + b*t;
+    }
+    {
+        qreal a = y1*m_t + y2*t;
+        qreal b = y2*m_t + y3*t;
+        qreal c = y3*m_t + y4*t;
+        a = a*m_t + b*t;
+        b = b*m_t + c*t;
+        y = a*m_t + b*t;
+    }
     return QPointF(x, y);
-#endif
 }
 
 inline QPointF QBezier::normalVector(qreal t) const
index 3941a11..33315ad 100644 (file)
@@ -114,6 +114,8 @@ private slots:
     void connectPathMoveTo();
 
     void translate();
+
+    void lineWithinBounds();
 };
 
 // Testing get/set functions
@@ -1306,6 +1308,25 @@ void tst_QPainterPath::translate()
     QCOMPARE(complexPath.translated(-offset), untranslatedComplexPath);
 }
 
+
+void tst_QPainterPath::lineWithinBounds()
+{
+    const int iteration_count = 3;
+    volatile const qreal yVal = 0.5;
+    QPointF a(0.0, yVal);
+    QPointF b(1000.0, yVal);
+    QPointF c(2000.0, yVal);
+    QPointF d(3000.0, yVal);
+    QPainterPath path;
+    path.moveTo(QPointF(0, yVal));
+    path.cubicTo(QPointF(1000.0, yVal), QPointF(2000.0, yVal), QPointF(3000.0, yVal));
+    for(int i=0; i<=iteration_count; i++) {
+        qreal actual = path.pointAtPercent(qreal(i) / iteration_count).y();
+        QVERIFY(actual == yVal); // don't use QCOMPARE, don't want fuzzy comparison
+    }
+}
+
+
 QTEST_APPLESS_MAIN(tst_QPainterPath)
 
 #include "tst_qpainterpath.moc"