Can 2D and 3D transforms be applied to iOS controls? - iphone

Having never actually developed with iOS before, I am investigating the possibilies with standard iOS controls such as text fields, list etc. and would like to know which transforms can be applied to them.
Do these standard controls support 2D transforms such as scale, translate and rotate?
Do these standard controls also support 3D transforms such as scale, translate and rotate which include a z axis?
If the answer is yes to either questions, what "level" of support exists? For example with a text field, if I transform it in 3D coordinate space can I still enter text into it?

Yes, and a lot. UITextField for example inherits from UIControl with inherits from UIView. This means that you have access to the view's transform property directly via its transform property:
[myTextField setTransform:CGAffineTransformMake(a, b, c, d, x, y)];
Or for 3D support, you can access the transform property of the view's layer to apply a CATransform3D:
[myTextField.layer setTransform:CATransform3DRotate(trans, theta, x, y, z)];
CATransform3D is defined as follows, and as #Xman pointed out, you'll need to import the QuartzCore framework using #import <QuartzCore/QuartzCore.h>, as well as link against it in your build phases.
struct CATransform3D
{
CGFloat m11, m12, m13, m14;
CGFloat m21, m22, m23, m24;
CGFloat m31, m32, m33, m34;
CGFloat m41, m42, m43, m44;
};
typedef struct CATransform3D CATransform3D;
In both of these cases, you can still interact with the text field after a transform has been applied to it.
More information can be found in Apple's documentation.

Check CATransform3D also,
CATransform3D yourTransform = CATransform3DIdentity;
yourTransform.m34 = 1.0 / -500;
//You can rotate the component in any angle around x,y,z axis using following line.
//Below line will rotate the component in 60 degree around y-axis.
yourTransform = CATransform3DRotate(yourTransform, DEGREES_TO_RADIANS(60), 0.0f, 1.0f, 0.0f); //#define DEGREES_TO_RADIANS(d) (d * M_PI / 180)
//You can even translate the component along x,y,z axis
//Below line will translate component by 50 on y-axis
yourTransform = CATransform3DTranslate(yourTransform, 0, 50, 0);
//apply transform to component
yourComponent.layer.transform = yourTransform;
Don't forget to import
#import <QuartzCore/QuartzCore.h>
Hope this will help you.

Related

Convert screen coordinates to Metal's Normalized Device Coordinates

