CGAffineTranformRotate atan2 inaccuration - iphone

I'm making transition of my view and always happens something weird, I mean I calculate angle using this code: angle = atan2f(currentTouchPoint.y - center.y, currentTouchPoint.x - center.x) - atan2f(previousTouchPoint.y - center.y, previousTouchPoint.x - center.x);
And the view rotates but not properly. I mean it rotates in proper direction but the angle is always inaccurate about +/- 0.05 radians. And when I tap again, view rotates into proper position. Any adivices? It's important to me to get angle accurate to 5 places after comma.
Some NSLog to show you the problem:
First rotation first tap and second tap
2012-01-07 01:01:26.283 Wheel[9080:707] Angle: 0.598412
2012-01-07 01:01:29.281 Wheel[9080:707] Angle: -0.070008
Second rotation first tap and second tap
2012-01-07 01:01:31.103 Wheel[9080:707] Angle: -0.679809
2012-01-07 01:01:32.450 Wheel[9080:707] Angle: 0.092595
Third rotation first tap and second tap
2012-01-07 01:01:35.745 Wheel[9080:707] Angle: 0.607844
2012-01-07 01:01:36.945 Wheel[9080:707] Angle: -0.064927
Fourth rotation first tap and second tap
2012-01-07 01:01:41.073 Wheel[9080:707] Angle: -0.635756
2012-01-07 01:01:41.920 Wheel[9080:707] Angle: 0.052361
And I forget to tell you, condition, the difference between points is farther the inaccuration is bigger.
EDIT:
Circle *view = (Circle *) [self view];
for (CircleThumb *thumb in view.subviews) {
CGPoint point = [thumb convertPoint:thumb.centerPoint toView:nil];
CircleThumb *shadow = [[view.overlayView subviews] lastObject];
CGPoint centralPoint = [shadow convertPoint:shadow.centerPoint toView:nil];
CGRect shadowRect = [shadow.superview convertRect:shadow.frame toView:nil];
if (CGRectContainsPoint(shadowRect, point) == YES) {
CGPoint pointInShadowRect = [thumb convertPoint:thumb.centerPoint toView:shadow];
if (CGPathContainsPoint(shadow.arc.CGPath, NULL, pointInShadowRect, NULL)) {
CGAffineTransform current = view.transform;
CGPoint center = view.window.center;
CGPoint currentTouchPoint =centralPoint;
CGPoint previousTouchPoint = point;
long double angle = atan2f(currentTouchPoint.y - center.y, currentTouchPoint.x - center.x) - atan2f(previousTouchPoint.y - center.y, previousTouchPoint.x - center.x);
[UIView animateWithDuration:0.3f animations:^{
[view setTransform:CGAffineTransformRotate(current, angle)];
}];
[view.delegate circle:view didMoveToSegment:thumb.tag thumb:thumb];
NSLog(#"Angle: %Lf ", angle);
break;
}
}
}
This is code which is part of '- touchesEnded: withEvent:' implementation
I'm making control similar to the one in Convert bot. the "wheels" from my app and from convertbot look similar but mine uses custom drawing.
So Circle is a UIView which we rotate. Circle has got subviews - CircleThumbs. thumb represents single segment of the circle. Points are calculated properly but I won't explain why, because there's no need.

An atan calculation is never entirely correct. And you let iOS calculate cos and sin out of the angle again (cgaffinetransformRotate does that). So you're stacking up trigonometric inaccuracies. And since you calculate only the difference wi the previous angle, I imagine that you are also stacking up inaccuracies over the multiple touch events.
If it is to rotate something, then there is no reason to use trigonometry or angles. You can implement this completely using linear algebra. Something like this:
v.x = touch.x - center.x; v.y = touch.y - center.y;
float length = sqrt(v.x*v.x + v.y* v.y);
if (length < 5 ) break;
v.x /= length; v.y /= length;
// the rotation matrix is
// v.x -v.y
// v.y v.x
rot.x = previous.x * v.x + previous.y * v.y;
rot.y = previous.x * v.y - previous.y * v.x;
CGAffineTransform rotate = CGAffineTransformMake( rot.x, rot.y, -rot.y, rot.x, 0,0);
...
[view SetTransform:CGAffineTransformConcat ( current, rotate )];
...
previous.x = v.x;
previous.y = v.y;
This pseudo code calculates a normalized vector for current point (the one for the previous point was also stored). Then we define a matrix that rotates the previous vector into the new vector. That matrix is concatenated with the existing transformation.
It would be better if the rotation wasn't always calculated from the previous state, but rather from an initial state. The trick is to calculate 'previous' only for the touchdown.

Related

determine angle between two points on a circle with respect to center

I have a Ring(width 25px) as UIView. When User selects any where on the ring, I want to Calculate the Angle between the the Points Selected on a fixed point on the circle considering the Center of Circle.
I have found few examples but they are not taking center into consideration.
What is the Optimum way to do this ?
You'll have to handle the code yourself (I'm a Java developer), but the simplest way to get the angle between two points on a circle (measured against its center) is to recall the geometry of the situation. The triangle formed by any two points on the circumference of a circle and the circle's center is necessarily isosceles.
An isosceles triangle, recall, has (at least) two sides of the same length -- the radial segments to your two points. Bisecting the angle results in a radial segment which is perpendicular to and bisects the line connecting the two points. This forms a pair of right triangles, with the radius as the hypotenuse, and half the distance between the two points as the 'opposite' side.
Moving the factor of two to the denominator and recognizing what twice the radius is, simply calculate the distance between the two points (on the circumference), and divide it by the diameter. The value you get is the sine of the half-angle (you desire the whole angle). Take the arcsine, and you'll have half your angle:
θ/2 = sin-1(d/D)
With d as the distance between the two points, and D as the diameter of the circle. Since the diameter is given, and the distance between the two points is simple to calculate, getting to this point should be easy, and then you just need to double the value calculated to get the whole angle between the two points.
This might help you, I have used same thing in one of my projects.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
beginPoint = [[[event allTouches] anyObject] locationInView:self];
currentAngle = 0.0f;
centerPoint = self.center;
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
endPoint = [[[event allTouches] anyObject] locationInView:self];
float fromAngle = atan2(beginPoint.y - self.center.y, beginPoint.x - self.center.x);
float toAngle = atan2(endPoint.y - self.center.y, endPoint.x - self.center.x);
float newAngle = [self wrapd:currentAngle + (toAngle - fromAngle) min:0 max:2 * 3.14];
currentAngle = newAngle;
CGAffineTransform cgaRotate = CGAffineTransformMakeRotation(newAngle);
[self setTransform:cgaRotate];
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
endPoint = [[[event allTouches] anyObject] locationInView:self];
float fromAngle = atan2(beginPoint.y - self.center.y, beginPoint.x - self.center.x);
float toAngle = atan2(endPoint.y - self.center.y, endPoint.x - self.center.x);
float newAngle = [self wrapd:currentAngle + (toAngle - fromAngle) min:0 max:2 * 3.14];
currentAngle = newAngle;
}
- (double) wrapd:(double)_val min:(double)_min max:(double)_max {
if(_val < _min) return _max - (_min - _val);
if(_val > _max) return _val - _max; /*_min - (_max - _val)*/;
return _val;
}
As we know
((center.x + radius*cos(theta)) ,(center.y + radius*sin(theta))) ~ (X,Y)
where (X,Y) belongs to the any circumference point
so you can calculate the Angle i.e. theta for any Circumference point as:
X= center.x + radius*cos(theta)
cos(theta) = (X - center.x)/radius .......... eqn-1
similarly
Y= center.y + radius*sin(theta)
sin(theta) = (Y - center.y)/radius .......... eqn-2
By dividing eqn-2 by eqn-1 we have
tan(theta) = (Y - center.y)/(X - center.x) -----------------Final Equation

