Given these static points, which is Bezier Curve - geometry

I have this question that is to determine the bezier curve that goes through theses points: (1,1), (2,-1), (3,1). How can we find the curve? I dont get how to use the equation. And how could we find the curves degree?

There is infinity of Bezier curves through these points. You need to be more specific.
For example, one may define that curve is quadratic, the first point is starting one (t=0), the third is ending one (t=1) and the second is exact middle (t=0.5).
Then build equations (substituting t value and points coordinates) and solve them for coefficients
p[0].x * (1-t)^2 + p[1].x * 2 * t * (1-t) + p[2].x * t^2 = X(t)
example for the first point:
p[0].x * (1-0)^2 + p[1].x * 2 * 0 * (1-0) + p[2].x * (0)^2 = 1
p[0].x = 2

A Bezier curve of degree N is a weighted sum of N+1 control points in 2D
Sum(i=0,N) Wi(t).Ci
If you have N+1 points Pj for which the values of t are known, you get a linear system of 2(N+1) equations in 2(N+1) unknowns
Sum(I=0,N) Wi(tj).Ci = Pj
The tj can be chosen uniformly in [0,1]. Another choice is using the cumulated linear distances between the control points (also reduced to [0,1]).
It is a very different matter if you don't want to provide the tj yourself. Then you can reduce the degree of the curve and trade control points for the values of t. Generally, the number of equations and unknowns cannot match and the system will be over or underdetermined.
Another difficulty is if you impose the order of the points. Then the unknowns t are constrained to be increasing, leading to a difficult system of equations and inequations.

Related

Does Piecewise Bezier curve pass vertical line test?

Consider a piecewise cubic Bezier curve with N segments, defined in terms of 4N control points. How can I determine whether this curve passes the Vertical Line Test? That is: do there exist points x,y1,y2 such that y1!=y2 and both (x,y1) and (x,y2) lie on the curve? Additionally, it would be nice but not essential to return the values of the points x,y1,y2 if such points exist.
In principle, one could just evaluate the curve explicitly at a large number of points, but in my application the curves can have a large number of segments so this is prohibitively slow. Thus I am looking for an algorithm that operates on the control points only, and does not rely on explicitly evaluating the curves at a large number of points.
Closed curves will fail your test by definition, so let's look at open curves: for a curve to fail your vertical line test, at least one segment in your piecewise cubic polyBezier needs to "reverse direction" along the x-axis, which means its x component needs to have extrema in the inclusive interval [0,1].
For example, this curve doesn't (the right side shows the component function for x, with global extrema in red and inflections in purple):
(Also note that we're only drawing the [0,1] interval, but if we were to extend it, those global extrema wouldn't actually be at t=0 and t=1. This is in fact critically important, and we'll see why, below)
This curve also doesn't (but only just):
But this curve does:
As does this one. Twice, in fact:
As does a curve "just past its cusp":
Which is easier to see when we exaggerate it:
This means we need to find the first derivative, find out where it's zero, and then make sure that (because Beziers can have cusps) the sign of that derivative flips at that point (or points, as there can be two).
Turns out, the derivative is trivially not-even-really-computed, so for each segment we have:
w = [x1, x2, x3, x4]
Bx(t) = w[0] * (1-t)³ + 3 * w[1] * t(1-t)² + w[2] * t²(1-t) + w[3] * t³
// bezier form of the derivative:
v = [
3 * (w[1] - w[0]),
3 * (w[2] - w[1]),
3 * (w[3] - w[2]),
]
Bx'(t) = v[0] * (1-t)² + 2 * v[1] * t(1-t) + v[2] * t²
Which we can trivially rewrite to polynomial form:
a = v[0] - 2*v[1] + v[2]
b = 2 * (v[1] - v[0])
c = v[0]
Bx'(t) = a * t² + b * t + c
So we find the roots for that, which is a matter of applying the quadratic formula, which gives us 0, 1, or 2 roots.
if (a == 0) there are no roots
denominator = 2 * a
discriminant = b * b - 2 * denominator * c
if (discriminant < 0) there are no (real) roots
d = sqrt(discriminant)
t1 = -(b + d) / denominator
if (0 ≤ t1 ≤ 1) then t1 is a valid root
t2 = (d - b) / denominator
if (0 ≤ t2 ≤ 1) then t2 is a valid root
If there are no (real) roots, then this segment does not cause the curve to fail your vertical line test, and we move on to the next segment and repeat until we've either found a failure, or we've run out of segments to test.
If it's 1 or 2 roots, we check that the signs of Bx'(t-ε) and Bx'(t+ε) for some very small value of ε differ (because we want to make sure we don't conclude that the above curve with a cusp fails the test: it has a zero derivative but it "keeps going in the same direction" across that root instead of flipping direction). If they do, this segment is (one of) the segment(s) that makes your curve fail your vertical line test.
Also, note that we're testing even if the root is at 0 or 1: the piecewise curve might inflect "across segments", and we can take advantage of the fact that we can evaluate Bx'(t) for t = -ε or t = 1+ε to see if we flip direction, even if we never draw the curve prior to t=0 or after t=1.
Leaving solution here for reference. Not especially elegant but gets the job done. Assume that the curve is parametrized from left to right. The key observation is that the curve intersects a vertical at two points if and only if there is a point at which the derivative x'(t) of the x component is strictly negative. For a cubic Bezier curve the derivative is a quadratic polynomial x'(t)=at^2+bt+c. So we just need to check whether the minimum of this quadratic over the interval 0<=t<=1 is negative, which is straightforward (if a bit tedious) using basic algebra.

