Get angle and quadrant from three points
Given:
- point clickWheelCenter
- point startPoint (first touch)
- point point (actual touch)
I want to determine the angle between these three point, and I want to know, in which quadrant the last touch was found.
The calculation of the angle is working well, while the quadrant is always 1 or 2, never 3 or 4.Where am I wrong?
CGFloat DistanceBetweenTwoPoints(CGPoint point1,CGPoint point2)
{
CGFloat dx = point2.x - point1.x;
CGFloat dy = point2.y - point1.y;
return sqrt(dx*dx + dy*dy );
};
NSInteger GetQuadrant(double angle)
{
double sinAngle = sin(angle);
double cosAngle = cos(angle);
double tanAngle = tan(angle);
double cotAngle = 1.0/tanAngle;
NSLog(@"%f %f %f %f", sinAngle, cosAngle, tanAngle, cosAngle);
if(sinAngle > 0 && cosAngle > 0 && tanAngle > 0 && cotAngle > 0) return 1;
if(sinAngle > 0 && cosAngle < 0 && tanAngle < 0 && cotAngle < 0) return 2;
if(sinAngle < 0 && cosAngle < 0 && tanAngle > 0 && cotAngle > 0) return 3;
if(sinAngle < 0 && cosAngle > 0 && tanAngle < 0 && cotAngle < 0) return 4;
return 0;
}
double AngleBetweenThreePoints(CGPoint point1,CGPoint point2, CGPoint point3)
{
CGPoint point_a = point1;
CGPoint point_b = point2;
CGPoint point_c = point3;
CGFloat a, b, c;
a = DistanceBetweenTwoPoints(point_b, point_c);
b = DistanceBetweenTwoPoints(point_a, point_c);
c = DistanceBetweenTwoPoints(point_a, point_b);
double result = acos((b*b+c*c-a*a)/(2*b*c));
NSLog(@"%d", GetQuadrant(result));
return result/M_PI * 180.0;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
startPoint = [[touches anyObject] locationInView:self];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
CGPoint point = [[touches anyObject] locationInView:self];
double angle = AngleBetweenThreePoints(self.clickWheelCenter, startPoint, point);
开发者_StackOverflowNSLog(@"%f", angle);
}
This can all be done much easier.
With {x1,y1} = first point - center point, and {x2,y2} = third point - center point, the signed angle (-pi,+pi) is given by:
atan2(x2*y1 - x1*y2,x1*x2 + y1*y2)
Depending on your definition of the sign of the angle you may or may not want to add a minus in front.
You can easily derive the quadrant from sign and absolute size.
- size <= pi/2, sign +
- size > pi/2, sign +
- size > pi/2, sign -
- size <= pi/2, sign -
As others have mentioned, what you are doing gets the inner angle which is always less than 180 degrees. I am assuming you want the quadrant as measured by the counter clockwise angle from one vector to the other.
An easy way to get this quadrant is to do dot product and perp dot product and check the signs of each result.
This works because dot product equals |A|*|B|*cos(theta) where || means magnitude and theta is the angle between A and B. Since magnitude is always positive, by checking the sign of the result you are actually checking sign of cos(theta).
Perp dot product is actually |A|*|B|*sin(theta) so by checking the sign of perp dot you are actually checking the sign of sin(theta).
Lets say we are measuring theta as the counter clockwise angle from A to B. The quadrant is also measured by theta. We can compute the dot and perpdot by the following
First you will need to get the vectors A and B. A and B will be the vectors from clickWheelCenter to startpoint and from clickWheelCenter to what you called 'point'
dot = A.x*B.x + A.y*B.y
perpdot = A.x*B.y - A.y*B.x
then check the signs, noting the below table.
quadrant dot perpdot 1 positive positive 2 negative positive 3 positive negative 4 negative negative
Again, by checking the sign of dot product you are actually checking the sign of cos(theta) and by checking perpdot you are checking the sign of sin(theta). All you need is a set of if statements to compare if these are > 0 or < 0.
(note: I wrote the above quickly and haven't checked everything well for accuracy, but the general idea is correct)
BBitmaster
Because law of cosine is only going to return an interior angle of 180 degrees or less. I'm not quite sure what you are trying to do here, but this approach doesn't make sense in terms of quadrents. Also, you need to error check if this is production level code. Law of Cosines blows up when your triangle in degenerate.
It seems like your function AngleBetweenThreePoints
is only ever returning an angle between 0 and Pi which is then being passed on.
Your problem is that acos is not a one to one function. For any given input there are multiple values so without any further knowledge it will just choose one in a given range. An easy example is that 270degrees and 90degrees both have cos equal to 0. So if you acos(0) then it can clearly only return one of the two.
I'm a little unclear on what you mean by which quadrant it is in though. Usually you can just tell by looking at the sign of the x and y co-ordinates and use that to work out what your quadrant is. Looking at the angle between two lines will give a strange value for quadrant (quadrant relative to the first line) - is this what you want?
You are calculating the inner angle which is always less than 180 degrees. Here is a quick and correct way of calculating the right angle value:
double AngleBetweenThreePoints(CGPoint pointA, CGPoint pointB, CGPoint pointC)
{
CGFloat a = pointB.x - pointA.x;
CGFloat b = pointB.y - pointA.y;
CGFloat c = pointB.x - pointC.x;
CGFloat d = pointB.y - pointC.y;
CGFloat atanA = atan2(a, b);
CGFloat atanB = atan2(c, d);
return atanB - atanA;
}
精彩评论