iOS Get Subview's Rotation - iphone

I'm using CGAffineTransformMakeRotation to rotate a subview using its transform property. Later, I need to find out how far the subview has been rotated. I realize that I could simply use an int to keep track of this, but is there a simple way to get the current rotation of the subview?

CGAffineTransformMakeRotation is explicitly defined to return a matrix with cos(angle) in the transform's a value, sin(angle) in b, etc (and, given the way the transform works, that's the only thing it really could do).
Hence you can work out the current rotation by doing some simple inverse trigonometry. atan2 is probably the thing to use, because it'll automatically figure out the quadrants appropriately.
So, e.g.
- (float)currentAngleOfView:(UIView *)view
{
CGAffineTransform transform = view.transform;
return atan2f(transform.b, transform.a);
}
Because that'll do an arctangent that'll involve dividing the a and b fields in an appropriate manner, that method will continue to work even if you apply scaling (as long as it's the same to each axis) or translation.
If you want to apply more complex or arbitrary transformations then things get a lot more complicated. You'll want to look how to calculate normal matrices. From memory I think you'd want the adjugate, which is about as much fun to work out as it sounds.

Related

iphone - apply CGAffineTransformRotate on point in hittest

I've got an image that's allowed to be rotated and scaled by the user.
Every time the user clicks the image I try to figure out if the point is transparent or not.
If it's transparent I return null in my view's HitTest, if it's not transparent I return the view. Problems start when user rotates the image. In my hitTest method, I need to transform the point according to the current view's rotation. Otherwise the point will indicate an irrelevant location on the view (and the image).
How do I do that?
Thank you very much.
This CGAffineTransform Reference might help:
CGPointApplyAffineTransform
CGRectApplyAffineTransform
and
CGSizeApplyAffineTransform
But before you start thinking that you need to perform the mapping by hand, I would suggest to give it a try 'as if' the current transformation was CGAffineIdentity, and code your coordinate detection accordingly. You might be surprised by the results ...
My own experience says that it looks like when you get your points from UITouch locationIn_SomeView_ the inverted matrix of SomeView is applied to the point before it is handed back to you.
So, you probably don't need any of the CGxxxApplyAffineTransform unless you generate the points yourself, outside of the events system.

How to restore saved transform when flipped

I persist a transform and a frame of an object. When the app reloads I simply need to restore the frame and the transform on a new view. You would think that you could simply set the frame and transform to the saved values, but you can't. Doing so creates undesired results.
http://iphonedevelopment.blogspot.com/2008/10/demystifying-cgaffinetransform.html
Says : "When you apply success transformations, the order matter. Rotating and then translating will give you a different result then translating and then rotating. This can bite you if you're not careful."
I only have transform problems when I save an transform that has been FLIPPED & ROTATED. My save values are identical to the values that are applied to the restored view.
Actual Results:
When doing :
myNewView.frame = savedFrame
myNewView.transform = savedTransform
My view will shift down and to the right (out of place).
Can someone help me restore my transform in the proper order of operations?
Again, I have confirmed that my saved and loaded transform data is perfect, so I know its an order of operations problem, im just not smart enough to figure out what to do next :P
To undo the saved transformation, you need the inverse transformation. See the heading Stepping Back in the page you referenced. Try:
myNewView.transform = CGAffineTransformInvert(savedTransform);
An arbitrary matrix is not always invertible, but you are usually safe with an affine transformation. Here are some special cases to watch out for.
In general, matrix inversion may lead to round-off errors, so if you need to do this repeatedly, you may need to use a different approach.
EDIT:
Having re-read the question, I suspect that the origin of the transformation may have changed. When a rotation is involved, rotating about a different origin will appear to as an unexpected translation. From the docs, the origin is either the view's center, or the layer's anchor point. I suggest checking these properties when you save the transform and see if they differ when you restore.

Rotating an object in OpenGL ES for iPhone [translate to origin --> rotate --> translate back is not working]

I recently started working with OpenGL ES for the iPhone, and I am having a bit of trouble with it. I want to be able to rotate an object with your fingers. My problem is that I have my object placed at (0, 0, -3), and I would like to rotate it about its center. I know that I need to translate back to the origin, rotate, and then bring it back to the original place. I think I am facing a problem because I am using a matrix to keep track (?) of all of my rotations/translation/scaling etc, and I think it may be combining the operations in a way that order is not even considered (so the two translations would cancel each other). I just started learning OpenGL a day ago and am a complete newbie, so my assumption may be wrong.
Here is the part of the in drawView that I am having trouble with:
GLfloat matrix[16];
glGetFloatv(GL_MODELVIEW_MATRIX, matrix);
glLoadIdentity();
glTranslatef(0, 0, 3); // bring to origin
glRotatef(self.angle, self.dy, self.dx, 0); // rotate
glTranslatef(0, 0, -3); // put it back in place
glMultMatrixf(matrix); // save the transformations performed
Help would be much appreciated, thank you!
Retrieving the modelview matrix, and then multiplying it back on after your rotation seems fishy, but it depends on what your other transforms are and what coordinate space things are supposed to be. Your comment on the glMultMatrix line doesn't correspond with what you're doing.
Normally you would just do the translate+rotate+translate as the most local actions on the object, just before you render it. Also note that this would only apply if you're object is at (0, 0, -3) in object-space. If it's at that location in world space, then the rotation will already rotate the object around its own center if you have previously made a series of transform calls (translate, rotate, etc) to move the object to its intended position in the world.
Transform order is one of the tricky parts of learning OpenGL. As a general rule of thumb, your operations start with the outer-most and progress to the inner-most. So a typical simple set of transforms would be: the inverse of the camera transform to move the world to match up with the camera, then the object's translation to move it to its world-space position, then the rotation to set it's intended orientation. The PushMatrix/PopMatrix stack functions let you save and undo part of that series of transforms so that groups of objects can share portions of that chain.

rotating circle with swipe question

Okay so I'm making something that basically works as a knob, but only half on the screen -- so I just need horizontal swipes to cause it to rotate. I have it nearly all sorted with one exception: if you change direction of your swipe in mid-swipe, the rotation doesn't change direction. I even can see the problem, but not sure what to do about a solution.
So in my touchesMoved, I get the swipe into radians in the predictable way:
CGFloat radians = atan2f(location.y - centerY, location.x - centerX);
I then store radians, add/subtract it to previous rotation and then give the result to the CATransform3D.
So the prob is that even though the swipe changes direction, there is a "balance" which doesn't allow an immediate change of direction.
Does this make any sense?
First, I hope you get that atan() returns values in the range -π/2..+π/2, so you'll never get values in the other half of the circle. (If you deduce that the answer is on that side, you have to do your own flipping/mirroring.)
In your touches-moved, NSLog() what you've calculated the new difference to be, and be sure that it's the value you're expecting.
Of course, you keep the previous radians on touchesBegan, and save-off the current radians on init, touchesEnded and touchesCancelled, right?
(In many cases, touchesCancelled can just fwd the arguments to touchesEnded.)
If you do all of that, then the scheme you describe ought to work. If it does not, perhaps provide more information, like the NSLog() output for the various values when you think those values should be different.
For interactions like this, what I find usually works best is to save the state at the start of the interaction and the touch down location. Then, whenever you update, compute the new state based on the distance between the original touch and the current touch (applied as a difference from the original state).
For example, in the case you mention, what I would do on a touch down would be to store the current angle of the knob and the location of the touch. Then, on a touch move, I would compute the difference between the current touch location and the starting location. This will give you a delta value, which you can translate into an angle which you can add to the starting angle - update your knob graphic accordingly. This ought to allow you to change direction however you like throughout your behavior without worrying about keeping up with incremental changes to your angle. This also makes it extremely easy to handle things like resistance or dampening after a certain angle - far easier than it would be to handle that when updating things incrementally.
Be careful to use atan2 to avoid quadrant issues and division by zero. That's what it's there for.
// angle with +ve x-axis, in the range [0, 2π)
float getDirection(CGPoint V)
{
double theta = atan2(V.x, V.y); // angle with +ve x-axis, in the range (−π, π]
if (theta < 0)
theta = 2 * M_PI - theta;
return theta;
}
+ (CGPoint) vectorFrom: (CGPoint) A to: (CGPoint) B
{ return CGPointMake(B.x - A.x, B.y - A.y); }
i did something similar to this for simulating a turntable scratching motion. Try:
[CATransaction begin];
[CATransaction setValue:[NSNumber numberWithInt:0] forKey:kCATransactionAnimationDuration];
[myLayer setValue:[NSNumber numberWithFloat:radians] forKeyPath:#"transform.rotation.z"];
[CATransaction commit];
on the underlying layer of your view.

iPhone Orientation Expressed as Rotation

Ola Folks,
This might not be the right place for this. Let me know where I should post if I should post it elsewhere.
I want to get the orientation of the device. I am thinking I can use something like this:
float fAngleX = atan2(acceleration.y, acceleration.z);
float fAngleY = atan2(acceleration.x, acceleration.z);
float fAngleZ = atan2(acceleration.y, acceleration.x);
First, is my formula right?
Second, is this going to work for the device?
Third, I'm going back and forth about filtering out gravity. Any thoughts?
Lastly, is there a better way to get the devices orientation expressed as rotation for all three axis?
Thanx
-isdi-
To give the rotation of the device as three numbers like that, is actually ambiguous. This kind of thing can get quite confusing. I think this might be the best place to start: http://en.wikipedia.org/wiki/Euler_angles
As long as you're careful you can use angles, though it would probably be way easier to use vectors directly.
You'll almost certainly want to do filtering; a simple low-pass would be great, but don't filter out gravity, as it is the orientation of the device :).