I am working on a C++ problem where I'm trying to make a utility function func() that takes as input two line segments starting points in 3d space [(x,y,z) and radius r]. If the segments can be oriented such that they end at the same point, the function should return true and print out that point. If there are multiple orientations that would produce a common endpoint, the function should choose the one that is furthest in the direction indicated by hint_direction.
The function receives these values:
bool func(
point3d position_0, // origin of first line segment.
float length_0, // length of first line segment.
point3d position_1, // origin of second line segment.
float length_1, // length of second line segment.
vector3d hint_direction, // in the event there are multiple solutions, return the one furthest in this direction.
point3d *out_common_end_position) // if result is true, point where both line segments can be oriented to end. otherwise uninitialized.
I am working on one of the first edge cases; where there is a single intersection point such as in the image below.
(image link = https://i.stack.imgur.com/dh9Vr.png)
My code correctly identifies this edge case, but I'm not sure how to programmatically find this intersection point.
//calling function with example from image above, cords, radius, and hint
bool result = func({1, 1, 0}, 1.0, {3, 1, 0}, 1.0, {0, 0, 1}, &common_end_position);
bool func(point3d position_0, float length_0, point3d position_1, float length_1,vector3d hint_direction,point3d *out_common_end_position){
//if statement detecting single intersection
if(length_0 + length_1 == d){
printf("intersection at a single point\n");
//find single intersection point (?)
}
I have been following some guides online which lay out how to do this such as this:
https://gamedev.stackexchange.com/questions/75756/sphere-sphere-intersection-and-circle-sphere-intersection
Which says: "If r_1 + r_2 == d, then the intersection is a single point, located a distance of r_1 on the line from c_1 to c_2, or: c_i = c_1 + (c_2 - c_1) * r_1/d"
It's been a long time since I've done geometry like this, if I want to find the single point of intersection, how do I do that with the equation above "c_i = c_1 + (c_2 - c_1) * r_1/d" ? I understand that c_2 - c_1 is the distance between both centers which I have computed earlier in my program as float d, but I'm sure sure what's meant by "c_1 + " since c_1 refers to the whole set of cords (x,y,z).
Overall, I'm trying to find a way to get the single intersection point for example such as in my image, could somebody please help em understand the linked solution above? I am going to continue to research solutions in the meantime. Thank you.
I understand that c_2 - c_1 is the distance between both centers
Incorrect. c_2 - c_1 is the vector (in the geometry sense, not the c++ sense) that describes the direction from c_1 to c_2 and which has magnitude d. (c_2 - c_1)/d is therefore the unit vector that describes the direction from c_1 to c_2.
(c_2 - c_1)/d * r_1 is therefore the point on the line between c_1 and c_2 that is distance r_1 from c_1, or, the intersection point of the circles.
but I'm sure sure what's meant by "c_1 + " since c_1 refers to the whole set of cords (x,y,z).
c_1 is not the whole set of points on the circle. c_1 is the center of circle 1.
Also keep in mind that you're dealing with floats. As such, it is subject to floating point errors. Looking for an exact equality in cases like this is subject to error. Consider that looking to see if d is within some acceptable error tolerance of r1 + r2 might be more appropriate
((r1 + r2 - e) <= d && d <= (r1 + r2 + e))
instead of
d == r1 + r2
Related
I am currently looking to implement an algorithm that will be able to compute the arc midpoint. From here on out, I will be referring to the diagram below. What is known are the start and end nodes (A and B respectively), the center (point C) and point P which is the intersection point of the line AB and CM (I am able to find this point without knowing point M because line AB is perpendicular to line CM and thus, the slope is -1/m). I also know the arc angle and the radius of the arc. I am looking to find point M.
I have been looking at different sources. Some suggest converting coordinates to polar, computing the mid point from the polar coordinates then reverting back to Cartesian. This involves sin and cos (and arctan) which I am a little reluctant to do since trig functions take computing time.
I have been looking to directly computing point M by treating the arc as a circle and having Line CP as a line that intersects the circle at Point M. I would then get two values and the value closest to point P would be the correct intersection point. However, this method, the algebra becomes long and complex. Then I would need to create special cases for when P = C and for when the line AB is horizontal and vertical. This method is ok but I am wondering if there are any better methods out there that can compute this point that are simpler?
Also, as a side note, I will be creating this algorithm in C++.
A circumference in polar form is expressed by
x = Cx + R cos(alpha)
y = Cy + R sin(alpha)
Where alpha is the angle from center C to point x,y. The goal now is how to get alpha without trigonometry.
The arc-midpoint M, the point S in the middle of the segment AB, and your already-calculated point P, all of them have the same alpha, they are on the same line from C.
Let's get vector vx,vy as C to S. Also calculate its length:
vx = Sx - Cx = (Ax + Bx)/2 - Cx
vy = Sy - Cy = (Ay + By)/2 - Cy
leV = sqrt(vx * vx + vy * vy)
I prefer S to P because we can avoid some issues like infinite CP slope or sign to apply to slope (towards M or its inverse).
By defintions of sin and cos we know that:
sin(alpha) = vy / leV
cos(alpha) = vx / leV
and finally we get
Mx = Cx + R * vx / leV
My = Cy + R * vy / leV
Note: To calculate Ryou need another sqrt function, which is not quick, but it's faster than sin or cos.
For better accuracy use the average of Ra= dist(AC) and Rb= dist(BC)
I would then get two values
This is algebraically unavoidable.
and the value closest to point P would be the correct intersection point.
Only if the arc covers less than 180°.
Then I would need to create special cases for when P = C
This is indeed the most tricky case. If A, B, C lie on a line, you don't know which arc is the arc, and won't be able to answer the question. Unless you have some additional information to start with, e.g. know that the arc goes from A to B in a counter-clockwise direction. In this case, you know the orientation of the triangle ABM and can use that to decide which solition to pick, instead of using the distance.
and for when the line AB is horizontal and vertical
Express a line as ax + by + c = 0 and you can treat all slopes the same. THese are homogeneous coordinates of the line, you can compute them e.g. using the cross product (a, b, c) = (Ax, Ay, 1) × (Bx, By, 1). But more detailed questions on how best to compute these lines or intersect it with the circle should probably go to the Math Stack Exchange.
if there are any better methods out there that can compute this point that are simpler?
Projective geometry and homogeneous coordinates can avoid a lot of nasty corner cases, like circles of infinite radius (also known as lines) or the intersection of parallel lines. But the problem of deciding between two solutions remains, so it probably doesn't make things as simple as you'd like them to be.
Is there any numerically stable angle bisector algorithm?
The problem is the following:
Given three vectors (2 dimensional) A,B,C
Find the bisector of angle B (angle between AB and BC)
Actually I'm computing it in the following way:
Normalize AB
Normalize BC
Find (AB+CD)/2f (Mid Point)
The bisector is line passing between B and the Mid Point.
The problem with my approach is that when the angle is almost 180° (AB almost parallel to BC) the bisector is very inaccurate (of course because mid point is almost coincident with B). The current algorithm is so inaccurate that sometimes the resulting bisector is almost parallel to one of the other 2 segments.
And yes there are no "cast" problems, all computations are done in single precision floating point.
You could use that the angle bisector remains the same if you rotate BA by +90° and BC by -90°.
So use the original formula if the situation is stable, that is, if the dot product of BA and BC is positive.
If it is negative, apply the rotations, for BA (x,y) -> (-y,x) and for BC (x,y) -> (y,-x), which also renders the dot product positive. Proceed as before with the new vectors.
If you try this out you will note that the jump in direction of the bisector now occurs for the angle -90° between the vectors. It is not possible to avoid this jump, as a continuous bisector will only be the same after two turns (fixing BA and moving C).
It’s not trivial. Let’s say the two edge vectors are a and b:
float2 a = A - B;
float2 b = C - B;
Compute the dot product float dp = dot( a, b )
Normalize both vectors:
float2 a_norm = normalize( a );
float2 b_norm = normalize( b );
Check the sign bit of the dot product. When the dp is non-negative,
return normalize( a_norm + b_norm ); and you’re done.
When the dot product is negative, you have obtuse angle between input vectors.
Applying a naïve formula in this case would screw up the numerical precision.
Need another way.
float2 c = normalize( a_norm - b_norm );
float dir = dot( a, rotate90( b ) );
return ( dir < 0 ) ? rotate90( c ) : rotate270( c );
Note - instead of the +, this is what gives the precision win. When the angle between a and b is greater than 90°, the angle between a and -b is less than 90°, and the length of a_norm - b_norm is large enough to give accurate direction. We just need to rotate it by 90° afterwards, in the correct direction.
P.S. Rotating 2D vectors by multiples of 90° is lossless operation.
Here’s pseudocode for rotate90 and rotate270 functions:
float2 rotate90( float2 vec )
{
return float2( vec.y, -vec.x );
}
float2 rotate270( float2 vec )
{
return float2( -vec.y, vec.x );
}
A simple enough way to do this follows in two formats (but the content is otherwise identical):
Pseudocode
// Move A and C to the origin for easier rotation calculations
Aprime=A-B;
Cprime=C-B;
// The counter-clockwise angle between the positive X axis to A'
angle_a = arctan(Aprime.y, Aprimet.x);
// ditto for C'
angle_c = arctan(Cprime.y, Cprime.x);
// The counter-clockwise angle from A' to C'
angle_ac = angle_c - angle_a;
// The counter-clockwise angle from the positive X axis to M'
angle_m = angle_ac/2 + angle_a;
// Construct M' which, like A' and C', is relative to the origin.
Mprime=(cos(angle_m), sin(angle_m));
// Construct M which is relative to B rather than relative to the origin.
M=Mprime+B
In English
Move the vectors to the origin by
A'=A-B
B'=B
C'=C-B
Get the angle from the positive X axis to A' as angle_a = arctan(A_y, A_x).
Get the angle from the positive X axis to C' as angle_c = arctan(C_y, C_x).
Get the counter-clockwise angle from A' to C' as angle_ac = angle_c - angle_a.
Get the angle from the positive X axis to M' as angle_m = angle_ac/2 + angle_a.
Construct M' from this angle as M' = (cos(angle_m), sin(angle_m)).
Construct M as M = M' + B.
The vector BM bisects the angle ABC.
Since there is arbitrary division, there are no difficulties with this method. Here's a graphing calculator to encourage intuition with the solution: https://www.desmos.com/calculator/xwbno717da
You can find the bisecting vector quite simply with:
∥BC∥ * BA + ∥BA∥ * BC
But that also won't be numerically stable with ABC collinear or nearly so. What might work better would be to find the angle between AB and BC, via the dot product.
cos θ = (BA · BC) / (∥BC∥ * ∥BA∥)
That will produce the correct angle even in the collinear case.
Definition: If A and B are points, vector(A,B) is the vector from point A to B.
Lets say that point O is the point of origin for our coordinate system.
The coordinates of point A are the same as of radius-vector(O,A).
Let point M be the middle point for the bisector,so you need to:
-normalize vector(B,A)
-normalize vector(B,C)
-vector(B,M) = vector(B,A)+vector(B,C) //vector from B to middle point
-(optionally) You can multiply vector(B,M) with a scalar to get a longer vector / increase distance between B and M
-vector(O,M) = vector(O,B) + vector(B,M)//radius-vector from O to M
Now middle point M has the same coordinates as radius-vector(O,M).
I want to fit a plane to a 3D point cloud. I use a RANSAC approach, where I sample several points from the point cloud, calculate the plane, and store the plane with the smallest error. The error is the distance between the points and the plane. I want to do this in C++, using Eigen.
So far, I sample points from the point cloud and center the data. Now, I need to fit the plane to the samples points. I know I need to solve Mx = 0, but how do I do this? So far I have M (my samples), I want to know x (the plane) and this fit needs to be as close to 0 as possible.
I have no idea where to continue from here. All I have are my sampled points and I need more data.
From you question I assume that you are familiar with the Ransac algorithm, so I will spare you of lengthy talks.
In a first step, you sample three random points. You can use the Random class for that but picking them not truly random usually gives better results. To those points, you can simply fit a plane using Hyperplane::Through.
In the second step, you repetitively cross out some points with large Hyperplane::absDistance and perform a least-squares fit on the remaining ones. It may look like this:
Vector3f mu = mean(points);
Matrix3f covar = covariance(points, mu);
Vector3 normal = smallest_eigenvector(covar);
JacobiSVD<Matrix3f> svd(covariance, ComputeFullU);
Vector3f normal = svd.matrixU().col(2);
Hyperplane<float, 3> result(normal, mu);
Unfortunately, the functions mean and covariance are not built-in, but they are rather straightforward to code.
Recall that the equation for a plane passing through origin is Ax + By + Cz = 0, where (x, y, z) can be any point on the plane and (A, B, C) is the normal vector perpendicular to this plane.
The equation for a general plane (that may or may not pass through origin) is Ax + By + Cz + D = 0, where the additional coefficient D represents how far the plane is away from the origin, along the direction of the normal vector of the plane. [Note that in this equation (A, B, C) forms a unit normal vector.]
Now, we can apply a trick here and fit the plane using only provided point coordinates. Divide both sides by D and rearrange this term to the right-hand side. This leads to A/D x + B/D y + C/D z = -1. [Note that in this equation (A/D, B/D, C/D) forms a normal vector with length 1/D.]
We can set up a system of linear equations accordingly, and then solve it by an Eigen solver as follows.
// Example for 5 points
Eigen::Matrix<double, 5, 3> matA; // row: 5 points; column: xyz coordinates
Eigen::Matrix<double, 5, 1> matB = -1 * Eigen::Matrix<double, 5, 1>::Ones();
// Find the plane normal
Eigen::Vector3d normal = matA.colPivHouseholderQr().solve(matB);
// Check if the fitting is healthy
double D = 1 / normal.norm();
normal.normalize(); // normal is a unit vector from now on
bool planeValid = true;
for (int i = 0; i < 5; ++i) { // compare Ax + By + Cz + D with 0.2 (ideally Ax + By + Cz + D = 0)
if ( fabs( normal(0)*matA(i, 0) + normal(1)*matA(i, 1) + normal(2)*matA(i, 2) + D) > 0.2) {
planeValid = false; // 0.2 is an experimental threshold; can be tuned
break;
}
}
This method is equivalent to the typical SVD-based method, but much faster. It is suitable for use when points are known to be roughly in a plane shape. However, the SVD-based method is more numerically stable (when the plane is far far away from origin) and robust to outliers.
I am following this paper here using de Casteljau's Algorithm http://www.cgafaq.info/wiki/B%C3%A9zier_curve_evaluation and I have tried using the topic Drawing Bezier curves using De Casteljau Algorithm in C++ , OpenGL to help. No success.
My bezier curves look like this when evaluated
As you can see, even though it doesn't work the wanted I wanted it to, all the points are indeed on the curve. I do not think that this algorithm is inaccurate for this reason.
Here are my points on the top curve in that image:
(0,0)
(2,0)
(2,2)
(4,2) The second curve uses the same set of points, except the third point is (0,2), that is, two units above the first point, forming a steeper curve.
Something is wrong. I should put in 0.25 for t and it should spit out 1.0 for the X value, and .75 should always return 3. Assume t is time. It should progress at a constant rate, yeah? Exactly 25% of the way in, the X value should be 1.0 and then the Y should be associated with that value.
Are there any adequate ways to evaluate a bezier curve? Does anyone know what is going on here?
Thanks for any help! :)
EDIT------
I found this book in a google search http://www.tsplines.com/resources/class_notes/Bezier_curves.pdf and here is the page I found on explicit / non-parametric bezier curves. They are polynomials represented as bezier curves, which is what I am going for here. Here is that page from the book:
Anyone know how to convert a bezier curve to a parametric curve? I may open a different thread now...
EDIT AGAIN AS OF 1 NOVEMBER 2011-------
I've realized that I was only asking the question about half as clear as I should have. What I'm trying to build is like Maya's animation graph editor such as this http://www.youtube.com/watch?v=tckN35eYJtg&t=240 where the bezier control points that are used to modify the curve are more like tangent modifiers of equal length. I didn't remember them as being equal length, to be honest. By forcing a system like this, you can insure 100% that the result is a function and contains no overlapping segments.
I found this, which may have my answer http://create.msdn.com/en-US/education/catalog/utility/curve_editor
Here you can see the algorithm implemented in Mathematica following the nomenclature in your link, and your two plots:
(*Function Definitions*)
lerp[a_, b_, t_] := (1 - t) a + t b;
pts1[t_] := {
lerp[pts[[1]], pts[[2]], t],
lerp[pts[[2]], pts[[3]], t],
lerp[pts[[3]], pts[[4]], t]};
pts2[t_] := {
lerp[pts1[t][[1]], pts1[t][[2]], t],
lerp[pts1[t][[2]], pts1[t][[3]], t]};
pts3[t_] := {
lerp[pts2[t][[1]], pts2[t][[2]], t]};
(*Usages*)
pts = {{0, 0}, {2, 0}, {2, 2}, {4, 2}};
Framed#Show[ParametricPlot[pts3[t], {t, 0, 1}, Axes -> True],
Graphics[{Red, PointSize[Large], Point#pts}]]
pts = {{0, 0}, {2, 0}, {0, 2}, {4, 2}};
Framed#Show[ParametricPlot[pts3[t], {t, 0, 1}, Axes -> True],
Graphics[{Red, PointSize[Large], Point#pts}]]
BTW, the curves are defined by the following parametric equations, which are the functions pts3[t] in the code above:
c1[t_] := {2 t (3 + t (-3 + 2 t)), (* <- X component *)
2 (3 - 2 t) t^2} (* <- Y component *)
and
c2[t_] := {2 t (3 + t (-6 + 5 t)), (* <- X component *)
, 2 (3 - 2 t) t^2} (* <- Y component *)
Try plotting them!
Taking any of these curve equations, and by solving a cubic polynomial you can in these cases get an expression for y[x], which is certainly not always possible. Just for you to get a flavor of it, from the first curve you get (C syntax):
y[x]= 3 - x - 3/Power(-2 + x + Sqrt(5 + (-4 + x)*x),1/3) +
3*Power(-2 + x + Sqrt(5 + (-4 + x)*x),1/3)
Try plotting it!
Edit
Just an amusement:
Mathematica is a quite powerful functional language, and in fact the whole algorithm can be expressed as a one liner:
f = Nest[(1 - t) #[[1]] + t #[[2]] & /# Partition[#, 2, 1] &, #, Length## - 1] &
Such as
f#{{0, 0}, {2, 0}, {0, 2}, {4, 2}}
gives the above results, but supports any number of points.
Let's try with six random points:
p = RandomReal[1, {6, 2}];
Framed#Show[
Graphics[{Red, PointSize[Large], Point#p}],
ParametricPlot[f#p, {t, 0, 1}, Axes -> True]]
Moreover, the same function works in 3D:
p = RandomReal[1, {4, 3}];
Framed#Show[
Graphics3D[{Red, PointSize[Large], Point#p}],
ParametricPlot3D[f[p], {t, 0, 1}, Axes -> True]]
A bezier curve can be solved by solving the following parametric equations for the x, y, and z coordinates (if it's just 2D, do only x and y):
Px = (1-t)^3(P1x) + 3t(1-t)^2(P2x) + 3t^2(1-t)(P3x) + t^3(P4x)
Py = (1-t)^3(P1y) + 3t(1-t)^2(P2y) + 3t^2(1-t)(P3y) + t^3(P4y)
Pz = (1-t)^3(P1z) + 3t(1-t)^2(P2z) + 3t^2(1-t)(P3z) + t^3(P4z)
You can also solve this by multiplying the matrix equation ABC = X where:
matrix A is a 1x4 matrix and represents the values of the powers of t
matrix B are the coefficients of the powers of t, and is a lower-triangular 4x4 matrix
matrix C is a 4x3 matrix that represents each of the four bezier points in 3D-space (it would be a 4x2 matrix in 2D-space)
This would look like the following:
(Update - the bottom left 1 ought to be a -1)
An important note in both forms of the equation (the parametric and the matrix forms) is that t is in the range [0, 1].
Rather than attempting to solve the values for t that will give you integral values of x and y, which is going to be time-consuming given that you're basically solving for the real root of a 3rd-degree polynomial, it's much better to simply create a small enough differential in your t value such that the difference between any two points on the curve is smaller than a pixel-value increment. In other words the distance between the two points P(t1) and P(t2) is such that it is less than a pixel value. Alternatively, you can use a larger differential in t, and simply linearly interpolate between P(t1) and P(t2), keeping in mind that the curve may not be "smooth" if the differential between P(t1) and P(t2) is not small enough for the given range of t from [0, 1].
A good way to find the necessary differential in t to create a fairly "smooth" curve from a visual standpoint is to actually measure the distance between the four points that define the bezier curve. Measure the distance from P1 to P2, P2, to P3, and P3 to P4. Then take the longest distance, and use the inverse of that value as the differential for t. You may still need to-do some linear interpolation between points, but the number of pixels in each "linear" sub-curve should be fairly small, and therefore the curve itself will appear fairly smooth. You can always decrease the differential value on t from this initial value to make it "smoother".
Finally, to answer your question:
Assume t is time. It should progress at a constant rate, yeah? Exactly 25% of the way in, the X value should be 1.0 and then the Y should be associated with that value.
No, that is not correct, and the reason is that the vectors (P2 - P1) and (P3 - P4) are not only tangent to the bezier curve at P1 and P4, but their lengths define the velocity along the curve at those points as well. Thus if the vector (P2 - P1) is a short distance, then that means for a given amount of time t, you will not travel very far from the point P1 ... this translates into the x,y values along the curve being packed together very closely for a given fixed differential of t. You are effectively "slowing down" in velocity as you move towards P1. The same effect takes place at P4 on the curve depending on the length of the vector (P3 - P4). The only way that the velocity along the curve would be "constant", and therefore the distance between any points for a common differential of t would be the same, would be if the lengths of all three segements (P2 - P1), (P3 - P2), and (P4 - P3) were the same. That would then indicate that there was no change in velocity along the curve.
It sounds like you actually just want a 1D cubic Bezier curve instead of the 2D that you have. Specifically, what you actually want is just a cubic polynomial segment that starts at 0 and goes up to 2 when evaluated over the domain of 0 to 4. So you could use some basic math and just find the polynomial:
f(x) = a + b*x + c*x^2 + d*x^3
f(0) = 0
f(4) = 2
That leaves two degrees of freedom.
Take the derivative of the function:
f'(x) = b + 2*c*x + 3*d*x^2
If you want it to be steep at the beginning and then level off at the end you might say something like:
f'(0) = 10
f'(4) = 0
Then we can plug in values. a and b come for free because we're evaluating at zero.
a = 0
b = 10
So then we have:
f(4) = 2 = 40 + c*16 + d*64
f'(4) = 0 = 10 + c*8 + d*48
That's a pretty easy linear system to solve. For completeness, we get:
16c + 64d = -38
8c + 48d = -10
So-
1/(16*48 - 8*64)|48 -64||-38| = |c| = |-37/8 |
|-8 16||-10| |d| | 9/16|
f(x) = 10*x - (37/8)*x^2 + (9/16)*x^3
If, instead, you decide that you want to use Bezier control points, just pick your 4 y-value control points and recognize that in order to get t in [0,1], you just have to say t=x/4 (remembering that if you also need derivatives, you'll have to do a change there too).
Added:
If you happen to know the points and derivatives you want to begin and end with, but you want to use Bezier control points P1, P2, P3, and P4, the mapping is just this (assuming a curve parametrized over 0 to 1):
P1 = f(0)
P2 = f'(0)/3 + f(0)
P3 = f(1) - f'(1)/3
P4 = f(1)
If, for some reason, you wanted to stick with your 2D Bezier control points and wanted to ensure that the x dimension advanced linearly from 0 to 2 as t advanced from 0 to 1, then you could do that with control points (0,y1) (2/3,y2) (4/3,y3) (2,y4). You can see that I just made the x dimension start at 0, end at 2, and have a constant slope (derivative) of 2 (with respect to t). Then you just make the y-coordinate be whatever you need it to be. The different dimensions are essentially independent of each other.
After all this time, I was looking for hermite curves. Hermites are good because in one dimension they're guaranteed to produce a functional curve that can be evaluated to an XY point. I was confusing Hermites with Bezier.
"Assume t is time."
This is the problem - t is not the time. The curve has it's own rate of change of t, depending on the magnitude of the tangents. Like Jason said, the distance between the consequent points must be the same in order of t to be the same as the time. This is exactly what the non-weighted mode (which is used by default) in the Maya's curve editor is. So this was a perfectly good answer for how to fix this issue. To make this work for arbitrary tangents, you must convert the time to t. You can find t by calculating the bezier equation in the x (or time) direction.
Px = (1-t)^3(P1x) + 3t(1-t)^2(P2x) + 3t^2(1-t)(P3x) + t^3(P4x)
Px is your time, so you know everything here, but t. You must solve a cubic equation to find the roots. There is a tricky part to find the exact root you need though. Then you solve the other equation to find Py (the actual value you are looking for), knowing now t:
Py = (1-t)^3(P1y) + 3t(1-t)^2(P2y) + 3t^2(1-t)(P3y) + t^3(P4y)
This is what the weighted curves in Maya are.
I know the question is old, but I lost a whole day researching this simple thing, and nobody explains exactly what happens. Otherwise, the calculation process itself is written on many places, the Maya API manual for example. The Maya devkit also has a source code to do this.
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.