Get angle and quadrant from three points - objective-c

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);
NSLog(#"%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;
}

Related

Velocity Verlet Algorithm: Cannot seem to determine correct velocity for stable Orbit?

Like many that post about this topic, I too am busy trying to write myself an accurate simulator for the movement of objects in a 2D gravitation field.
I decided early on that I would settle on Velocity Verlet Integration, as I want my objects to maintain stable orbits and conserve energy even if the timestep is rather large. So, what might the problem be?
Well, so far, everything seems to behave correctly, except for one component. When I try to calculate the correct velocity for a stable orbit at a certain distance, the resulting velocity sends them into odd elliptical orbits that quickly increase in magnitude each time.
So, to begin, here are the following methods that determine an objects next position, velocity, and acceleration in scene: (Objective C)
Acceleration:
-(CGVector)determineAccelerationFor:(SKObject *)object
{ // Ok, let's find Acceleration!
CGVector forceVector = (CGVector){0,0}; // Blank vector that we will add forces to
for (SKObject *i in self.sceneObjects)
{
if (![i isEqual:object]) // Just make sure we're not counting ourselves here
{
CGPoint distance = [self getDistanceBetween:i.position And:object.position];
float hypotenuse = sqrtf(powf(distance.x, 2)+ powf(distance.y, 2));
float force = ((self.gravity * object.mass * i.mass)/powf(hypotenuse, 3));
float xMagnitude = (force * distance.x);
float yMagnitude = (force * distance.y);
forceVector.dx += xMagnitude;
forceVector.dy += yMagnitude;
}
}
CGVector acceleration = (CGVector){forceVector.dx/object.mass, forceVector.dy/object.mass};
return acceleration;
}
Cool, so basically, I just take an object, add all the other forces that each other object imposes on it together then divide the X & Y factor by the mass of the current object to get the acceleration!
Next up is Velocity. Here I use the following equation:
The method for it is pretty straightforward too:
-(CGVector)determineVelocityWithCurrentVelocity:(CGVector)v OldAcceleration:(CGVector)ao NewAcceleration:(CGVector)a
{
float xVelocity = (v.dx + ((ao.dx + a.dx)/2) * self.timeStep);
float yVelocity = (v.dy + ((ao.dy + a.dy)/2) * self.timeStep);
CGVector velocity = (CGVector){xVelocity,yVelocity};
return velocity;
}
And finally, position! The equation for this is:
And it is determined with the following method!
-(CGPoint)determinePositionWithCurrentPosition:(CGPoint)x CurrentVelocity:(CGVector)v OldAcceleration:(CGVector)ao
{
float xPosition = (x.x + v.dx * self.timeStep + ((ao.dx * powf(self.timeStep, 2))/2));
float yPosition = (x.y + v.dy * self.timeStep + ((ao.dy * powf(self.timeStep, 2))/2));
CGPoint position = (CGPoint){xPosition,yPosition};
return position;
}
This is all called from the below method!!
-(void)refreshPhysics:(SKObject *)object
{
CGPoint position = [self determinePositionWithCurrentPosition:object.position CurrentVelocity:object.velocity OldAcceleration:object.acceleration]; // Determine new Position
SKAction *moveTo = [SKAction moveTo:position duration:0.0];
[object runAction:moveTo]; // Move to new position
CGVector acceleration = [self determineAccelerationFor:object]; // Determine acceleration off new position
CGVector velocity = [self determineVelocityWithCurrentVelocity:object.velocity OldAcceleration:object.acceleration NewAcceleration:acceleration];
NSLog(#"%# Old Velocity: %f, %f",object.name,object.velocity.dx,object.velocity.dy);
NSLog(#"%# New Velocity: %f, %f\n\n",object.name,velocity.dx,velocity.dy);
[object setAcceleration:acceleration];
[object setVelocity:velocity];
}
Okay, so those methods above dictate how objects are moved in scene. Now onto the initial issue, the ever present problem of achieving a stable orbit!
In order to determine what velocity an object should have to maintain an orbit, I use the following equation:
And I implement that as follows:
-(void)setObject:(SKObject *)object ToOrbit:(SKObject *)parent
{
float defaultSeparation = 200;
// Move Object to Position at right of parent
CGPoint defaultOrbitPosition = (CGPoint){parent.position.x + (parent.size.width/2)+ defaultSeparation,parent.position.y};
[object setPosition:defaultOrbitPosition];
// Determine Orbital Velocity
float velocity = sqrtf((self.gravity * parent.mass)/(parent.size.width/2+defaultSeparation));
CGVector vector = (CGVector){0,velocity};
[object setVelocity:vector];
}
And for some reason, despite this, I get abysmal results. Here is some of the output:
Information:
Gravity(constant) = 1000 (For test purposes)
Mass(Parent) = 5000 units
Mass(Satellite) = 1 units
Separation = 224 pixels
It determines that in order for the Satellite to Orbit the Parent, a velocity of:
149.403580 pixels/timeStep
is required. And that checks out on my calculator.
So this has left me a little confused as to what could be going wrong. I log all the output concerning new velocities and positions, and it does use the velocity I set it to, but that just doesn't seem to make a difference. If anyone could possible help spot what's going wrong here I would be immensely grateful.
If anyone believes I have left something out, tell me and I will edit this right away. Thanks!

iBeacon Trilateration - Vector Math

I'm having a bit of trouble with Trilateration using iBeacon's. I found a formula using vector math in this thread, and I implemented it in my project. However it seems to be giving me odd numbers.
It seems like it works most of the time, however I've noticed that sometimes it will give me extremely similar answers when using distances that are vastly different, which seems odd to me.
CGPoint pointA = [[curBlufiPoints objectAtIndex:0] CGPointValue];
CGPoint pointB = [[curBlufiPoints objectAtIndex:1] CGPointValue];
CGPoint pointC = [[curBlufiPoints objectAtIndex:2] CGPointValue];
/*Point A = (40, 612)
Point B = (379, 788)
Point C = (0, 352)*/
float distanceA = [[distances objectAtIndex:0] floatValue];
float distanceB = [[distances objectAtIndex:1] floatValue];
float distanceC = [[distances objectAtIndex:2] floatValue];
/*distanceA = 13.535637
distanceB = 46.931522
distanceC = 51.585461
----OR----
distanceA = 349.9057;
distanceB = 352.84134;
distanceC = 353.37515;*/
CGPoint P2MinusP1 = CGPointMake(pointB.x - pointA.x, pointB.y - pointA.y);
CGPoint P3MinusP1 = CGPointMake(pointC.x - pointA.x, pointC.y - pointA.y);
CGFloat magP2MinusP1 = sqrt(pow((P2MinusP1.x), 2) + pow((P2MinusP1.y), 2));
CGPoint eX = CGPointMake(P2MinusP1.x / magP2MinusP1, P2MinusP1.y / magP2MinusP1);
CGFloat i = eX.x * P3MinusP1.x + eX.y * P3MinusP1.y;
CGPoint eXTimesI = CGPointMake(eX.x * i, eX.y * i);
CGPoint P3MinusP1MinusITimesEX = CGPointMake(P3MinusP1.x - eXTimesI.x, P3MinusP1.y - eXTimesI.y);
CGFloat magP3MinusP1MinusITimesEX = sqrt(pow(P3MinusP1MinusITimesEX.x, 2) + pow(P3MinusP1MinusITimesEX.y, 2));
CGPoint eY = CGPointMake(P3MinusP1MinusITimesEX.x / magP3MinusP1MinusITimesEX, P3MinusP1MinusITimesEX.y / magP3MinusP1MinusITimesEX);
CGFloat j = eY.x * P3MinusP1.x + eY.y * P3MinusP1.y;
CGFloat x = (pow(distanceA, 2) - pow(distanceB, 2) + pow(magP2MinusP1, 2)) / (2 * magP2MinusP1);
CGFloat y = (pow(distanceA, 2) - pow(distanceC, 2) + pow(i, 2) + pow(j, 2)) / (2 * j) - (i * x) / j;
CGPoint finalPoint = CGPointMake(pointA.x + x * eX.x + y * eY.x, pointA.y + x * eX.y + y * eY.y);
NSLog(#"%f %f %f %f", finalPoint.x, finalPoint.y);
//finalPoint.x = ~343
//finalPoint.y = ~437
As you can see from the values I've commented in the code above, when I am using the same points, but different distances I end up with the same result. It doesn't make sense to me how they can both have around the same answer. If the point (343, 437), (the answer I get for both sets of input) is 13.5 units away from point (40, 612), how can the same point also be 349.9 units away?
I'm not sure where my math has gone wrong but I think that something about calculating x and y is where the problems comes in. I've discovered that the lines "pow(distanceA, 2) - pow(distanceB, 2)" and "pow(distanceA, 2) - pow(distanceC, 2)" give me approximately the same answer for both sets of numbers, which is why x and y end up being the same regardless of which set of numbers I use.
I don't think that this should be the case, any help would be greatly appreciated, thank you!
I tried your code with a sample (and bad looking code test).
The testing code is here.
To debug, I used the "3D" new feature of XCode 6, to see the points and circle that were out of the screen bounds, because I didn't want to do translation (recentering the plane, etc.)
The main issue (with your first sample test (#[#(13.535637), #(46.931522), #(51.585461)];) is that the circles aren't overlapping. There is NO intersection. So you can't guess the position.
If you draw a circle with center pointA, and radius distanceA, and do it for point B and C, you'll need to find the intersection to geolocalize, which is clearly illustrated on the wiki-related article on the linked question.
I added a "check" method to see if they overlap correctly, with a "allowed mistake", because we're doing some maths with float and there could be a few rounding issue.
The check method consist on recalculating the distance between the finalPoint (coordinate found) and each point. The question is now if they differs (a lot?) from the original ones.
Screenshot with first set of distances
Screenshot with second set of distances

How To Create a Rotating Wheel Control with UIKit

Hi I'm trying to create a Rotation Wheel in iOS and I found this fantastic tutorial
How to Create a Rotation Wheel Control
and it is very nice and complete, but in this case the selected object is in the left and need the object in the right.
So I'm wondering if somebody knows what I need to change in order to select the rigth side
Well in the example we can see in the endtrackingWithTouch Event the following code
// 1 - Get current container rotation in radians
CGFloat radians = atan2f(container.transform.b,container.transform.a);
NSLog(#"Radians %f", radians);
// 2 - Initialize new value
CGFloat newVal = 0.0;
// 3 - Iterate through all the sectors
for (SMSector *s in sectors) {
// 4 - Check for anomaly (occurs with even number of sectors)
if (s.minValue > 0 && s.maxValue < 0) {
if (s.maxValue > radians || s.minValue < radians) {
// 5 - Find the quadrant (positive or negative)
if (radians > 0) {
newVal = radians - M_PI;
} else {
newVal = M_PI + radians;
}
currentSector = s.sector;
}
}
// 6 - All non-anomalous cases
else if (radians > s.minValue && radians < s.maxValue) {
newVal = radians - s.midValue;
currentSector = s.sector;
}
}
Doing the Math for radians and making some comparing the min and max in the sectors we get the selected sector also if I change (CGFloat radians = atan2f(container.transform.b,container.transform.a);) for CGFloat radians = atan2f(container.transform.d,container.transform.c); I'm able to get the sector from the bottom
I think you can simply put your wheel in another view and rotate this view one PI. Something like this:
UIView *testView = [[UIView alloc]initWithFrame:CGRectMake(10, 80,300, 300)];
SMRotaryWheel *wheel = [[SMRotaryWheel alloc] initWithFrame:CGRectMake(0, 0,300, 300)
andDelegate:self
withSections:5];
[testView addSubview:wheel];
testView.transform = CGAffineTransformMakeRotation(M_PI);
[self.view addSubview:testView];

Find point on the perimeter of a rectangle in Objective-C (Sprite Kit)

I want to move an object from a random point just outside of the view in a Sprite Kit game.
The logical way of doing this would be to create a rectangle 100px (example) bigger than the view, and pick a random point on it's perimeter. Unfortunately, I don't know an easy way to do this.
How can I easily create a random point on the perimeter of a rectangle (which is slightly bigger than my view)?
Update
This should do what you want:
- (CGPoint)randomPointOutsideRect:(CGRect)rect withOffset:(CGFloat)offset {
NSUInteger random = arc4random_uniform(4);
UIRectEdge edge = 1 << random; // UIRectEdge enum values are defined with bit shifting
CGPoint randomPoint = CGPointZero;
if (edge == UIRectEdgeTop || edge == UIRectEdgeBottom) {
randomPoint.x = arc4random_uniform(CGRectGetWidth(rect)) + CGRectGetMinX(rect);
if (edge == UIRectEdgeTop) {
randomPoint.y = CGRectGetMinY(rect) - offset;
}
else {
randomPoint.y = CGRectGetMaxY(rect) + offset;
}
}
else if (edge == UIRectEdgeLeft || edge == UIRectEdgeRight) {
randomPoint.y = arc4random_uniform(CGRectGetHeight(rect)) + CGRectGetMinY(rect);
if (edge == UIRectEdgeLeft) {
randomPoint.x = CGRectGetMinX(rect) - offset;
}
else {
randomPoint.x = CGRectGetMaxX(rect) + offset;
}
}
return randomPoint;
}
This should be fairly straightforward, let me know if there's something unclear.
Basically, we pick one edge at random, then "fix" one axis and pick a random value on the other (within the width/height boundaries).
arc4random_uniform gives us only integers, but that's fine because floating point values in frames are bad when displaying stuff on screen.
There is probably a shorter way to write this; feel free to edit to improve, everyone.
Original answer
How can I easily create a point 100 pixels away from the edge of my view?
With CGRectOffset().
Assuming you want a CGPoint 100pt "higher" (ie. with a lower y) than your view, do:
CGRect viewFrame = // lets say for this example that your frame is at {{20, 40}, {300, 600}}
CGRect offsetFrame = CGRectOffset(viewFrame, 0, -100);
CGPoint offsetPoint = offsetFrame.origin
// offsetPoint = {20, -60}

help to calculate atan2 properly

I need to calculate the angle between lines. I need to calculate atan. So I am using such code
static inline CGFloat angleBetweenLinesInRadians2(CGPoint line1Start, CGPoint line1End)
{
CGFloat dx = 0, dy = 0;
dx = line1End.x - line1Start.x;
dy = line1End.y - line1Start.y;
NSLog(#"\ndx = %f\ndy = %f", dx, dy);
CGFloat rads = fabs(atan2(dy, dx));
return rads;
}
But I can't get over 180 degrees(( After 179 deg going 178..160..150 and so on.
I need to rotate on 360 degrees. How can I do it? What's wrong?
maby this helps:
//Tells the receiver when one or more fingers associated with an event move within a view or window.
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
NSArray *Touches = [touches allObjects];
UITouch *first = [Touches objectAtIndex:0];
CGPoint b = [first previousLocationInView:[self imgView]]; //prewious position
CGPoint c = [first locationInView:[self imgView]]; //current position
CGFloat rad1 = angleBetweenLinesInRadians2(center, b); //first angel
CGFloat rad2 = angleBetweenLinesInRadians2(center, c); //second angel
CGFloat radAngle = fabs(rad2 - rad1); //angel between two lines
if (tempCount <= gradus)
{
[imgView setTransform: CGAffineTransformRotate([imgView transform], radAngle)];
tempCount += radAngle;
}
}
atan2 returns results in [-180,180] (or -pi, pi in radians). To get results from 0,360 use:
float radians = atan2(dy, dx);
if (radians < 0) {
radians += M_PI*2.0f;
}
It should be noted that it is typical to express rotations in [-pi,pi] and thusly you can just use the result of atan2 without worrying about the sign.
Remove the fabs call and simply make it:
CGFloat rads = atan2(dy, dx);
Use this function in Swift. This makes sure the angle from "fromPoint" to "toPoint" lands between 0 to <360 (not including 360). Please note, the following function assumes that CGPointZero is at top left corner.
func getAngle(fromPoint: CGPoint, toPoint: CGPoint) -> CGFloat {
let dx: CGFloat = fromPoint.x - toPoint.x
let dy: CGFloat = fromPoint.y - toPoint.y
let twoPi: CGFloat = 2 * CGFloat(M_PI)
let radians: CGFloat = (atan2(dy, -dx) + twoPi) % twoPi
return radians * 360 / twoPi
}
For the case where the origin is at the bottom left corner
let twoPi = 2 * Float(M_PI)
let radians = (atan2(-dy, -dx) + twoPi) % twoPi
let angle = radians * 360 / twoPi
Your problem is that the result of atan2 is between -180 and +180 degrees.
If you want it to be between 0 and 360 then move the result to sure be a positive value, and then do a modulo. For example:
let angle = fmod(atan2(dx,dy) + .pi * 2, .pi * 2)