I am trying to render a 2D triangle using user touches. So, I will let a user touch three points on the screen and those points will be used as vertices of a triangle.
You're already aware that you need to return clip-space coordinates (technically not normalized device coordinates) from your vertex shader. The question is how and where to go from UIKit coordinates to Metal's clip-space coordinates.
Let's start by defining these different spaces. Note that below, I actually am using NDC coordinates for the sake of simplicity, since in this particular case, we aren't introducing perspective by returning vertex positions with w != 1. (Here I'm referring to the w coordinate of the clip-space position; in the following discussion, w always refers to the view width).
We pass the vertices into our vertex shader in whatever space is convenient (this is often called model space). Since we're working in 2D, we don't need the usual series of transformations to world space, then eye space. Essentially, the coordinates of the UIKit view are our model space, world space, and eye space all in one.
We need some kind of orthographic projection matrix to move from this space into clip space. If we strip out the unnecessary parts related to the z axis and assume that our view bounds' origin is (0, 0), we come up with the following transformation:
We could pass this matrix into our vertex shader, or we could do the transformation prior to sending the vertices to the GPU. Considering how little data is involved, it really doesn't matter at this point. In fact, using a matrix at all is a little wasteful, since we can just transform each coordinate with a couple of multiplies and an add. Here's how that might look in a Metal vertex function:
float2 inverseViewSize(1.0f / width, 1.0f / height); // passed in a buffer
float clipX = (2.0f * in.position.x * inverseViewSize.x) - 1.0f;
float clipY = (2.0f * -in.position.y * inverseViewSize.y) + 1.0f;
float4 clipPosition(clipX, clipY, 0.0f, 1.0f);
Just to verify that we get the correct results from this transformation, let's plug in the upper-left and lower-right points of our view to ensure they wind up at the extremities of clip space (by linearity, if these points transform correctly, so will all others):
These points appear correct, so we're done. If you're concerned about the apparent distortion introduced by this transformation, note that it is exactly canceled by the viewport transformation that happens prior to rasterization.
Here is a function that will convert UIKit view-based coordinates to Metal's clip space coordinates (based on warrenm`s answer). It can be added directly to a shader file & called from the vertex shader function.
float2 convert_to_metal_coordinates(float2 point, float2 viewSize) {
float2 inverseViewSize = 1 / viewSize;
float clipX = (2.0f * point.x * inverseViewSize.x) - 1.0f;
float clipY = (2.0f * -point.y * inverseViewSize.y) + 1.0f;
return float2(clipX, clipY);
}
You'll want to pass the viewSize (UIKit's bounds) to Metal somehow, say via a buffer parameter on the vertex function.
Translated Thompsonmachine's code to swift, using SIMD values which is what I need to pass to shaders.
func convertToMetalCoordinates(point: CGPoint, viewSize: CGSize) -> simd_float2 {
let inverseViewSize = CGSize(width: 1.0 / viewSize.width, height: 1.0 / viewSize.height)
let clipX = Float((2.0 * point.x * inverseViewSize.width) - 1.0)
let clipY = Float((2.0 * -point.y * inverseViewSize.height) + 1.0)
return simd_float2(clipX, clipY)
}

Translate 3d object center coordinates to 2d visible viewport coordinates