Figure out angle with right triangle in objective-c

I'm trying to calculate the angle of the click i am making in relationship to the middle of the screen. But maybe i am confused on how atanf is suppsoed to work.
CGPoint pt = [self convertTouchToNodeSpace:[touches anyObject]];
float adj = pt.x - 512;
float opposite = pt.y - 384;
float combined = opposite / adj;
float tan = atanf(combined);
but when i try to NSLog Tan, i just get some giant number like 0.1253649
thoughts?
The right way to convert vector to angle is through atan2 function:
float angle = atan2f (pt.y - 384, pt.x - 512) * 180 / PI;
PS: Are you using cocos2d engine? It has ccpToAngle(...) function.

How do I keep track of the total number of complete rotations of an image in an iOS app?

I am working on an app that has a rotating image (the user tapps and drags and the image rotates in a circle tracking their finger). What I am trying to keep track of is how many times the user makes a complete circle. An additional "hitch" is that I also need to know if the user is circling clockwise vs counter clockwise.
Here is the Code that is rotating the image... Please feel free to request additional information.
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchPoint = [touch locationInView:self.view];
long double rotationNumber = atan2(touchPoint.y - originY, touchPoint.x - originX);
totalRotationCount ++;
schedulingWheel.transform = CGAffineTransformMakeRotation(rotationNumber);
offset = (rotationNumber * 100)/14;
dateRibbon.center = CGPointMake(offset, 24);
}
Thanks for the help!
My solution isn't elegant and there might be a cleaner solution I'm missing but this is what I did recently. The trick is to keep track of the angle from the last time touchesMoved: is called. Then, add the delta of your current angle and the stored angel value to your total.
The problem is the "boundaries" that atan2 creates needed ugly code to overcome. Say your lastAngle is 359 and you cross the origin so your next angle is 1. The difference will not be 2 but -358, so whenever you cross that boundary your total will be reset to 0.
Here is what I did:
CGFloat angle = atan2f(center.y - point.y, point.x - center.x);
//Translate to Unit circle
if (angle > 0) {
angle = (M_PI - angle) + M_PI;
} else {
angle = fabsf(angle);
}
CGFloat delta = angle - lastAngle;
//Adjust for boundaries
if (fabsf(delta) > (2*M_PI)-fabsf(delta)) {
BOOL greaterThanZero = (delta > 0);
delta = (2*M_PI)-fabsf(delta);
if (greaterThanZero) {
delta = -1 * delta;
}
}
totalAngle += delta;
lastAngle = angle;
The big/ugly conditional under "Adjust for boundaries" basically just looks to see if there is a shorter angle to get to the new point (So, 2 instead of -258) & assumes that if there is it probably means you crossed that origin and adjusts the delta accordingly.
I translated the Atan2 results so that it represents a full unit circle from 0 to 2π. Bonus side affect, it then accounts for clockwise/counter clockwise movement better than the standard -π to π of Atan2.
To find out what is the total number of the rotations simply sum all the rotation angles in either directions. For clockwise the value of the rotation angle will be positive and for the counter clockwise it will be negative. Then divide it by a pi (~3.14) to get your total number of rotations.
long double rotationNumber = atan2(touchPoint.y - originY, touchPoint.x - originX);
long double totalRotationsAngle += rotationNumber;
Then whenever you want to get the number of full rotations:
double numberOfRotations = floor(totalRotationsAngle/M_PI);

