My trigonometry always returns 90 degrees, despite my formula seeming correct, and my variables working out correctly - actionscript-2

I am writing a simple flash game which requires an angle to be calculated between one object and another. I have checked and my variables seem to be correct, and I know my law of cosines formula is correct. However, it always returns 90 degrees. Maybe you guys can see what is wrong with it. BTW, I used the mouse instead of an object as a reference point.
onClipEvent (enterFrame) {
var xdiff:Number = Math.abs(_root._xmouse - this._x);
var ydiff:Number = Math.abs(_root._ymouse - this._y);
var xd2:Number = xdiff * xdiff;
var yd2:Number = ydiff * ydiff;
var hypot:Number = Math.sqrt(xd2+yd2);
var angle:Number = Math.acos((xd2 + yd2 - hypot * hypot) / (2*xdiff * ydiff))*180/Math.PI ;
trace("xdiff:"+xdiff);
trace("ydiff:"+ydiff);
trace("xd2:"+xd2);
trace("yd2:"+yd2);
trace("hypot:"+hypot);
trace(angle);
}

Isn't
xd2 + yd2 - hypot * hypot
going to be always zero, hence acos(0) is 90.
Cos is Adjacent/Hypotenuse so, assuming you're calculating the gradient of the line between the two objects,
acos(xdiff/hypot)
then convert from radians with your 180/pi.

What does it mean to you an angle between two objects?
An angle can be calculated between two lines. You are calculating the angle between the x side and the y side of a right triangle and that is always 90 degrees.

Related

Comparing a saved movement with other movement with Kinect

I need to develop an application where a user (physiotherapist) will perform a movement in front of the Kinect, I'll write the data movement in the database and then the patient will try to imitate this motion. The system will calculate the similarity between the movement recorded and executed.
My first idea is, during recording (each 5 second, by example), to store the position (x, y, z) of the points and then compare them in the execution time(by patient).
I know that this approach is too simple, because I imagine that in people of different sizes the skeleton is recognized differently, so the comparison is not reliable.
My question is about the best way to compare a saved motion with a movement executed (on the fly).
I have done this, where a doctors frame is projected onto the patients frame, but with the whole skeleton this doesn't work so well because of different bone heights :/. The code can be found here. It is in beta 2 code, the more current version can be found here, although it is not currently working perfectly
As for comparing, do something like this
for (int i = 0; i < patientList.Count; i++)
{
int diff = (int)Math.Abs(patientList[i] - doctorList[i]);
if (diff < 100) //or whatever number you want
{
Debug.WriteLine("Good Job");
}
}
I have abandoned the idea of a whole figure because of the bone heights mentioned by Fixus, so my current program looks some thing like:
EDIT
This is the concept of camparing two movements with kinect and calculate a similarity between the two movements I explain in depth.
Suppose I have the following 2 points, point A (0, 0, 0) and point B (1, 1, 1). Now I want to find the difference from point A to B, so I would subtract all of the X, Y, and Z numbers, so the difference is 1 X 1 Y 1 Z. That is the simple stuff. Now to implement it. The code I have written above, I would implement like this.
//get patient hand coordinates
double patienthandX = Canvas.GetLeft(patienthand);
double patienthandY = Canvas.GetTop(patienthand);
//get doctor hand coordinates
double doctorhandX = Canvas.GetLeft(doctorhand);
double doctorhandY = Canvas.GetTop(doctorhand);
//compare difference for each x and y
//take Absolute value so that it is positive
double diffhandX = Math.Abs(patienthandX - doctorhandX);
double diffhandY = Math.Abs(patienthandY - doctorhandY);
Now here comes another issue. The doctor coordinates are always the same, but what if the patient isn't standing where the doctor coordinates were recorded? Now we implement more simple math. Take this simple example. suppose I want point A(8, 2) to move to point B(4, 12). You multiply the x and y's of A to get to B. So I would multiply the X by .5, and the Y by 6. So for Kinect, I would put a element on the patients hip, then compare this to the doctors hip. Then multiply all of the doctor joints by that number to achieve the doctor joints on top of the patients (more or less). For example
double whatToMultiplyX = (double) doctorhipX / patienthipX;
double whatToMultiplyY = (double) doctorhipY / patienthipY;
This is all pretty simple, but bringing it together is the harder part. So far we, 1) Scale the doctor frames on top of the patient frames, 2) Calculate the difference. 3) Compare the difference throughout the entire rep. and 4) Reset for the next rep. This seems simple but it is not. To calculate the entire difference for the rep, do something like this:
//get patient hand coordinates
double patienthandX = Canvas.GetLeft(patienthand);
double patienthandY = Canvas.GetTop(patienthand);
//get doctor hand coordinates
double doctorhandX = Canvas.GetLeft(doctorhand);
double doctorhandY = Canvas.GetTop(doctorhand);
//compare difference for each x and y
//take Absolute value so that it is positive
double diffhandX = Math.Abs(patienthandX - doctorhandX);
double diffhandY = Math.Abs(patienthandY - doctrorhandY);
//+= so that it keeps adding to it.
totaldiffhandX += diffhandX;
totaldiffhandY += diffhandY;
Now we can compare, and say:
if (totaldiffhandX < 1000 && totaldiffhandY < 1000) //keep numbers pretty high since it is an entire rep
{
//reset difference
totaldiffhandX = 0;
totaldiffhandY = 0;
//tell the patient good job
Debug.WriteLine("Good Job");
}
This is pretty easy, but keep in mind you must do this for every single joint's x and y. Otherwise it will not work. Hope this Helps.
First of all remember that people are diffrent. Every person has diffrent height, width, weight, diffrent bones length etc etc
You`re code probably will never work cause of this.
Secondly you need to think more geometrically. Don`t think about points only, think with vectors, their directions. Each movement is movent of some vectors in some directions.
Then the proportion. You need to configure application for each user.
You have some pattern. The patter is your physiotherapist. You need to remember not only his movements but also his body. Arm length, leg length, distances etc. Each user that will be using your app also need to me mesured. Having all this data you can compare movement by scaling sizes and comparing directions of movent
Of course remember that there are some very simple moves like for example. They can be recognized by simple mathematic by checking actual position of the hand and checking direction of the movement. You need for this 3 control points and you`re at home :)
Gesture recognizing isn`t a simple thing