I have loaded an wavefront object in Iphone OpenGL.
It can be rotated around x/y axis, panned around, zoomed in/out.
My task is - when object is tapped, highlight it's 2d center coordinates on screen for example like this: (Imagine that + is in the center of visible object.)
When loading OpenGL object I store it's:
object center position in world,
x,y,z position offset,
x,y,z rotation,
zoom scale.
When user taps on the screen, I can distinguish which object was tapped. But - as user can tap anywhere on object - Tapped point is not center.
When user touches an object, I want to be able to find out corresponding object visible approximate center coordinates.
How can I do that?
Most code in google I could find is meant - to translate 3d coordinates to 2d but without rotation.
Some variables in code:
Vertex3D centerPosition;
Vertex3D currentPosition;
Rotation3D currentRotation;
//centerPosition.x, centerPosition.y, centerPosition.z
//currentPosition.x, currentPosition.y, currentPosition.z
//currentRotation.x, currentRotation.y, currentRotation.z
Thank You in advance.
(To find out which object I tapped - re-color each object in different color, thus I know what color user tapped.)
object drawSelf function:
// Save the current transformation by pushing it on the stack
glPushMatrix();
// Load the identity matrix to restore to origin
glLoadIdentity();
// Translate to the current position
glTranslatef(currentPosition.x, currentPosition.y, currentPosition.z);
// Rotate to the current rotation
glRotatef(currentRotation.x, 1.0, 0.0, 0.0);
glRotatef(currentRotation.y, 0.0, 1.0, 0.0);
glRotatef(currentRotation.z, 0.0, 0.0, 1.0);
// Enable and load the vertex array
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, vertices);
glNormalPointer(GL_FLOAT, 0, vertexNormals);
// Loop through each group
if (textureCoords != NULL)
{
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(valuesPerCoord, GL_FLOAT, 0, textureCoords);
}
for (OpenGLWaveFrontGroup *group in groups)
{
if (textureCoords != NULL && group.material.texture != nil)
[group.material.texture bind];
// Set color and materials based on group's material
Color3D ambient = group.material.ambient;
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, (const GLfloat *)&ambient);
Color3D diffuse = group.material.diffuse;
glColor4f(diffuse.red, diffuse.green, diffuse.blue, diffuse.alpha);
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, (const GLfloat *)&diffuse);
Color3D specular = group.material.specular;
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, (const GLfloat *)&specular);
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, group.material.shininess);
glDrawElements(GL_TRIANGLES, 3*group.numberOfFaces, GL_UNSIGNED_SHORT, &(group.faces[0]));
}
if (textureCoords != NULL)
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
// Restore the current transformation by popping it off
glPopMatrix();
ok, as I said, you'll need to apply the same transformations to the object center that are applied to the object's vertices by the graphics pipeline; only this time, the graphics pipeline won't help you - you'll have to do it yourself. And it involves some matrix calculations, so I'd suggest getting a good maths library like the OpenGL Maths library, which has the advatage that function names etc. are extremly similar to OpenGL.
step 1: transform the center form object coordinates to modelview coordinates
in your code, you set up your 4x4 modelview matrix like this:
// Load the identity matrix to restore to origin
glLoadIdentity();
// Translate to the current position
glTranslatef(currentPosition.x, currentPosition.y, currentPosition.z);
// Rotate to the current rotation
glRotatef(currentRotation.x, 1.0, 0.0, 0.0);
glRotatef(currentRotation.y, 0.0, 1.0, 0.0);
glRotatef(currentRotation.z, 0.0, 0.0, 1.0);
you need to multiply that matrix with the object center, and OpenGL does not help you with that, since it's not a maths library itself. If you use glm, there are functions like rotate(), translate() etc that function similiar to glRotatef() & glTranslatef(), and you can use them to build your modelview matrix. Also, since the matrix is 4x4, you'll have to append 1.f as 4th component to the object center ( called 'w-component' ), otherwise you can't multiply it with a 4x4 matrix.
Alternatively, you could query the current value of th modelview matrix directly from OpenGl:
GLfloat matrix[16];
glGetFloatv (GL_MODELVIEW_MATRIX, matrix);
but then you'll have to write your own code for the multiplication...
step 2: go from modelview coordinates to clip coordinates
from what you posted, I can't tell whether you ever change the projection matrix ( is there a glMatrixMode( GL_PROJECTION ) somewhere? ) - if you never touch the projection matrix, you can omit this step; otherwise you'll now need to multiply the transformed object center with the projection matrix as well.
step 3: perspective division
divide all 4 components of the object center by the 4th - then throw away the 4th component, keeping only xyz.
If you omitted step 2, you can also omit the division.
step 4: map the object center coordinates to window coordinates
the object center is now defined in normalized device coordinates, with x&y components in range [-1.f, 1.f]. the last step is mapping them to your viewport, i.e. to pixel positions. the z-component does not really matter to you anyway, so let's ignore z and call the x & y component obj_x and obj_y, respectively.
the viewport dimensions should be set somewhere in your code with glViewport( viewport_x, viewport_y, width, height ). from the function arguments, you can then calculate the pixel position for the center like this:
pixel_x = width/2 * obj_x + viewport_x + width/2;
pixel_y = height/2 * obj_y + viewport_y + height/2;
and that's basically it.

Rotate an object around its center point in unity 3d

How to rotate a 3d game object around its center point in unity 3d.
Just use bounds.center from the renderer
Vector3 position = myGameObject.GetComponent<Renderer>().bounds.center;
myGameObject.transform.RotateAround(position, rotationVector, degreesPerSecond * Time.deltaTime);
where rotationVector is your rotation axis (Vector3)
The two common ways to rotate an object are
The rotate attribute in the transform. Using this one you can set the exact coordinates for the target object rotation. However, you'll have to manage the smoothness by yourself if you want to make animations and the values are given through Quaternion type. I recommend to use the static method Quaternion.Euler so you can pass values in a X, Y, Z. The example below set the object to turn 90 degrees clockwise in the Y axis:
transform.rotation = Quaternion.Euler (0, 90, 0);
The second approach uses the Rotation method in the same transform attribute. This method allow you to pass the amount of degrees in which object will rotate and already accept X, Y and Z coordinates instead Quaternion.
The example below rotate the object 1 degree clockwise in the Y axis:
transform.Rotate (0, 1, 0);
To best understand the difference between both methods, if you use the first one in an Update method you'll see the object static rotated 90 degrees in the Y axis. The second example used in an Update will make the object spin clockwise in the Y axis (too fast).
use
Transform.rotation
look here for Examples/Documentation :
Unity Script Reference