Calculate angle for rotation in Pie Chart

I want to rotate the image around its center point.The problem i am facing is i need to get the angle to calculate in touch moved event (i dont want to use multi touch).I am current using the below code
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
NSArray *allTouches = [touches allObjects];
gestureStartPoint = gestureMovedPoint;//i am getting the gestureStartPoint on touch began event
gestureMovedPoint = [[allTouches objectAtIndex:0] locationInView:[self superview]];
NSLog(#"gestureMovedPoint = %#",NSStringFromCGPoint(gestureMovedPoint));
}
CGFloat previousAngle = [self angleBetweenPoints:gestureStartPoint second11:gestureMovedPoint]; // atan2(gestureMovedPoint.y - gestureStartPoint.y, gestureMovedPoint.x - gestureStartPoint.x) * 180 / M_PI;
CGFloat currentAngle =atan2(self.transform.b, self.transform.a);//atan2(gestureMovedPoint.y - gestureStartPoint.y,gestureMovedPoint.x - gestureStartPoint.x) * 180 / M_PI;
CGFloat angleToRotate = currentAngle - previousAngle;
float xpoint = (((atan2((gestureMovedPoint.x - gestureStartPoint.x) , (gestureMovedPoint.y - gestureStartPoint.y)))*180)/M_PI);
CGAffineTransform transform = CGAffineTransformMakeRotation(angleToRotate-100);
self.transform = transform;
Kindly help me find the solution as i am stuck here and need to complete this application very soon as there is a dead line.
Thanks in advance
Glad I remember triginometry
-(void)degreesToRotateObjectWithPosition:(CGPoint)objPos andTouchPoint:(CGPoint)touchPoint{
float dX = touchPoint.x-objPos.x; // distance along X
float dY = touchPoint.y-objPos.y; // distance along Y
float radians = atan2(dY, dX); // tan = opp / adj
//Now we have to convert radians to degrees:
float degrees = radians*M_PI/360;
return degrees;
}
Once you have your nice method, just do this in the touch event method. (I forgot what it's called...)
CGAffineTransform current = view.transform;
[view setTransform:CGAffineTransformRotate(current, [self degreesTorotateObjectWithPosition:view.frame.origin andTouchPoint:[touch locationInView:parentView]]
//Note: parentView = The view that your object to rotate is sitting in.
This is pretty much all the code that you'll need.The math is right, but I'm not sure about the setTransform stuff. I'm at school writing this in a browser. You should be able to figure it out from here.
Good luck,
Aurum Aquila
Have to think at this. But I will prefer rotating the view with two touches. It will be much simpler.
I did struggle a bit with how to get a touch driven rotation, even more so because I want 100% understanding of the code I am using. So I ended up, after many failed attempts, with this:
- (CGFloat) pointToAngleFromCenter: (CGPoint) point {
// transform point to a self.center'ed origin based coordinate system
point.x = point.x - self.center.x ;
// ditto for y, but compensate for y going downwards to y going upwards
point.y = self.center.y - point.y ;
return ::atan2(point.y, point.x) ;
}
If anyone has a better name for this method, I'm all ears.
What it does is that it takes a point in parent view coordinates, remaps it relative to the center of the view (which is in parent view coordinate), and computes the angle between this remapped point and the axis [0X]. To do so, it normalizes y to the normal mathematical coordinates (y goes up when its value increases, not down), hence self.center.y - point.y and not the opposite.
Finally, in touchesMoved:
- (void) touchesMoved: (NSSet *) touches withEvent: (UIEvent *) event {
UITouch * touch = [touches anyObject] ;
CGFloat currA = [self pointToAngleFromCenter:[touch locationInView:self.superview]] ;
CGFloat prevA = [self pointToAngleFromCenter:[touch previousLocationInView:self.superview]] ;
// the current value of the rotation angle
CGFloat tranA = ::atan2(self.transform.b, self.transform.a) ;
// the angle difference between last touch and the current touch
CGFloat diffA = currA - prevA ;
// the new angle resulting from applying the difference
CGFloat angle = tranA - diffA ;
CGAffineTransform t = ::CGAffineTransformMakeRotation(angle) ;
self.transform = t ;
[self setNeedsDisplay] ;
}

Rotate Arrow UIImage in a circle in objC

I'm working on the iPad app, In which I have to rotate a arrow in a circle by touch. But I'm facing problems in it. The problem is in the angle calculation to which the image must be rotate.
You can check it here .
I have to rotate the big red arrow image around the circle. Can anybody help me, How to get the angle of the touched point. Currently I'm using the following code, I found it at somewhere in the net. But it didn't rotate the arrow to the touched place.
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *oneTouch = [touches anyObject];
CGPoint currentPoint = [oneTouch locationInView:imgCompass];
double current_angle = [self wheelAngleFromPoint:currentPoint];
imgNeedle.transform = CGAffineTransformMakeRotation( current_angle );
}
- (double) wheelAngleFromPoint:(CGPoint)location
{
double retAngle;
// subtract center of wheel
location.x -= (self.imgNeedle.bounds.size.width ) / 2.0;
location.y = (self.imgNeedle.bounds.size.height ) / 2.0 - location.y;
// normalize vector
double vector_length = sqrt(location.x*location.x + location.y*location.y);
location.x = location.x/vector_length;
location.y = location.y/vector_length;
retAngle = acos(location.y);
float offset = 0.28;
//if (location.x)
// offset = 0.28;
retAngle += offset;
if (location.x<0)
{
retAngle = -retAngle;
}
return retAngle;
}
Can anyone help me in correct angle calculation.
Thanks
Here's the problem:
location.x -= (self.imgNeedle.bounds.size.width ) / 2.0;
location.y = (self.imgNeedle.bounds.size.height ) / 2.0 - location.y;
The values in location.x and location.y after the calculation should represent a vector from the centre of the imgNeedle (or wheel?) to the touch point. try this instead:
location.x = location.x - self.imgNeedle.center.x;
location.y = self.imgNeedle.center.y - location.y;
Note that since you are setting the imgNeedle.transform property, the frame will be invalid, but the center will be ok :) - the imgNeedle will rotate about the center of the image.
Notice that I explicitly stated location.x - self.imgNeedle.center.x to help you understand what's going on - you need to calculate the difference between the two points (a vector between them). You should understand that in mathematics, (0, 0) being the origin, the x-axis is positive to the right and the y-axis is positive upwards. However, on your device's screen, the co-ordinate system you are using (view co-ordinates) (0, 0) is at the top left with the x-axis going positive to the right but the y-axis going positive downwards (this isn't always true, because you can map your screen co-ordinates a number of ways). That is why the location.y subtraction is the other way around from the location.x subtraction - to account for the screen being "flipped" vertically (in terms of mathematically calculating the angle). It's also good to know that in mathematics, a positive rotation represents a rotation in the anti-clockwise direction, and a flip on one axis can hide this and make it appear that a positive angle means a clockwise rotation (like a double negative making a positive), so when you're playing with rotations and vectors, you should take all of this into account. However, for your purposes, it's quite simple, so instead of trying to wrap your head around all the maths you may just prefer to play with the code using trial and error, you might need to flip one of the directions (change the order of subtraction) or even change the direction of rotation (do 2*M_PI - retAngle for example).