Making a Bezier curve based on 3 points the line will intersect

A quadratic bezier curve needs these three points, but I do not have an ordered pair of p1. Instead, I have the ordered pair of points here
The middle point (P1) is the highest point of the parabola.
The parabola is equal in both sides
How do I get the 3 points from image 1 using the points from image 2?
Apply the knowledge explained in https://pomax.github.io/bezierinfo/#abc and you should be good to go. You'll need to decide which time value that "somewhere on the curve" point has, and then you can use the formula for the projection ratio to find the actual control point coordinate.
However, at t=0.5 the ratio is just "1:1" so things get even easier because your point projects onto the midpoint of the line that connects that first and last point, and the real control point is the same distance "above" your point as the point is above that line:
So you just compute the midpoint:
m =
x: (p1.x + p2.x) / 2
y: (p1.y + p2.y) / 2
and the x and y distance to the midpoint from the "p2 you have" point:
d =
x: (p2.x - m.x)
y: (p2.y - m.y)
and then the real p2 is simply that distance away from the "p2 you have":
real2 =
x: p2.x + d.x
y: p2.y + d.y
However, note that this only works for t=0.5: both that projected point on the start--end line and the distance ratios will be (possibly very) different for any other t value and you should use the formula that the Bezier primer talks about.
Also note that what you call "the peak" is in no way guaranteed to be at t=0.5... for example, have a look at this curve:
The point that is marked as belonging to t=0.5 is certainly not where you would say the "peak" of the curve is (in fact, that's closer to t=0.56), so if all you have is three points, you technically always have incomplete information and you're going to have to invent some rule for deciding how to fill in the missing bits. In this case "what t value do I consider my somewhere-on-the-curve point to be?".

ray tracing with sphere

I am doing ray tracing with sphere. When we solve quadratic equation and get two roots then which value we need to take? My understanding is that we need to take smallest value. But with that i get wrong results when i put that value in calculating intersection point and further lighting calculations. When I take larger one it gives correct results. I saw some examples on the net and some of them are taking smaller one and others are taking larger. I am really confused which one is correct and why.
Suppose the origin of the ray is O and the direction is R (ideally a unit vector). Then the ray is defined by the parametric equation O + tR. Break this into components Ox + tRx, Oy + tRy and Oz + tRz. Substitute these into the equation for the circle x^2 + y^2 + z^2 = 0. This produces a quadratic equation with the variable t. Find the roots of the equation.
Now, if you have no real roots, the ray doesn't hit the sphere. If you have one real root then you hit the side of the sphere. If you have two real roots, then you hit the sphere twice. You want the closer root (the smaller t) unless t is negative in which case the intersection point is behind you and you don't care. If both roots are negative, then both intersection points are behind the origin of the ray and you can ignore them.
If the sphere is transformed away from the origin and you care about rotation and scaling of the sphere, then the equation of the sphere becomes more complex with xy, yz, xz, x, y and z terms. A general quadric shape is defined as Ax^2 + By^2 + Cy^2 + Dxy + Eyz + Fxz + Gx + Hy + Jz + K = 0. The principle above still applies. Substitute the parametric line equation in, solve for t, take the smallest non-negative root.
Your intersection point is O+tR where t is the smallest non-negative root. From there, I don't know why your lighting calculations would be off.

How to draw the normal to the plane in PCL

I have the plane equation describing the points belonging to a plane in 3D and the origin of the normal X, Y, Z. This should be enough to be able to generate something like a 3D arrow. In pcl this is possible via the viewer but I would like to actually store those 3D points inside the cloud. How to generate them then ? A cylinder with a cone on top ?
To generate a line perpendicular to the plane:
You have the plane equation. This gives you the direction of the normal to the plane. If you used PCL to get the plane, this is in ModelCoefficients. See the details here: SampleConsensusModelPerpendicularPlane
The first step is to make a line perpendicular to the normal at the point you mention (X,Y,Z). Let (NORMAL_X,NORMAL_Y,NORMAL_Z) be the normal you got from your plane equation. Something like.
pcl::PointXYZ pnt_on_line;
for(double distfromstart=0.0;distfromstart<LINE_LENGTH;distfromstart+=DISTANCE_INCREMENT){
pnt_on_line.x = X + distfromstart*NORMAL_X;
pnt_on_line.y = Y + distfromstart*NORMAL_Y;
pnt_on_line.z = Z + distfromstart*NORMAL_Z;
my_cloud.points.push_back(pnt_on_line);
}
Now you want to put a hat on your arrow and now pnt_on_line contains the end of the line exactly where you want to put it. To make the cone you could loop over angle and distance along the arrow, calculate a local x and y and z from that and convert them to points in point cloud space: the z part would be converted into your point cloud's frame of reference by multiplying with the normal vector as with above, the x and y would be multiplied into vectors perpendicular to this normal vectorE. To get these, choose an arbitrary unit vector perpendicular to the normal vector (for your x axis) and cross product it with the normal vector to find the y axis.
The second part of this explanation is fairly terse but the first part may be the more important.
Update
So possibly the best way to describe how to do the cone is to start with a cylinder, which is an extension of the line described above. In the case of the line, there is (part of) a one dimensional manifold embedded in 3D space. That is we have one variable that we loop over adding points. The cylinder is a two dimensional object so we have to loop over two dimensions: the angle and the distance. In the case of the line we already have the distance. So the above loop would now look like:
for(double distfromstart=0.0;distfromstart<LINE_LENGTH;distfromstart+=DISTANCE_INCREMENT){
for(double angle=0.0;angle<2*M_PI;angle+=M_PI/8){
//calculate coordinates of point and add to cloud
}
}
Now in order to calculate the coordinates of the new point, well we already have the point on the line, now we just need to add it to a vector to move it away from the line in the appropriate direction of the angle. Let's say the radius of our cylinder will be 0.1, and let's say an orthonormal basis that we have already calculated perpendicular to the normal of the plane (which we will see how to calculate later) is perpendicular_1 and perpendicular_2 (that is, two vectors perpendicular to each other, of length 1, also perpendicular to the vector (NORMAL_X,NORMAL_Y,NORMAL_Z)):
//calculate coordinates of point and add to cloud
pnt_on_cylinder.x = pnt_on_line.x + 0.1 * perpendicular_1.x * 0.1 * cos(angle) + perpendicular_2.x * sin(angle)
pnt_on_cylinder.y = pnt_on_line.y + perpendicular_1.y * 0.1 * cos(angle) + perpendicular_2.y * 0.1 * sin(angle)
pnt_on_cylinder.z = pnt_on_line.z + perpendicular_1.z * 0.1 * cos(angle) + perpendicular_2.z * 0.1 * sin(angle)
my_cloud.points.push_back(pnt_on_cylinder);
Actually, this is a vector summation and if we were to write the operation as vectors it would look like:
pnt_on_line+perpendicular_1*cos(angle)+perpendicular_2*sin(angle)
Now I said I would talk about how to calculate perpendicular_1 and perpendicular_2. Let K be any unit vector that is not parallel to (NORMAL_X,NORMAL_Y,NORMAL_Z) (this can be found by trying e.g. (1,0,0) then (0,1,0)).
Then
perpendicular_1 = K X (NORMAL_X,NORMAL_Y,NORMAL_Z)
perpendicular_2 = perpendicular_1 X (NORMAL_X,NORMAL_Y,NORMAL_Z)
Here X is the vector cross product and the above are vector equations. Note also that the original calculation of pnt_on_line involved a vector dot product and a vector summation (I am just writing this for completeness of the exposition).
If you can manage this then the cone is easy just by changing a couple of things in the double loop: the radius just changes along its length until it is zero at the end of the loop and in the loop distfromstart will not start at 0.

Given a set of points, how do I approximate the major axis of its shape?

Given a "shape" drawn by the user, I would like to "normalize" it so they all have similar size and orientation. What we have is a set of points. I can approximate the size using bounding box or circle, but the orientation is a bit more tricky.
The right way to do it, I think, is to calculate the majoraxis of its bounding ellipse. To do that you need to calculate the eigenvector of the covariance matrix. Doing so likely will be way too complicated for my need, since I am looking for some good-enough estimate. Picking min, max, and 20 random points could be some starter. Is there an easy way to approximate this?
Edit:
I found Power method to iteratively approximate eigenvector. Wikipedia article.
So far I am liking David's answer.
You'd be calculating the eigenvectors of a 2x2 matrix, which can be done with a few simple formulas, so it's not that complicated. In pseudocode:
// sums are over all points
b = -(sum(x * x) - sum(y * y)) / (2 * sum(x * y))
evec1_x = b + sqrt(b ** 2 + 1)
evec1_y = 1
evec2_x = b - sqrt(b ** 2 + 1)
evec2_y = 1
You could even do this by summing over only some of the points to get an estimate, if you expect that your chosen subset of points would be representative of the full set.
Edit: I think x and y must be translated to zero-mean, i.e. subtract mean from all x, y first (eed3si9n).
Here's a thought... What if you performed a linear regression on the points and used the slope of the resulting line? If not all of the points, at least a sample of them.
The r^2 value would also give you information about the general shape. The closer to 0, the more circular/uniform the shape is (circle/square). The closer to 1, the more stretched out the shape is (oval/rectangle).
The ultimate solution to this problem is running PCA
I wish I could find a nice little implementation for you to refer to...
Here you go! (assuming x is a nx2 vector)
def majAxis(x):
e,v = np.linalg.eig(np.cov(x.T)); return v[:,np.argmax(e)]

Resources