Getting any point along an NSBezier path

For a program I'm writing, I need to be able to trace a virtual line (that is not straight) that an object must travel along. I was thinking to use NSBezierPath to draw the line, but I cannot find a way to get any point along the line, which I must do so I can move the object along it.
Can anyone suggest a way to find a point along an NSBezierPath? If thats not possible, can anyone suggest a method to do the above?
EDIT: The below code is still accurate, but there are much faster ways to calculate it. See Introduction to Fast Bezier and Even Faster Bezier.
There are two ways to approach this. If you just need to move something along the line, use a CAKeyframeAnimation. This is pretty straightforward and you never need to calculate the points.
If on the other hand you actually need to know the point for some reason, you have to calculate the Bézier yourself. For an example, you can pull the sample code for Chapter 18 from iOS 5 Programming Pushing the Limits. (It is written for iOS, but it applies equally to Mac.) Look in CurvyTextView.m.
Given control points P0_ through P3_, and an offset between 0 and 1 (see below), pointForOffset: will give you the point along the path:
static double Bezier(double t, double P0, double P1, double P2,
double P3) {
return
pow(1-t, 3) * P0
+ 3 * pow(1-t, 2) * t * P1
+ 3 * (1-t) * pow(t, 2) * P2
+ pow(t, 3) * P3;
}
- (CGPoint)pointForOffset:(double)t {
double x = Bezier(t, P0_.x, P1_.x, P2_.x, P3_.x);
double y = Bezier(t, P0_.y, P1_.y, P2_.y, P3_.y);
return CGPointMake(x, y);
}
NOTE: This code violates one of my cardinal rules of always using accessors rather than accessing ivars directly. It's because in it's called many thousands of times, and eliminating the method call has a significant performance impact.
"Offset" is not a trivial thing to work out. It does not proceed linearly along the curve. If you need evenly spaced points along the curve, you'll need to calculate the correct offset for each point. This is done with this routine:
// Simplistic routine to find the offset along Bezier that is
// aDistance away from aPoint. anOffset is the offset used to
// generate aPoint, and saves us the trouble of recalculating it
// This routine just walks forward until it finds a point at least
// aDistance away. Good optimizations here would reduce the number
// of guesses, but this is tricky since if we go too far out, the
// curve might loop back on leading to incorrect results. Tuning
// kStep is good start.
- (double)offsetAtDistance:(double)aDistance
fromPoint:(CGPoint)aPoint
offset:(double)anOffset {
const double kStep = 0.001; // 0.0001 - 0.001 work well
double newDistance = 0;
double newOffset = anOffset + kStep;
while (newDistance <= aDistance && newOffset < 1.0) {
newOffset += kStep;
newDistance = Distance(aPoint,
[self pointForOffset:newOffset]);
}
return newOffset;
}
I leave Distance() as an exercise for the reader, but it's in the example code of course.
The referenced code also provides BezierPrime() and angleForOffset: if you need those. Chapter 18 of iOS:PTL covers this in more detail as part of a discussion on how to draw text along an arbitrary path.

