[Interest] How to paint smooth rendering of connected QLineF's?

Patrick Stinson patrickkidd at gmail.com
Tue Mar 21 09:34:18 CET 2017


Here is the modified code from QtCharts which works well for me to return a QPainterPath made from bezier curves. I don’t understand the algorithm but got the code to work with no dependencies.

The only problem now is to smoothly vary the pen width along the path to produce something like the attached image. Is there any way to produce vectors like this in Qt5?



static QVector<qreal> firstControlPoints(const QVector<qreal>& vector)
{
    QVector<qreal> result;

    int count = vector.count();
    result.resize(count);
    result[0] = vector[0] / 2.0;

    QVector<qreal> temp;
    temp.resize(count);
    temp[0] = 0;

    qreal b = 2.0;

    for (int i = 1; i < count; i++) {
        temp[i] = 1 / b;
        b = (i < count - 1 ? 4.0 : 3.5) - temp[i];
        result[i] = (vector[i] - result[i - 1]) / b;
    }

    for (int i = 1; i < count; i++)
        result[count - i - 1] -= temp[count - i] * result[count - i];

    return result;
}
    
/*!
  Calculates control points which are needed by QPainterPath.cubicTo function to draw the cubic Bezier cureve between two points.
*/
static QVector<QPointF> calculateControlPoints(const QVector<QPointF> &points)
{
    QVector<QPointF> controlPoints;
    controlPoints.resize(points.count() * 2 - 2);

    int n = points.count() - 1;

    if (n == 1) {
        //for n==1
        controlPoints[0].setX((2 * points[0].x() + points[1].x()) / 3);
        controlPoints[0].setY((2 * points[0].y() + points[1].y()) / 3);
        controlPoints[1].setX(2 * controlPoints[0].x() - points[0].x());
        controlPoints[1].setY(2 * controlPoints[0].y() - points[0].y());
        return controlPoints;
    }

    // Calculate first Bezier control points
    // Set of equations for P0 to Pn points.
    //
    //  |   2   1   0   0   ... 0   0   0   ... 0   0   0   |   |   P1_1    |   |   P0 + 2 * P1             |
    //  |   1   4   1   0   ... 0   0   0   ... 0   0   0   |   |   P1_2    |   |   4 * P1 + 2 * P2         |
    //  |   0   1   4   1   ... 0   0   0   ... 0   0   0   |   |   P1_3    |   |   4 * P2 + 2 * P3         |
    //  |   .   .   .   .   .   .   .   .   .   .   .   .   |   |   ...     |   |   ...                     |
    //  |   0   0   0   0   ... 1   4   1   ... 0   0   0   | * |   P1_i    | = |   4 * P(i-1) + 2 * Pi     |
    //  |   .   .   .   .   .   .   .   .   .   .   .   .   |   |   ...     |   |   ...                     |
    //  |   0   0   0   0   0   0   0   0   ... 1   4   1   |   |   P1_(n-1)|   |   4 * P(n-2) + 2 * P(n-1) |
    //  |   0   0   0   0   0   0   0   0   ... 0   2   7   |   |   P1_n    |   |   8 * P(n-1) + Pn         |
    //
    QVector<qreal> vector;
    vector.resize(n);

    vector[0] = points[0].x() + 2 * points[1].x();


    for (int i = 1; i < n - 1; ++i)
        vector[i] = 4 * points[i].x() + 2 * points[i + 1].x();

    vector[n - 1] = (8 * points[n - 1].x() + points[n].x()) / 2.0;

    QVector<qreal> xControl = firstControlPoints(vector);

    vector[0] = points[0].y() + 2 * points[1].y();

    for (int i = 1; i < n - 1; ++i)
        vector[i] = 4 * points[i].y() + 2 * points[i + 1].y();

    vector[n - 1] = (8 * points[n - 1].y() + points[n].y()) / 2.0;

    QVector<qreal> yControl = firstControlPoints(vector);

    for (int i = 0, j = 0; i < n; ++i, ++j) {

        controlPoints[j].setX(xControl[i]);
        controlPoints[j].setY(yControl[i]);

        j++;

        if (i < n - 1) {
            controlPoints[j].setX(2 * points[i + 1].x() - xControl[i + 1]);
            controlPoints[j].setY(2 * points[i + 1].y() - yControl[i + 1]);
        } else {
            controlPoints[j].setX((points[n].x() + xControl[n - 1]) / 2);
            controlPoints[j].setY((points[n].y() + yControl[n - 1]) / 2);
        }
    }
    return controlPoints;
}