CATransform3D transform

I'm trying to add perspective to a view by using CATransform3D. Currently, this is what I'm getting:
And this is what I wanna get:
I'm having a hard time doing that. I'm completely lost here. Here's my code:
CATransform3D t = CATransform3DIdentity;
t.m11 = 0.8;
t.m21 = 0.1;
t.m31 = -0.1;
t.m41 = 0.1;
[[viewWindow layer] setTransform:t];
Matrix element .m34 is responsible for perspective. It's not discussed much in the documentation, so you'll have to toy with it. This answer talks a little bit about how to use it: https://stackoverflow.com/a/7596326/1228525
To actually see the effects of that matrix you need to do two things:
1. Apply that perspective matrix to the parent view's sublayer transform
2. Rotate the child view (the one on which you want perspective) - otherwise it will remain flat and you won't be able to tell it now has a 3D perspective.
The numbers are arbitrary, make them whatever looks best:
CATransform3D t = CATransform3DIdentity;
t.m34 = .005;
parentView.layer.sublayerTransform. = t;
childview.layer.transform = CATransform3DMakeRotation(45,1,0,0);
The perspective will look different depending on where the child is in the parent view. If the child is in the center of the parent it will be like you are looking at the child view in 3D straight on. The further from the center it is, the more it will be like you are viewing from a glancing angle.
This is what I got using the above code and centering the child view: (apparently I'm not allowed to post pictures since I'm new, so you'll have to see the link) http://i.stack.imgur.com/BiYCS.png
It's very hard to tell what you're going for based on those pictures; a bit more explanation might be helpful if my answer isn't what you want. From what I can tell from the picture, the bottom one isn't perspective...
I was able to easily achieve the right CATransform3D using AGGeometryKit.
#import <AGGeometryKit/AGGeometryKit.h>
UIView *view = ...; // create a view
// setting anchorPoint to zero
view.layer.anchorPoint = CGPointZero;
view.layer.transform = CATransform3DMakeTranslation(-view.layer.bounds.size.width * .5, -view.layer.bounds.size.height * .5, 0);
// setting a trapezoid transform
AGKQuad quad = view.layer.quadrilateral;
quad.tl.x -= 10; // shift top left x-value with 10 pixels
view.layer.quadrilateral = quad; // the quad is converted to CATransform3D and applied

3D Cube Problem! Part 1

I have created a 3D cube in iphone using CALayer's. Now I wanted to rotate that cube(CALayer) at 90˚ when user double taps on it.
I was able to rotate that cube(CALayer) to 90˚ once but when I double tap the cube(CALayer) is not rotating.
Here is the code that I used to rotate the cube(CALayer)
CATransform3D x = CATransform3DRotate(currentLayer.sublayerTransform, M_PI / 2, 0, 0, 1);
currentLayer.transform = x;
Can anyone help in this. What I'm doing wrong.
PS. For the people who are wondering how I got the degree sign then here is the trick
Option + K
its because you are not changing the angle of rotation .... to understand this lets say you are passing M_PI/2 each time to that method .... so CATransform3DRotate do not rotate it to next 90˚ rather it rotate the layer to the specified angle in this case its 90... so you are not getting any chage because it already at 90˚ ..... so to get correct result do this
static float angle = M_PI / 2;//dont make it static rather make it a global variable
angle += M_PI / 2;
CATransform3D x = CATransform3DRotate(currentLayer.sublayerTransform,angle, 0, 0, 1);
currentLayer.transform = x;