How to create a B-Spline with QwtSpline - c++

In QwtSpline there are two different types of splines, but I don't see a difference between both types.
I found a image, that explains my problem:
QwtSpline always creates a spline, like that on the left side of the picture.
But I want to have a Spline, like that one on the right side.
My Code is the following:
QwtSpline spline;
QPolygonF polygon;
QVector<QPointF> result;
polygon.append(startPoint);
polygon.append(rotatedPoint);
polygon.append(endPoint);
spline.setPoints(polygon);
for(double i = startPoint.rx(); i < endPoint.rx(); i++)
{
result << QPointF(i, spline.value(i));
}
result << QPointF(endPoint.rx(), spline.value(endPoint.rx()));
What I want to do, is to draw a spline like that one on the right side of the picture to a QwtPlot. Maybe there is a easier way to solve my problem, than creating a QwtSpline iterate throug it to creat a QwtCurve with every point on the QwtSpline.
If it is easier, to draw a bezier curve to QwtPlot, that's no problem, a bezier curve would be easier for me. I only took a spline, because I didn't find a bezier curve in Qwt.

Try Qwt from one of the branches >= 6.2. It has a complete new implementation of several spline interpolation algorithms.
But if it is about drawing a bezier curve only you can also use QwtShapeItem, what is displaying a QPainterPath. Of course you can also use QPainterPath to create a QPolygon from your bezier curve and then use QwtPlotCurve.

Ok guys because you didn't like my answer bevore, I will try to explain how to calculate a bezier curve:
I think the easiest way is to use the Bernstein-Bézier representation of curves.
To do that, you have to find out the Bernstein polynomials. That's not difficult. There is a formular to do that its
n is the number of points of your curve and
i is the actually point.
That means that you have so many Bernstein polynom, how many points you have.
If you know every Bernstein polynom you are able to use the following formular to calculate the curve.
n is the total number of points and
i is the index of the current point.
P is a point and t always runs from 0 to 1. 0 is the positon at the left side and 1 represents the position on the right side. r is the new point of the curve.
Now you have two calculate the formular above for x and y.
This is the formular for x
This is the formular for y
As you can see the only variable parameter on the right side is t. That means, that you have to calculate this formular many times with t between 0 and 1. The easiest way to do that, is to write a for loop like that:
QList<QPointF> results = QList<QPointF>();
QList<QPointF> points = QList<QPointF>();
for(double i = 0; i <= 1; i+=0.01)
{
double x = //formular for rx
double y = //formular for ry
results << QPointF(x, y);
}
I hope it wasn't to complicated. If you didn't understand this short explanation, you can look at the Handbook of Mathematics. In the Sixth Edition its on site 1000 to 1001.
ISBN: 978-3-662-46220-1

Related

Is there a way to generate the corners of a regular N-gon without division and without trig, given N as input

Edit: So I found a page related to rasterizing a trapezoid https://cse.taylor.edu/~btoll/s99/424/res/ucdavis/GraphicsNotes/Rasterizing-Polygons/Rasterizing-Polygons.html but am still trying to figure out if I can just do the edges
I am trying to generate points for the corners of an arbitrary N-gon. Where N is a non-zero positive integer. But I am trying to do so efficiently without the need of division and trig. I am thinking that there is probably some of sort of Bresenham's type algorithm for this but I cannot seem to find anything.
The only thing I can find on stackoverflow is this but the internal angle increments are found by using 2*π/N:
How to draw a n sided regular polygon in cartesian coordinates?
Here was the algorithm from that page in C
float angle_increment = 2*PI / n_sides;
for(int i = 0; i < n_sides; ++i)
{
float x = x_centre + radius * cos(i*angle_increment +start_angle);
float y = y_centre + radius * sin(i*angle_increment +start_angle);
}
So is it possible to do so without any division?
There's no solution which avoids calling sin and cos, aside from precomputing them for all useful values of N. However, you only need to do the computation once, regardless of N. (Provided you know where the centre of the polygon is. You might need a second computation to figure out the coordinates of the centre.) Every vertex can be computed from the previous vertex using the same rotation matrix.
The lookup table of precomputed values is not an unreasonable solution, since at some sufficiently large value of N the polygon becomes indistinguishable from a circle. So you probably only need a lookup table of a few hundred values.

CGAL Intersection Circle and Vertical Lines (not segments)

