Hermite Interpolation - c#

I am trying to interpolate between 4 points using a Hermite spline. However my spline seems to always start on the second point and only interpolate to the 3rd point. I have tried this with several differnt calculations and keep getting the same result.
Can anyone give me insight on this? Here is my code.
public ControlPoint Get(float t)
{
//speed multiplyer
//t = t * 10;
return new ControlPoint(
new Vector3(Hermite(points[0].pos.x, points[1].pos.x, points[2].pos.x, points[3].pos.x, t)
, Hermite(points[0].pos.y, points[1].pos.y, points[2].pos.y, points[3].pos.y, t)
, Hermite(points[0].pos.z, points[1].pos.z, points[2].pos.z, points[3].pos.z, t)
),
new Quaternion(Hermite(points[0].rot.x, points[1].rot.x, points[2].rot.x, points[3].rot.x, t)
, Hermite(points[0].rot.y, points[1].rot.y, points[2].rot.y, points[3].rot.y, t)
, Hermite(points[0].rot.z, points[1].rot.z, points[2].rot.z, points[3].rot.z, t)
, Hermite(points[0].rot.w, points[1].rot.w, points[2].rot.w, points[3].rot.w, t)
)
);
}
float Hermite(
float y0, float y1,
float y2, float y3,
float mu,
float tension = 0,
float bias = 0)
{
float m0, m1, mu2, mu3;
float a0, a1, a2, a3;
mu2 = mu * mu;
mu3 = mu2 * mu;
m0 = (y1 - y0) * (1 + bias) * (1 - tension) / 2;
m0 += (y2 - y1) * (1 - bias) * (1 - tension) / 2;
m1 = (y2 - y1) * (1 + bias) * (1 - tension) / 2;
m1 += (y3 - y2) * (1 - bias) * (1 - tension) / 2;
a0 = 2 * mu3 - 3 * mu2 + 1;
a1 = mu3 - 2 * mu2 + mu;
a2 = mu3 - mu2;
a3 = -2 * mu3 + 3 * mu2;
return (a0 * y1 + a1 * m0 + a2 * m1 + a3 * y2);
}

I'm not an expert Hermite Splines by any stretch of the imagination, but from what I've seen is that the expected behavior would be to interpolate between the second and third point. It looks to me like you just hardcoded in each coordinate to your Get function, so it makes sense that you only get a single interpolation when a Hermite Spline is a function. Think of the second and third points as the two points you want to interpolate between, and the first and fourth points just help to create a better curve.
Since it appears you only have four points total, to interpolate between the first and second points, and third and fourth points, try repeating your first coordinates and last coordinates.
//Interpolate between 1st and 2nd points' x coord
Hermite(points[0].pos.x, points[0].pos.x, points[1].pos.x, points[2].pos.x);
//Interpolate between 3rd and 4th points' x coord
Hermite(points[2].pos.x, points[3].pos.x, points[4].pos.x, points[4].pos.x);
To interpolate between the first and second points points[0] is repeated twice because there is no points[-1]. For interpolation between the third and fourth points, points[4] is repeated because there is no points[5].
To reiterate, do not hardcode in coordinates unless you only want a single interpolation. You'll have to modify your Get function and call it a few times to adjust for the behavior you want. Check out how Snea implemented a Hermite Spline in his DrawGraph function, it helped me to better understand Hermite Spline behavior: Cubic Hermite Spline behaving strangely

Related

