在QGraphicsView上实现交互式样条曲线的最佳方法是什么?

时间:2019-06-10 14:33:17

标签: c++ qt spline qgraphicsscene qgraphicsitem

我正在开发应用程序,该应用程序将允许用户上传岩石裂缝的图像并将样条曲线近似应用于该裂缝。

为此,我有QGraphicsView,它显示了上载的图像。 上载图像后,用户可以选择在scene上绘制点,我希望这些点始终与线相连。这些连接的点将构成一条曲线,我想进行交互。交互式是指我希望用户能够拖动点而不会破坏曲线,使连接点的线随点移动。我还希望使用户能够删除所选点,以便相邻点保持连接。这些不是我想要的所有功能,但我认为您明白了。

我所做的是创建了一个类MeasurePoint,该类继承自QGraphicsItem,并包含有关单个点的所有信息。它具有以下字段:

int xPos;
int yPos;
int index;
bool movable;
bool selected;

该类还具有几种方法来管理这些点(字段的getter和setter函数等)。但是我没有在该类中实现连接功能,因为它仅包含有关单个点的信息,而没有进一步的信息。我喜欢将点简单地添加到场景中的行为方式。但是现在我需要按照我之前描述的方式将它们连接起来,但我真的不知道该怎么做。

存储积分的最佳方法是什么?我应该如何精确实现点之间的连接?任何可能的帮助表示赞赏。

P.s。如果在这个问题中需要我的课程MeasurePoint的实现,我将对其进行编辑。

1 个答案:

答案 0 :(得分:2)

使用QGraphicsPathItem绘制样条曲线。然后,使用QPainterPath::cubicTo方法在各点之间创建连接。

您需要计算控制点才能在两个点之间绘制平滑的贝塞尔曲线。

您可以找到基于二次Bezier曲线的一堆更好的库,但是下面的一个简单示例使用Qt类型和一个简单的方法来了解如何获取控制点:

QPair<QPointF, QPointF> controlPoints(QPointF const& p0, QPointF const& p1, QPointF const& p2, qreal t=0.25)
{
    QPair<QPointF, QPointF> pair;
    qreal d01 = qSqrt( ( p1.x() - p0.x() ) * ( p1.x() - p0.x() ) + ( p1.y() - p0.y() ) * ( p1.y() - p0.y() ) );
    qreal d12 = qSqrt( ( p2.x() - p1.x() ) * ( p2.x() - p1.x() ) + ( p2.y() - p1.y() ) * ( p2.y() - p1.y() ) );

    qreal fa = t * d01 / ( d01 + d12 );
    qreal fb = t * d12 / ( d01 + d12 );

    qreal c1x = p1.x() - fa * ( p2.x() - p0.x() );
    qreal c1y = p1.y() - fa * ( p2.y() - p0.y() );
    qreal c2x = p1.x() + fb * ( p2.x() - p0.x() );
    qreal c2y = p1.y() + fb * ( p2.y() - p0.y() );

    pair.first = QPointF( c1x, c1y );
    pair.second = QPointF( c2x, c2y );

    return pair;
}

然后,浏览您的积分列表以创建QPainterPath

QPainterPath BackgroundBuilder::buildPath(QList<QPointF> const& points)
{
    QPainterPath pth;

    QPair<QPointF, QPointF> pair = controlPoints(points.at(0), points.at(1), points.at(2));
    QPointF p0 = pair.second;
    pth.moveTo(0, 0);
    pth.lineTo(p0);
    for (int i = 2; i != points.count() - 1; ++i)
    {
        QPair<QPointF, QPointF> pair = controlPoints( points.at(i - 1), points.at(i), points.at(i + 1));
        pth.cubicTo( p0, pair.first, points.at( i ) );
        p0 = pair.second;
    }
    return pth;
}

您可能需要将MeasurePoint转换为QPointF才能使此代码通用:

QList<QPointF> points;
QList<MeasurePoint*> measures = ...;
for (MeasurePoint* measure: measures)
{
    points << QPointF(measure.xPos, measure.yPos);
}