In CGAL I need to compute the exact intersection points between a set of lines and a set o circles. Starting from the circles (which can have irrational radius but rational squared_radius) I should compute the vertical line passing through the x_extremal_points of each circle (not segment but lines) and calculate the intersection point of each circle with each line.
I’m using CircularKernel and Circle_2 for the circles and Line_2 for the lines.
Here’s an example of how I compute the circles and the lines and how I check if they intersect.
int main()
{
Point_2 a = Point_2(250.5, 98.5);
Point_2 b = Point_2(156, 139);
//Radius is half distance ab
Circular_k::FT aRad = CGAL::squared_distance(a, b);
Circle_2 circle_a = Circle_2(a, aRad/4);
Circular_arc_point_2 a_left_point = CGAL::x_extremal_point(circle_a, false);
Circular_arc_point_2 a_right_point = CGAL::x_extremal_point(circle_a, true);
//for example use only left extremal point of circle a
CGAL::Bbox_2 a_left_point_bb = a_left_point.bbox();
Line_2 a_left_line = Line_2(Point_2(a_left_point_bb.xmin(), a_left_point_bb.ymin()),
Point_2(a_left_point_bb.xmin(), a_left_point_bb.ymax()));
if ( do_intersect(a_left_line, circle_a) ) {
std::cout << "intersect";
}
else {
std::cout << " do not intersect ";
}
return 0;
}
This flow rises this exception:
CGAL error: precondition violation!
Expression : y != 0
File : c:\dev\cgal-4.7\include\cgal\gmp\gmpq_type.h
Line : 371
Explanation:
Refer to the bug-reporting instructions at http://www.cgal.org/bug_report.html
I can’t figure out how I can calculate the intersection points.
Also, Is there a better way to compute the lines? I know abot the x_extremal_point function but it returns the Circular_arc_point point and I’m not able to construct a vertical line passing through them directly without using Bounding box.
In your code, you seem to compute the intersection of a circle with the vertical line that passes through the extremal point of the circle (I forget the bounding box). Well, then the (double) intersection is the extremal point itself...
More globally, you say in your text of introduction that you want to compute exact intersections. Then you should certainly not use bounding boxes, which by definition introduce some approximation.
If I understand your text correctly,
* for testing the intersection of your vertical lines with the other circles, you don't need to construct the lines, you only need to compare the abscissae of the extremal points of two circles, which you can do with the CGAL circular kernel.
* for computing the intersection of a vertical line that has non-rational coefficients (since its equation is of the form x= +-sqrt(r)) with another circle, then the CGAL circular kernel will not give you a pre-cooked solution. That kernel will help, but you must still compute a few things by hand.
If you don't want to bother, then you can also just take a standard CGAL kernel with Core::Expr as underlying number type. It can do "anything", but it will be slower.
For efficiency, you should look at the underlying 1D problem: projecting the lines and the circle on the X axis, you have a set of points and a set of intervals [Xc-R, Xc+R].
If the L points are sorted increasingly, you can locate the left bound of an interval in time Lg(L) by dichotomy, and scan the list of points until the right bound. This results in a O(Lg(L).C + I) behavior (C circle intervals), where I is the number of intersections reported.
I guess that with a merge-like process using an active list, if the interval bounds are also sorted you can lower to O(L + C + I).
The extension to 2D is elementary.

Points on a circle, with limits. How To calculate without angle, but radius and centre point?

