首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何在QT中画出一条经过多个点的平滑曲线?

如何在QT中画出一条经过多个点的平滑曲线?
EN

Stack Overflow用户
提问于 2016-11-23 20:01:21
回答 3查看 10.2K关注 0票数 4

有没有办法在QT中画一条流畅的线通过一组点?点的数量和位置是在运行时设置的。

目前,我绘制了一个包含lineTo的QPainterPath,它从一个点到另一个点,创建一条路径。我确实使用了渲染提示抗锯齿,但路径仍然参差不齐。

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

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

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2017-04-25 19:07:20

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

下面是函数:

代码语言:javascript
运行
复制
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();
}
票数 6
EN

Stack Overflow用户

发布于 2016-11-23 20:17:56

我不认为Qt4.8中有开箱即用的解决方案(正如你已经注意到的,QSplineSeries是Qt5.x的一个特性)。此外,QSplineSeriesQtCharts模块的一部分,这是一个商业模块(如QtDataVisualization),所以除非你有商业许可,或者你的项目是GPL,否则你不能使用它。

您必须手动完成它,即通过所需的数学运算并自己实现它(或者找到一个很好的实现(甚至不需要用C++实现,更不用说Qt兼容了)。

既然你提到了贝塞尔曲线,我建议你试试composite Bezier curve。我记得在我参与的一个项目中实现了这个东西。它需要some...work。:D This article可能会帮助您入门。

Bezier曲线实际上是B样条(如果我没记错的话)。特别是,如果你可以解决某些缺乏平滑度的问题,你就可以非常快地生成复合贝塞尔曲线。对于它们的健壮性和受欢迎程度,我百分之百确定你可以在网上找到一个像样的实现。可能不是Qt友好的,但如果编写得当,你应该能够在任何时间适应代码。

This看起来很有前途(它在ActionScript中,但是没问题)。或者你可以给QPainterPath::cubicTo()一个镜头,它可以为你创建贝塞尔曲线,给定你也可以提供计算曲线所需的两个控制点。

票数 1
EN

Stack Overflow用户

发布于 2016-12-02 22:31:55

几乎每个人都使用三次插值来完成此任务,您可以选择Bezier曲线或Catmull-Rom样条线。如果你必须击中每个点,那么你需要保持“手柄”或贝塞尔曲线控制点之间的直线。然后使用最小二乘进行拟合,正如您已经发现的那样,这有点牵涉其中。

Catmull Rom样条线的优点是,它们只需要两个额外的控制点(开始和结束,只需镜像点来创建它们)。只要这些点是合理平滑的,这条线就会表现良好。QT图形不太可能直接绘制CatMull Rom样条线,所以转换为Bezier,这是一种标准的发布方法,您可以很容易地从Catmull Rom到Bezier,尽管不是相反-并不是每一个Bezier都可以用只有几个点的Catmull Rom来表示。

如果立方曲线不能给出你想要的曲线,你可以使用其他插值方法,等式五次插值。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/40764011

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档