Tan() Returns Wrong Value

I am trying to calculate angle between three points and I need to use the Tangent function tan(). The weird thing is that VBA return wrong value.
for example:
tan(209) = 0.554309051
but in VBA:
tan(209) = -.696695985548265
My friend told me about something called "Normalize". but I didn't understand what he's talking about and how to do it. Why is this happening?
According to this VBA uses radians.
Convert degrees into radians , ( degrees * 2 * pi) / 360
tan((209 * 2 * 3.14)/360)
(not addressing if using TAN is correct or not):
Perhaps your cell is formated in some special way and it's changing the value.
In Excel 2007, both the worksheet funcion and VBA return -11.8641847236695 for tan(209). That's different from what you have above.
In addition to confusing radians and degrees, I think you may be confusing tangent and arctangent.
In a comment, you say you call Tan like this: Math.Tan((A(2) - B(2)) / (B(1) - A(1))). That is a very atypical way to be supplying an angle argument to a tangent! And in another comment, you imply that you expect this to give you an angle (EDIT: or "radians"). But tangent won't give you an angle or "radians"!
I can't believe nobody else is pointing this out. This physicist is outraged.
Based on this, I believe that what you really want is arctangent, i.e. Math.Atn((A(2) - B(2)) / (B(1) - A(1))). That will give you an angle (in radians) when supplied the length ratio of the opposite to adjacent sides.
Of course, the above is largely speculative, because I don't know what you really are trying to accomplish, but from what I can tease out of the bits of implicit information sprinkled across your question and comments, that is indeed what I would put my money on.
It appears VB is like Excel where is assumes the input value for Tangent is in radians. If it is not in radians, you need to convert angles in degrees to radians.
In Excel, you have to use the RADIANS() function to convert your data from angles to radians. Use the DEGREES() function to convert back.

Obj-C Calculator - Radians to Degrees

