PDA

View Full Version : Curve through points



barry2310
11th January 2019, 12:40
Hey,
I'm new to posting on the forum but I've used it a lot.

I have an issue that I cant seem to find an answer on though.

My problem is that I have a number of points on a QGraphicsScene that are movable.

I want to be able to draw a curve that will pass through them. The thing is that the number of points is dynamic, can be 3 to a much larger number.
I have tried a QPainterPath, but I can only get straight lines between them.

I feel that someone will mention Bezier curves but I tried them. Unfortunately, it wont work for a couple of reasons:
1. The control point is not on the curve.
2. The curve will need to be more than one curve, it could be multiple "S" shapes strung together.

My attempt is below, I could be missing a flag from the QPainterPath to put a curve between the points but I'm struggling to find it:



QVector<QPointF> points;
points << startPoint;
for (int i=0; i<mHandles.size(); i++) {
points << mHandles.at(i)->scenePos();
}
points << endPoint;

QPainterPath testCurve;

testCurve.addPolygon(QPolygonF(points));
painter->drawPath(testCurve);


Any help would be greatly appreciated,
Thanks in advance

d_stranz
11th January 2019, 16:36
You want a "piecewise cubic spline". Google for "piecewise cubic spline C++" and you'll see several implementations. This type of spline interpolates between the control points using a cubic polynomial, with the conditions that the control points lie on the line and the slope of the two segments on either side of the control point is continuous. Thus you get a smooth curve that passes through each of the control points.

Uwe
12th January 2019, 13:59
If you are looking for an implementation for Qt you can have a look at Qwt. You have to use a branch >= 6.2 ( f.e https://svn.code.sf.net/p/qwt/code/trunk/qwt ) - I have replaced the broken spline implementation from 6.1 and added implementations for many spline interpolation algos, that looked relevant to me.

In playground/splineeditor you find an example, where you can play with the various algos and their parameters to see their effect.

Note that a cubic polynomial is different way to describe a Bezier curve and with the Qwt spline classes you can create a list of polynomials or bezier curves. So you could feed the QwtSpline classes with your points and use the calculated QPainterPath ( what is a sequence of bezier curves in this case ).

HTH,
Uwe

barry2310
14th January 2019, 14:57
Thanks for the replies guys.

I went with the Qwt 6.2 option.

I implemented the QwtSplineCurveFitter and I have what I want.

Here is my implementation for anyone who has this issue:



QVector<QPointF> points;
points << startPoint;
for (int i=0; i<mHandles.size(); i++) {
points << mHandles.at(i)->scenePos();
}
points << endPoint;

QPainterPath testCurve;

QwtSplineCurveFitter curveFitter;
testCurve = curveFitter.fitCurvePath(QPolygonF(points));
painter->drawPath(testCurve);


Uwe, thank you so much for this! I had been trying to come up with a solution for a long time!

- Barry

Uwe
14th January 2019, 17:17
Note that QwtSplineCurveFitter is only a wrapper around the actual QwtSpline classes, that you don't need in your case.

The default implementation of QwtSplineCurveFitter uses a parametric cardinal spline with a uniform parametrization. In case you have specific requirements ( like performance ) it might be worth to find the right spline interpolation/approximation options for you. F.e when your curve has increasing x or y values you don't need parametrization and could go with ParameterX/ParameterY ( = no parametrization ) instead.

( Parametrization is always doing things twice: x = f(t) and y = g(t) instead of y = f(x) )

Uwe