How to move the end points of a line to keep its length but make it parallel to another line (C# preferably!)

I have a line segment whose end points I know
Line1 (X1,Y1) (X2,Y2)
I have a second line
Line2 (X3,Y3) (X4,Y4)
I want to calculate new end points for line 1 such that the resulting line is parallel to Line2, and Line1's centre point remains at the same coordinates.
i.e. such that Line1 simply rotates so it is parallel to Line2
I know I can calculate each line's angle
var line1Angle = (Mathf.Atan2(x2 - x1, y2 - y1));
var line2Angle = (Mathf.Atan2(x4 - x3, y4 - y3));
I can also calculate the lengths
var len1 = Math.Sqrt((x2-x1)*(x2-x1)+ (y2-y1) * (y2-y1));
var len2 = Math.Sqrt((x4-x3)*(x4-x3)+ (y4-y3) * (y4-y3));
but everything I have tried seems to fail - either not rotating correctly, or rotating but with the incorrect length.
The closest code I have (below) rotates correctly, but the length of Line1 is not retained.
The code uses an 'offset' which was used by the code this version is based on as it simply drew a parallel line 'offset' pixels from the destination line - I have set it to an arbitrary value but I believe should be the distance of Line1's centre point from the closest point on Line2.
I'd love it if someone could supply a code version, rather than an explanation (or as well as!) as I've read and tried so many non-code solutions, and evidently my understanding / translation to code is flawed!
float len1 = (float)Math.Sqrt((x2-x1)*(x2-x1)+ (y2-y1) * (y2-y1));
float len2 = (float)Math.Sqrt((x4-x3)*(x4-x3)+ (y4-y3) * (y4-y3));
float offset = 3.0f; // This should be the dist from our center to the closest wall but I"m compromising for now!
float newX1 = x3 + offset * (y4 - y3) * (len1 / len2);
float newX2 = x4 + offset * (y4 - y3) * (len1 / len2);
float newY1 = y3 + offset * (x3 - x4) * (len1 / len2);
float newY2 = y4 + offset * (x3 - x4) * (len1 / len2);
Approach without angles: you know segment lengths, and can just form new segment ends with the same direction as line2 defines
dx1 = x2 - x1
dy1 = y2 - y1
dx2 = x4 - x3
dy2 = y4 - y3
len1 = hypot(dx1, dy1)
len2 = hypot(dx2, dy2)
midx = (x1 + x2) / 2
midy = (y1 + y2) / 2
coeff = 0.5 * len1 / len2
//now we make a vector with direction of line2
//and length of half of line1
newx1 = midx - dx2 * coeff
newy1 = midy - dy2 * coeff
newx2 = midx + dx2 * coeff
newy2 = midy + dy2 * coeff
I would highly recommend that you define actual types for your line segment and points. You can use Math.Net or System.Numerics.Vectors if you want something to start with.
Assuming your line segment have a StartPoint and EndPoint we can define a MidPoint and Direction extension methods. I'm going to use the Math.Net types for the example, but it is not difficult to make your own types.
static Vector2D Midpoint(this Line2D l) => (l.StartPoint + l.EndPoint) / 2;
static Vector2D Direction(this Line2D l) => (l.EndPoint - l.StartPoint ).Normalize() ;
We can also define a static method to create a new line from these methods/Properties:
static Line2D FromMidpointDirection(Vector2D midpoint, Vector2D direction, float length){
var halfDir = direction * length/ 2;
return new Line2D(midpoint - halfDir , midpint + halfDir );
Note that you might want to add comments or pick another name for Direction, since it is not obvious if this is normalized or not.
Then you can recreate your line:
var mid = sourceLine.Midpoint();
var dir = targetLine.Direction();
var newLine = FromMidpointDirection(mid, dir,sourceLine.Length);
Using higher level types like this tend to make your code more reusable and easier to read and understand.

How to find the radius of a circle given three points in 3d space C#

I have locations of three points along the circle. pt1(x1, y1,z1), pt2(x2, y2, z2), pt3(x3, y3,z3). and want to find the radius of the circle. Already I have a function to compute radius in 2d space, which I am copying it here
public static double ComputeRadius(Location a, Location b, Location c)
{
double x1 = a.x;
double y1 = a.y;
double x2 = b.x;
double y2 = b.y;
double x3 = c.x;
double y3 = c.y;
double mr = (double)((y2 - y1) / (x2 - x1));
double mt = (double)((y3 - y2) / (x3 - x2));
double xc = (double)((mr * mt * (y3 - y1) + mr * (x2 + x3) - mt * (x1 + x2)) / (2 * (mr - mt)));
double yc = (double)((-1 / mr) * (xc - (x1 + x2) / 2) + (y1 + y2) / 2);
double d = (xc - x1) * (xc - x1) + (yc - y1) * (yc - y1);
return Math.Sqrt(d);
}
If you know the order of points pt1,pt2,pt3 along the circle then you can use graphical method:
cast normal axises from middle of each line segment in the plane of circle
your circle plane is defined by your 3 points. so the normal vector is
n = (pt2-pt1) x (pt3-pt2)
where the x is cross product so you have 2 lines (pt1,pt2) and (pt2,pt3) in black. The mid points are easy
p0=0.5*(pt1+pt2)
p1=0.5*(pt2+pt3)
the axis directions can be obtained also by cross product
dp0=(pt2-pt1) x n
dp1=(pt3-pt2) x n
so you got 2 axises:
pnt0(t)=p0+dp0*t
pnt1(u)=p1+dp1*u
Where t,u are scalar parameters t,u=(-inf,+inf) it is just position in axis from the starting mid point ...
the intersection is center of circle
So find the intersection of 2 axises and call it pt0
compute distance between center and any of your points
r=|pt1-pt0|
Sorry the image is for any curve (too lazy to repaint for circle as it is almost the same). If you do not know the order of points then the 2 points that are most distant to each other are the outer points ... In case they are equidistant the order does not matter any is OK

Tracking speed with kinect and Visual Studio

I need to track the speed of a kick. I programmed this code, but when I run the program even when I move my Right Foot very fast, the speed does not change too much.
what is wrong?
Is there a different approache?
if (bandera == true)
{
X1 = skeleton.Joints[JointType.FootRight].Position.X;
Y1 = skeleton.Joints[JointType.FootRight].Position.Y;
Z1 = skeleton.Joints[JointType.FootRight].Position.Z;
}
if (bandera == false)
{
X2 = skeleton.Joints[JointType.FootRight].Position.X;
Y2 = skeleton.Joints[JointType.FootRight].Position.Y;
Z2 = skeleton.Joints[JointType.FootRight].Position.Z;
}
bandera = !bandera;
float d= (((X1 - X2) * (X1 - X2)) + ((Y1 - Y2) * (Y1 - Y2)) + ((Z1 - Z2 * (Z1 - Z2))));
double distance = System.Math.Sqrt(d);
double speed= 30 * distance;
Console.WriteLine(speed);
As you know: Speed= distance/time
And I understand there are 30 FPS per second, so time = 1/30
So speed equals distance divided by (1/30) is equal 30 * distance
Well ... because you're using the same points ... so when you're subtracting, it's all 0's ...
X1 = skeleton.Joints[JointType.FootRight].Position.X;
...
X2 = skeleton.Joints[JointType.FootRight].Position.X;
...
same point, so x1=x2, x1-x2=0, and everything goes pear shape from there...
As per your update:
I hope that is in a loop, otherwise, your flag (bandera) won't do much. I hope you also realize that the first time through the loop, some of your X and Y won't be initialized (because it'll only go through one of the cases).
now, d=t*s, so your distance calculation is completely correct.
If you chuck some debug output after initializing your variables, and calculations, what do you get ?
You messed up the distance formula. Take great care when placing parentesis. It should look like so
float d = (X1 - X2) * (X1 - X2) + (Y1 - Y2) * (Y1 - Y2) + (Z1 - Z2) * (Z1 - Z2);
Also there is no need to compare a bool value to true. You can also move out the distance calculation to its own method to make things clearer and avoid misstakes that take time to find. I suggest something like this:
double distance(Joint a, Joint b)
{
float d2 = (b.X - a.X) * (b.X - a.X) + (b.Y - a.Y) * (b.Y - a.Y) + (b.Z - a.Z) * (b.Z - a.Z);
return System.Math.Sqrt(d2)
}
...
if (bandera)
{
a = skeleton.Joints[JointType.FootRight];
}
else
{
b = skeleton.Joints[JointType.FootRight];
}
bandera = !bandera;
double speed = 30 * distance(a, b);
Console.WriteLine(speed);
Your speed calculation is very prone to fluctuation, so averaging over say 10 frames would probably also help.
The only mistake I see in your code is that you're missing parenthesis on the distance calculation: (Z1 - Z2) must go between parenthesis, or else the order of the operations is incorrect. Everything else should work fine. As vidstige stated, the speed will vary when the kick is in progress, so for your stated purpose I would recommend simply keeping the greatest value.
Saludos!!

C# Drawing Arc with 3 Points

I need to draw an arc using GraphicsPath and having initial, median and final points. The arc has to pass on them.
I tried .DrawCurve and .DrawBezier but the result isn't exactly an arc.
What can I do?
SOLUTION:
After a couple of hours of code writing I managed to draw what I wanted with this algorithm (give 3 Point a,b,c and a GraphicsPath path):
double d = 2 * (a.X - c.X) * (c.Y - b.Y) + 2 * (b.X - c.X) * (a.Y - c.Y);
double m1 = (Math.Pow(a.X, 2) - Math.Pow(c.X, 2) + Math.Pow(a.Y, 2) - Math.Pow(c.Y, 2));
double m2 = (Math.Pow(c.X, 2) - Math.Pow(b.X, 2) + Math.Pow(c.Y, 2) - Math.Pow(b.Y, 2));
double nx = m1 * (c.Y - b.Y) + m2 * (c.Y - a.Y);
double ny = m1 * (b.X - c.X) + m2 * (a.X - c.X);
double cx = nx / d;
double cy = ny / d;
double dx = cx - a.X;
double dy = cy - a.Y;
double distance = Math.Sqrt(dx * dx + dy * dy);
Vector va = new Vector(a.X - cx, a.Y - cy);
Vector vb = new Vector(b.X - cx, b.Y - cy);
Vector vc = new Vector(c.X - cx, c.Y - cy);
Vector xaxis = new Vector(1, 0);
float startAngle = (float)Vector.AngleBetween(xaxis, va);
float sweepAngle = (float)(Vector.AngleBetween(va, vb) + Vector.AngleBetween(vb, vc));
path.AddArc(
(float)(cx - distance), (float)(cy - distance),
(float)(distance * 2), (float)(distance * 2),
startAngle, sweepAngle);
I would use DrawArc() as suggested by ANC_Michael. To find an arc that passes through 3 points you want to calculate the circumcircle of the triangle formed by the points.
Once you have the circumcircle calculate a bounding box for the circle to use with DrawArc using the min/max dimensions (center +/- radius). Now calculate your start and stop angles by translating the points so that the circumcircle is centered on the origin (translate by -circumcenter) and take the dot-product of the normalized start and end vectors with the X-axis:
double startAngle = Math.Acos(VectorToLeftPoint.Dot(XAxis));
double stopAngle = Math.Acos(VectorToRightPoint.Dot(XAxis));
Note that DrawArc expects angles clockwise from the X-axis so you should add Math.PI if the calculated vector is above the x-axis. That should be enough information to call DrawArc().
Edit: This method will find a circular arc and not necessarily the 'best fit' arc depending on your expected endpoint behavior.
Have you tried the DrawArc method and seeing if u can manipulate your 3 points somehow?
maybe
Pen blackPen= new Pen(Color.Black, 3);
// Create rectangle to bound ellipse.
Rectangle rect = new Rectangle(initial x, initial y, final x, median y);
// Create start and sweep angles on ellipse.
float startAngle = 0F;
float sweepAngle = 270.0F;
// Draw arc to screen.
e.Graphics.DrawArc(blackPen, rect, startAngle, sweepAngle);
http://msdn.microsoft.com/en-us/library/system.drawing.graphics.drawarc%28VS.71%29.aspx

Shorten a line by a number of pixels

I'm drawing a custom diagram of business objects using .NET GDI+. Among other things, the diagram consists of several lines that are connecting the objects.
In a particular scenario, I need to shorten a line by a specific number of pixels, let's say 10 pixels, i.e. find the point on the line that lies 10 pixels before the end point of the line.
Imagine a circle with radius r = 10 pixels, and a line with start point (x1, y1) and end point (x2, y2). The circle is centered at the end point of the line, as in the following illustration.
How do I calculate the point marked with a red circle, i.e. the intersection between circle and line? This would give me the new end point of the line, shortening it by 10 pixels.
Solution
Thank you for your answers from which I was able to put together the following procedure. I named it LengthenLine, since I find it more natural to pass a negative number of pixels if I want the line shortened.
Specifically, I was trying to put together a function that could draw a line with rounded corners, which can be found here.
public void LengthenLine(PointF startPoint, ref PointF endPoint, float pixelCount)
{
if (startPoint.Equals(endPoint))
return; // not a line
double dx = endPoint.X - startPoint.X;
double dy = endPoint.Y - startPoint.Y;
if (dx == 0)
{
// vertical line:
if (endPoint.Y < startPoint.Y)
endPoint.Y -= pixelCount;
else
endPoint.Y += pixelCount;
}
else if (dy == 0)
{
// horizontal line:
if (endPoint.X < startPoint.X)
endPoint.X -= pixelCount;
else
endPoint.X += pixelCount;
}
else
{
// non-horizontal, non-vertical line:
double length = Math.Sqrt(dx * dx + dy * dy);
double scale = (length + pixelCount) / length;
dx *= scale;
dy *= scale;
endPoint.X = startPoint.X + Convert.ToSingle(dx);
endPoint.Y = startPoint.Y + Convert.ToSingle(dy);
}
}
Find the direction vector, i.e. let the position vectors be (using floats) B = (x2, y2) and A = (x1, y1), then AB = B - A. Normalize that vector by dividing by its length ( Math.Sqrt(xx + yy) ). Then multiply the direction vector AB by the original length minus the circle's radius, and add back to the lines starting position:
double dx = x2 - x1;
double dy = y2 - y1;
double length = Math.Sqrt(dx * dx + dy * dy);
if (length > 0)
{
dx /= length;
dy /= length;
}
dx *= length - radius;
dy *= length - radius;
int x3 = (int)(x1 + dx);
int y3 = (int)(y1 + dy);
Edit: Fixed the code, aaand fixed the initial explanation (thought you wanted the line to go out from the circle's center to its perimeter :P)
I'm not sure why you even had to introduce the circle. For a line stretching from (x2,y2) to (x1,y1), you can calculate any point on that line as:
(x2+p*(x1-x2),y2+p*(y1-y2))
where p is the percentage along the line you wish to go.
To calculate the percentage, you just need:
p = r/L
So in your case, (x3,y3) can be calculated as:
(x2+(10/L)*(x1-x2),y2+(10/L)*(y1-y2))
For example, if you have the two points (x2=1,y2=5) and (x1=-6,y1=22), they have a length of sqrt(72 + 172 or 18.38477631 and 10 divided by that is 0.543928293. Putting all those figures into the equation above:
(x2 + (10/l) * (x1-x2) , y2 + (10/l) * (y1-y2))
= (1 + 0.543928293 * (-6- 1) , 5 + 0.543928293 * (22- 5))
= (1 + 0.543928293 * -7 , 5 + 0.543928293 * 17 )
= (x3=-2.807498053,y3=14.24678098)
The distance between (x3,y3) and (x1,y1) is sqrt(3.1925019472 + 7.7532190152) or 8.384776311, a difference of 10 to within one part in a thousand million, and that's only because of rounding errors on my calculator.
You can use similar triangles. For the main triangle, d is the hypotenuses and the extension of r is the vertical line that meets the right angle. Inside the circle you will have a smaller triangle with a hypotenuses of length r.
r/d = (x2-a0)/(x2-x1) = (y2-b0)/(y2-y1)
a0 = x2 + (x2-x1)r/d
b0 = y2 + (y2-y1)r/d

Categories

Resources