I've been watching the Stanford CS193P lectures and I've been working on the assignment calculator. I got through the first assignment fairly easily, and I'm trying to do the extra credit for the assignment now. I'm stuck however at one of the extra credit questions:
Implement a user-interface for choosing whether the operand to sin() or cos() is
considered radians or degrees. When you call sin(x) in the C library, x is assumed
to be in radians (i.e. 0 to 2π goes around the circle once), but users might want to
enter 180 and press the sin button and get 0 instead of -0.8012 (which is the sine of
180 radians). You could use a UIButton for this and switch out the titleLabel’s
text each time the UIButton is pressed, but a better way would be to see if you can
figure out how to use a UISwitch by reading the documentation (if you dare!).
I implemented a UISwitch and hooked it up as an IBOutlet. When I perform an 'operation', I check if the switch is on or off and pass this to my model along with the operation to perform. In my sin and cos cases, I do the following:
else if ([operation isEqual:#"sin"])
{
if (radians) {
operand = sin(operand);
}
else {
operand = sin(operand) * (180 / M_PI);
}
}
// similar for cos
If radians (which is a BOOL: YES = radians, no = degrees), then I perform the operation as usually; if the user puts the switch to 'degrees', I want to return the value in degrees, as the assignment states: users might want to enter 180 and press the sin button and get 0 instead of -0.8012 (which is the sine of 180 radians).
However, this code doesn't fully work. When I put the switch to degrees and do sin 180 it returns a value of more or less -45. Something is wrong there; and I'm not really sure what. I have looked up how to do the conversion and I think I'm doing it right, but apparently not.
I realise this is perhaps better suited for math.stackexchange, but since there was some code I wanted to post I put it here. Can someone provide some advice on the best way to implement this? It's been a while since I even worked with cos or sin, radians and degrees.
Thanks!
Did you perhaps mean operand = sin(operand * M_PI / 180);?
Try this - It works for me:
if ([operation isEqual:#"sin"])
{
operand = (operand * M_PI/180); // first convert the operand from radians to degrees then perform the operation
operand = sin(operand);
}
Hope this helps
Where did you define operand?
Are you sure that operand is a CGFloat and not a memory address?
How are you formatting your output? If you are using %e it will output in scientific notation...try doing %f
operand = sin(operand * (M_PI/180));
Did you try
sin( operand ) / 180.0 * M_PI

Uniform distance between points

How could I, having a path defined by several points that are not in a uniform distance from each other, redefine along the same path the same number of points but with a uniform distance. I'm trying to do this in Objective-C with NSArrays of CGPoints but so far I haven't had any luck with this.
Thank you for any help.
EDIT
I was wondering if it would help to reduce the number of points, like when detecting if 3 points are collinear we could remove the middle one, but I'm not sure that would help.
EDIT
Illustrating:
Reds are the original points, blues the post processed points:
The new path defined by the blue dots does not correspond to the original one.
I don't think you can do what you state that you want to do. But that could be a misunderstanding on my part. For example, I have understood from your comment that the path is straight between successive points, not curved.
Take, for example, a simple path of 3 points (0,1,2) and 2 line segments (0-1,1-2) of different lengths. Leave points 0 and 2 where they are and introduce a new point 1' which is equidistant from points 0 and 2. If point 1' is on one of the line segments 0-1, 1-2, then one of the line segments 0-1', 1'-2 is not coincident with 0-1, 1-2. (Easier to draw this, which I suggest you do.) If point 1' is not on either of the original line segments then the entire path is new, apart from its endpoints.
So, what relationship between the new path and the old path do you want ?
EDIT: more of an extended comment really, like my 'answer' but the comment box is too small.
I'm still not clear how you want to define the new path and what relationship it has to the old path. First you wanted to keep the same number of points, but in your edit you say that this is not necessary. You agree that replacing points by new points will shift the path. Do you want, perhaps, a new path from point 0 to point N-1, defined by N points uniformly spaced on a path which minimises the area between the old and new paths when drawn on the Cartesian plane ?
Or, perhaps you could first define a polynomial (or spline or other simple curve) path through the original points, then move the points to and fro along the curve until they are uniformly spaced ?
I think the problem is simple and easily solvable actually :)
The basic idea is:
First check if the distance between your current point (P) and the end point of the line segment you are on is >= the distance between P and the next point (Q).
If it is, great, we use some simple trigonometry to figure it out.
Else, we move to the adjacent line segment (in your ordering) and deduct the distance between P and the endpoint of the line segment you are on and continue the process.
Pseudocode:
Defined previously
struct LineSegment
{
Point start,end;
int ID;
double len; // len = EuclideanDistance(start,end);
LineSegment *next_segment;
double theta; // theta = atan2(slope_of_line_segment);
}
Function [LineSegment nextseg] = FindNextLineSegment(LineSegment lineseg)
Input: LineSegment object of the current line segment
Output: LineSegment object of the adjacent line segment in your ordering.
nextseg.ID = -1 if there are no more segments
Function: Find the next point along your path
Function [Point Q, LineSegment Z] = FindNextPt(Point P, LineSegment lineseg, int dist):
Input: The current point P, the distance between this point and the next, and the LineSegment of the line segment which contains P.
Output: The next point Q, and the line segment it is on
Procedure:
distToEndpt = EuclideanDistance(P,lineseg->end);
if( distToEndpt >= d )
{
Point Q(lineseg->start.x + dist*cos(lineseg.theta), lineseg->start.y + dist*sin(lineseg.theta));
Z = lineseg;
}
else
{
nextseg = lineseg->next_segment;
if( nextseg.ID !=-1 )
{
[Q, Z] = FindNextPt(nextseg->start,nextseg->ID,dist-distToEndpt);
}
else
{
return [P,lineseg];
}
}
return [Q,Z]
Entry point
Function main()
Output: vector of points
Procedure:
vector<LineSegment> line_segments;
// Define it somehow giving it all the properties
// ....
vector<Point> equidistant_points;
const int d = DIST;
[Q Z] = FindNextPoint(line_segments[0].start,line_segments[0],DIST);
while( Z.ID != -1 )
{
equidistant_points.push_back(Q);
[Q Z] = FindNextPt(Q,Z,d);
}
My sense is that this is a very hard problem.
It basically amounts to a constrained optimization problem. The objective function measures how close the new line is from the old one. The constraints enforce that the new points are the same distance apart.
Finding a good objective function is the tricky bit, since it must be differentiable, and we don't know ahead of time on which segments each new point will lie: for instance, it's possible for two new points to lie on an extra-long old segment, and no new points lying on some extra-short old segment. If you somehow know a priori on which segments the new points will lie, you can sum the distances between points and their target segments and use that as your objective function (note that this distance function is nontrivial, since the segments are finite: it is composed of three pieces and its level-sets are "pill-shaped.")
Or you might forget about requiring the new points to lie on old segments, and just look for a new polyline that's "close" to the old one. For instance, you might try to write down an L2-like metric between polylines, and use that as your objective function. I don't expect this metric to be pleasant to write down, or differentiate.
I think a perturbative approach will work for this one.
I assume:
we know how to slide a point along the path and recalculate the distances (pretty trivial), and
the end points must remain fixed (otherwise the whole problem becomes trivial).
just iterate over the remaining (n-2) points: if point k is closer to point (k-1) than to point (k+1), move it a little forward along the path. Likewise if it's closer to point (k+1), move a little back along the path.
It's probably best to start with large step sizes (for speed) then make them smaller (for precision). Even if the points pass each other, I think this approach will sort them back into order.
This will use quite a bit of vector math but is quite simple really.
First you will need to find the total distance of the path. Depending on how the points of the path are stored is how you will do it. Here is a basic example on a 2 Dimensional Path in Pseudo-code.
// This would generally be done with vectors, however I'm not sure
// if you would like to make your own class for them as I do so I will use arrays.
// The collection of points
int Points[4][2] = { {0,0}, {1,2}, {5,4}, {6,5} };
int Points2 = Points;
// goes to 3 because there are 4 points
for(int i=0; i<3; i++) {
x = Points[i+1][0] - Points[i][0];
y = Points[i+1][1] - Points[i][1];
d += sqrt(( x * x ) + ( y * y ));
}
// divide distance by number of points to get uniform distance
dist = d/4;
// now that you have the new distance you must find the points
// on your path that are that far from your current point
// same deal here... goes to 3 because there are 4 points
for(int i=0; i<3; i++) {
// slope
m = ( Points[i+1][1] - Points[i][1] ) / ( Points[i+1][0] - Points[i][0] );
// y intercept
b = -(M * Points[i][0]) + Points[i][1];
// processor heavy which makes this problem difficult
// if some one knows a better way please say something
// check every degree grabbing the points till one matches
// if it doesn't then check next segment.
for(float j=0; j<360; j += 0.1) {
x = dist * sin(j);
y = sqrt((dist * dist) - ( x * x ));
if (y - (M * x + C)) {
// then the point is on the line so set it
Points2[i+1][0] = x;
Points2[i+1][1] = y;
}
}
}
The last step is what makes it unreasonable but this should work for you.
There may be a small math error somewhere I double checked this several times but there could be something I missed. So if anyone notices something please inform me and I will edit it.
Hope this helps,
Gale