QPainterPath splineFromPoints(const QVector<QPointF> &points, int penWidth)
{
    QPainterPath splinePath;
    QVector<QPointF> controlPoints;
    if (points.count() >= 2)
        controlPoints = calculateControlPoints(points);

    if ((points.size() < 2) || (controlPoints.size() < 2)) {
        return splinePath;
    }

    Q_ASSERT(points.count() * 2 - 2 == controlPoints.count());

    // Use worst case scenario to determine required margin.
    qreal margin = penWidth * 1.42;

    splinePath.moveTo(points.at(0));
    for (int i = 0; i < points.size() - 1; i++) {
        const QPointF &point = points.at(i + 1);
        splinePath.cubicTo(controlPoints[2 * i], controlPoints[2 * i + 1], point);
    }
    return splinePath;
}






> On Feb 28, 2017, at 10:58 AM, Patrick Stinson <patrickkidd at gmail.com> wrote:
> 
> Wow wow wow. This is a great example for me to port. I am working in Python, and if I can get a working snippet I'll pass it on for sure.
> 
> On Feb 28, 2017, at 8:38 AM, Mike Chinander <chinander at gmail.com <mailto:chinander at gmail.com>> wrote:
> 
>> If you are doing this within a QGraphicsView, make sure you turn on antialiasing <http://doc.qt.io/qt-5/qgraphicsview.html#renderHints-prop>. Also, you might want to try a rounded join style <http://doc.qt.io/qt-5/qpen.html#join-style> on the QPen you are using. Posting an example of what you are getting now would help others help you.
>> 
>> On Tue, Feb 21, 2017 at 4:17 AM, Patrick Stinson <patrickkidd at gmail.com <mailto:patrickkidd at gmail.com>> wrote:
>> Hello!
>> 
>> I am painting a series of connected QLineF's with varying vectors and widths. Is there any way to smooth them out to be more vector-ish, so you don't see the jagged transitions between the segments?
>> 
>> 
>> 
>> This is for hand writing notes with the Apple Pencil. I have one segment per event with pressure determining width and it just looks so jagged now...
>> 
>> 
>> 
>> Thanks!
>> 
>> 
>> _______________________________________________
>> Interest mailing list
>> Interest at qt-project.org <mailto:Interest at qt-project.org>
>> http://lists.qt-project.org/mailman/listinfo/interest <http://lists.qt-project.org/mailman/listinfo/interest>
>> 
>> 
>> _______________________________________________
>> Interest mailing list
>> Interest at qt-project.org <mailto:Interest at qt-project.org>
>> http://lists.qt-project.org/mailman/listinfo/interest <http://lists.qt-project.org/mailman/listinfo/interest>

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.qt-project.org/pipermail/interest/attachments/20170321/edfdb8cd/attachment.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: IMG_0020.jpeg
Type: image/jpeg
Size: 32593 bytes
Desc: not available
URL: <http://lists.qt-project.org/pipermail/interest/attachments/20170321/edfdb8cd/attachment.jpeg>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: IMG_0021.jpeg
Type: image/jpeg
Size: 29783 bytes
Desc: not available
URL: <http://lists.qt-project.org/pipermail/interest/attachments/20170321/edfdb8cd/attachment-0001.jpeg>


More information about the Interest mailing list