如何在QT中绘制经过多个点的平滑曲线?

Dam*_*bic 4 c++ qt qpainterpath

有没有办法通过QT中的一组点绘制一条平滑的线?点的数量和位置在运行时设置。

目前,我绘制了一个 QPainterPath,其中包含 lineTo 从点到点的移动,创建一条路径。我确实使用了渲染提示抗锯齿,但路径仍然是锯齿状的。

我见过 QSplineSeries 似乎提供了这种弯曲路径,但它在 Qt4.8 中不可用,这是我正在使用的 QT 版本。

经常建议的另一个选项是使用贝塞尔曲线,但那些使用一个起点和终点以及两个控制点,所以我需要为每个线段(每条线)计算它,并以某种方式计算那些我没有的控制点在这一刻。

Dam*_*bic 6

最后,我实现了某种解决方法,它基本上采用两条连接线,删除它们之间的连接点并将其替换为曲线。因为我有很多小线,这样的变化是不可见的,所以我删除了所有非常短的线并重新连接开放端。该功能主要由 Bojan Kverh 提供,查看他的教程:https ://www.toptal.com/c-plus-plus/rounded-corners-bezier-curves-qpainter

这里的功能:

namespace
{
    float distance(const QPointF& pt1, const QPointF& pt2)
    {
        float hd = (pt1.x() - pt2.x()) * (pt1.x() - pt2.x());
        float vd = (pt1.y() - pt2.y()) * (pt1.y() - pt2.y());
        return std::sqrt(hd + vd);
    }

    QPointF getLineStart(const QPointF& pt1, const QPointF& pt2)
    {
        QPointF pt;
        float rat = 10.0 / distance(pt1, pt2);
        if (rat > 0.5) {
            rat = 0.5;
        }
        pt.setX((1.0 - rat) * pt1.x() + rat * pt2.x());
        pt.setY((1.0 - rat) * pt1.y() + rat * pt2.y());
        return pt;
    }

    QPointF getLineEnd(const QPointF& pt1, const QPointF& pt2)
    {
        QPointF pt;
        float rat = 10.0 / distance(pt1, pt2);
        if (rat > 0.5) {
            rat = 0.5;
        }
        pt.setX(rat * pt1.x() + (1.0 - rat)*pt2.x());
        pt.setY(rat * pt1.y() + (1.0 - rat)*pt2.y());
        return pt;
    }

}

void PainterPath::smoothOut(const float& factor)
{
    QList<QPointF> points;
    QPointF p;
    for (int i = 0; i < mPath->elementCount() - 1; i++) {
        p = QPointF(mPath->elementAt(i).x, mPath->elementAt(i).y);

        // Except for first and last points, check what the distance between two
        // points is and if its less then min, don't add them to the list.
        if (points.count() > 1 && (i < mPath->elementCount() - 2) && (distance(points.last(), p) < factor)) {
            continue;
        }
        points.append(p);
    }

    // Don't proceed if we only have 3 or less points.
    if (points.count() < 3) {
        return;
    }

    QPointF pt1;
    QPointF pt2;
    QPainterPath* path = new QPainterPath();
    for (int i = 0; i < points.count() - 1; i++) {
        pt1 = getLineStart(points[i], points[i + 1]);
        if (i == 0) {
            path->moveTo(pt1);
        } else {
            path->quadTo(points[i], pt1);
        }
        pt2 = getLineEnd(points[i], points[i + 1]);
        path->lineTo(pt2);
    }

    delete mPath;
    mPath = path;
    prepareGeometryChange();
}
Run Code Online (Sandbox Code Playgroud)