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.
Related
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.
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
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.
I would like to implement the Ramer–Douglas–Peucker_algorithm in C++.
The pseudo code looks like this:
function DouglasPeucker(PointList[], epsilon)
//Find the point with the maximum distance
dmax = 0
index = 0
for i = 2 to (length(PointList) - 1)
d = OrthogonalDistance(PointList[i], Line(PointList[1], PointList[end]))
if d > dmax
index = i
dmax = d
end
end
//If max distance is greater than epsilon, recursively simplify
if dmax >= epsilon
//Recursive call
recResults1[] = DouglasPeucker(PointList[1...index], epsilon)
recResults2[] = DouglasPeucker(PointList[index...end], epsilon)
// Build the result list
ResultList[] = {recResults1[1...end-1] recResults2[1...end]}
else
ResultList[] = {PointList[1], PointList[end]}
end
//Return the result
return ResultList[]
end
Here is my understanding so far.
It is a recursive function taking in an array of points and a distance threshold.
Then it iterates through the current points to find the point with the maximum distance.
I got a bit lost with the Orthographical Distance function. How do I compute this? I've never seen a distance function take a line segment as a parameter.
I think aside from this I should be alright, I will just use std::vectors for the arrays. I think I will use std::copy and then push or pop according to what the algorithm says.
Thanks
The OrthogonalDistance is shown in this picture:
So it's the distance from your point and the point on the line which is the projection of that point on the line.
The distance from a point to a line is usally something like this:
(source: fauser.edu)
where x0 and y0 are the coordinates of the external point and a, b, c are the coefficient of the equation of your line.
That's what I remember from school, long time ago.
The orthogonal distance from a point P to a line L is defined by the distance between point P and point P2, where P2 is the orthogonal projection of P on line L.
How you compute this value depends on the dimension of the space you work on, but if it's 2D, you should be able to figure that out by drawing an example on a piece of paper !
Look at the topcoder tutorial and the
double linePointDist(point A, point B, point C, bool isSegment);
method it it what your looking for.
A brief description of the math required can be found here. Just realize that you can swap the word "Orthogonal" for "Perpendicular" when dealing with 2D. The site linked has instructions for lines defined by two points as well as lines defined by slope-intercept form.
The short version is reproduced here:
If the line is represented in slope intercept from: ax + by + c = 0, and the point is represented by x0, y0, then the function that will give the Orthogonal distance is:
abs(a*x0 + b*y0 + c)/sqrt(a*a + b*b)
It's not clear to me whether you want the distance of the point to the (infinite) line through the two points, or the distance to the line segment defined by the points, but I suspect its the latter.
Consider the somewhat contrived example of the points (0,0) (1,0) and (10, t) where t is small. The distance of (10,t) from the line through the first two points (ie the x axis) is t, while the distance (10,t) from the line segment with end points (0,0) and (1,0) is hypot(9,t) ~ 9. So if you were using distance to the line, there is a danger that the
algorithm above would not split at (10,t).
The method mentioned by jethro above handles both lines and line segments.
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.