This is quite complicated to explain, so I will do my best, sorry if there is anything I missed out, let me know and I will rectify it.
My question is, I have been tasked to draw this shape,
(source: learnersdictionary.com)
This is to be done using C++ to write code that will calculate the points on this shape.
Important details.
User Input - Centre Point (X, Y), number of points to be shown, Font Size (influences radius)
Output - List of co-ordinates on the shape.
The overall aim once I have the points is to put them into a graph on Excel and it will hopefully draw it for me, at the user inputted size!
I know that the maximum Radius is 165mm and the minimum is 35mm. I have decided that my base Font Size shall be 20. I then did some thinking and came up with the equation.
Radius = (Chosen Font Size/20)*130. This is just an estimation, I realise it probably not right, but I thought it could work at least as a template.
I then decided that I should create two different circles, with two different centre points, then link them together to create the shape. I thought that the INSIDE line will have to have a larger Radius and a centre point further along the X-Axis (Y staying constant), as then it could cut into the outside line.
So I defined 2nd Centre point as (X+4, Y). (Again, just estimation, thought it doesn't really matter how far apart they are).
I then decided Radius 2 = (Chosen Font Size/20)*165 (max radius)
So, I have my 2 Radii, and two centre points.
Now to calculate the points on the circles, I am really struggling. I decided the best way to do it would be to create an increment (here is template)
for(int i=0; i<=n; i++) //where 'n' is users chosen number of points
{
//Equation for X point
//Equation for Y Point
cout<<"("<<X<<","<<Y<<")"<<endl;
}
Now, for the life of me, I cannot figure out an equation to calculate the points. I have found equations that involve angles, but as I do not have any, I'm struggling.
I am, in essence, trying to calculate Point 'P' here, except all the way round the circle.
(source: tutorvista.com)
Another point I am thinking may be a problem is imposing limits on the values calculated to only display the values that are on the shape.? Not sure how to chose limits exactly other than to make the outside line a full Half Circle so I have a maximum radius?
So. Does anyone have any hints/tips/links they can share with me on how to proceed exactly?
Thanks again, any problems with the question, sorry will do my best to rectify if you let me know.
Cheers
UPDATE;
R1 = (Font/20)*130;
R2 = (Font/20)*165;
for(X1=0; X1<=n; X1++)
{
Y1 = ((2*Y)+(pow(((4*((pow((X1-X), 2)))+(pow(R1, 2)))), 0.5)))/2;
Y2 = ((2*Y)-(pow(((4*((pow((X1-X), 2)))+(pow(R1, 2)))), 0.5)))/2;
cout<<"("<<X1<<","<<Y1<<")";
cout<<"("<<X1<<","<<Y2<<")";
}
Opinion?
As per Code-Guru's comments on the question, the inner circle looks more like a half circle than the outer. Use the equation in Code-Guru's answer to calculate the points for the inner circle. Then, have a look at this question for how to calculate the radius of a circle which intersects your circle, given the distance (which you can set arbitrarily) and the points of intersection (which you know, because it's a half circle). From this you can draw the outer arc for any given distance, and all you need to do is vary the distance until you produce a shape that you're happy with.
This question may help you to apply Code-Guru's equation.
The equation of a circle is
(x - h)^2 + (y - k)^2 = r^2
With a little bit of algebra, you can iterate x over the range from h to h+r incrementing by some appropriate delta and calculate the two corresponding values of y. This will draw a complete circle.
The next step is to find the x-coordinate for the intersection of the two circles (assuming that the moon shape is defined by two appropriate circles). Again, some algebra and a pencil and paper will help.
More details:
To draw a circle without using polar coordinates and trig, you can do something like this:
for x in h-r to h+r increment by delta
calculate both y coordinates
To calculate the y-coordinates, you need to solve the equation of a circle for y. The easiest way to do this is to transform it into a quadratic equation of the form A*y^2+B*y+C=0 and use the quadratic equation:
(x - h)^2 + (y - k)^2 = r^2
(x - h)^2 + (y - k)^2 - r^2 = 0
(y^2 - 2*k*y + k^2) + (x - h)^2 - r^2 = 0
y^2 - 2*k*y + (k^2 + (x - h)^2 - r^2) = 0
So we have
A = 1
B = -2*k
C = k^2 + (x - h)^2 - r^2
Now plug these into the quadratic equation and chug out the two y-values for each x value in the for loop. (Most likely, you will want to do the calculations in a separate function -- or functions.)
As you can see this is pretty messy. Doing this with trigonometry and angles will be much cleaner.
More thoughts:
Even though there are no angles in the user input described in the question, there is no intrinsic reason why you cannot use them during calculations (unless you have a specific requirement otherwise, say because your teacher told you not to). With that said, using polar coordinates makes this much easier. For a complete circle you can do something like this:
for theta = 0 to 2*PI increment by delta
x = r * cos(theta)
y = r * sin(theta)
To draw an arc, rather than a full circle, you simply change the limits for theta in the for loop. For example, the left-half of the circle goes from PI/2 to 3*PI/2.

Finding points of inflection on a 2d Bezier curve

I need to determine points of inflection (points where the curvature changes) on a 2d Bezier curve, parameterized by t, 0 <= t <= 1, if they exist. My original method was to sample along the curve, evaluating second derivatives and finding the point where the derivative's sign changes.
2DVector curvature1, curvature2;
for (double t = 0, t <= 1.0; t += STEP) {
curvature1 = bezier.CurvatureAt(t);
curvature2 = bezier.CurvatureAt(t + (STEP/2.0 >= 1.0 ? 0 : t + STEP/2.0));
if (isNegative(curvature1) ? isPositive(curvature2) : isNegative(curvature2)) {
inflection_point = t;
}
}
where CurvatureAt() is a method that evaluates the second derivative of the bezier at t, but as the bezier curve is a vector valued function the derivative is returned as a 2D vector (not std::vector, a 2D vector class). I dont know how to interpret "where the sign changes" for vectors. Basically i dont know how to write isNegative or isPositive in the above snippet.
are there any other ways to find points of inflection on a 2d Bezier curve?
I dont think its possible to determine a closed form solution to this problem because the Bezier can be of arbitrary degree, however please correct me if I'm wrong here.
Curvature is related to but not the same as second derivative.
The signed curvature of a parametric curve P(t) = (x(t), y(t)) is actually a number and is defined as:
k(t) = (x'y'' - x''y') / (x' * x' + y' * y')^(3/2)
If you use this formula your original idea should work.
To determine the points of inflection on a bezier, find the time or times in the interval (0, 1) [excluding the endpoints of course] for which the cross product of the first and second derivatives of the parametric equation of the bezier is zero i.e. f' X f'' = 0.
This is noted in various sources like this page and p 4 of this paper.
I think you don't need such a loop.
According to this page, you can compute at any point the curvature of a Bezier curve. As a Bezier curve has a polynomial expression, you can easily compute when the sign of the curvature changes.

Tangent of a parametric discrete curve

I have a parametric curve, say two vectors of doubles where the parameter is the index, and I have to calculate the angle of the tangent to this curve at any given point (index).
Any suggestion or link about how to do that?
Thanks.
Here's a short formula, equivalent (I think) to pau.estalella's answer:
m[i] = (y[i+1] - y[i-1]) / (x[i+1] - x[i-1])
this approximates, reasonably well, the slope at the point (x[i], y[i]).
Your question mentions the "angle of the tangent". The tangent line, having slope m[i], makes angle arctangent(m[i]) with the positive x axis. If this is what you're after, you might use the two-argument arctangent, if it's available:
angle[i] = atan2(y[i+1] - y[i-1], x[i+1] - x[i-1])
this will work correctly, even when x[i+1] == x[i-1].
I suggest you check out the Wikipedia article on numerical differentiation for a start. Before you go much further than that, decide what purposes you want the tangent for and decide whether or not you need to try more complex schemes than the simple ones in the article.
The first problem you run into is to even define the tangent in one of the vertexes of the curve. Consider e.g. that you have the two arrays:
x = { 1.0, 2.0, 2.0 };
y = { 1.0, 1.0, 2.0 };
Then at the second vertex you have a 90-degree change of direction of the line. In that place the tangent isn't even defined mathematically.
Answer to gregseth's comment below
I guess in your example the "tangent" at the second point would be the line parallel to (P0,P2) passing through P1... which kind of give me the answer : for any point of index N the parallel to (N-1, N+1) passing through N. Would that be a not-too-bad approximation?
It depends on what you are using it for. Consider for example:
x = { 1.0, 2.0, 2.0 };
y = { 1.0, 1000000, 1000000 };
That is basically an L shape with a very high vertical line. In your suggestion it would give you an almost vertical tangent. Is that what you want, or do you rather want a 45-degree tangent in that case? It also depends on your input data how you sould define it.
One solution is get the two vectors connection to the vertex, normalize them and then use your algorithm. That way you would get a 45-degree tangent in the above example.
Compute the first derivative: dy/dx. That gives you the tangent.
The tangent to a smooth curve at a point P is the parametric straight line P + tV, where V is the derivative of the curve with respect to "the parameter". But here the parameter is just the index of an array, and numerical differentiation is a difficult problem, hence to approximate the tangent I would use (weighted) least squares approximation.
In other words, choose three or five points of the curve around your point of interest P (i.e. P[i-2], P[i-1], P[i], P[i+1], and P[i+2], if P==P[i]), and approximate them with a straight line, in the least squares sense. The more weight you assign to the middle point P, the more close the line will be to P; on the other hand, the more weight you assign to the extremal points, the more "tangent" the straight line will be, that is, the more nicely it will approximate you curve in the neighborhood of P.
For example, with respect to the following points:
x = [-1, 0, 1]
y = [ 0, 1, 0]
for which the tangent is not defined (as in Anders Abel's answer),
this approach should yield a horizontal straight line close to the point (0,1).
You can try to compute the tangent of an interpolating curve that passes through the given points (I'm thinking of a cubic spline, which is pretty easy to derive) or compute the tangent directly from the data points.
You can find a rough approximation of the derivative in the following manner
Let a curve C pass through points p1,p2 and p3. At point p2 you have two possible tangents: t1=p2-p1 and t2=p3-p2. You can combine them by simply computing their average: 0.5*(t1+t2)
or you can combine them according to their lengths (or their reciprocal 1/length)
Remember to normalize the resulting tangent.
In order to compute the angle between the tangent and the curve, remember that the dot product of two unit vectors gives the cosine of the angle between them. Take the resulting tangent t and the unit vector v2=|p3-p2|, and acos(dot(t,v2)) gives the angle you need.