Ok, so I realize I can find the scale value from a layer's CATransform3D like this:
float scale = [[layer valueForKeyPath: #"transform.scale"] floatValue];
But I can't for the life of me figure out how I would find the scale value from a CGAffineTransform. Say for instance I have this CGAffineTransform called "cameraTransform":
UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
CGAffineTransform *cameraTransform = imagePicker.cameraViewTransform;
Now how do I get the scale value from cameraTransform?
I try to give a general answer for all kinds of CGAffineTransforms, even rotated ones.
Assuming your CGAffineTransform contains (optionally)
rotation
translation
scaling
and
NO skew
then the there’s a general formula that gives you the scale factor:
CGAffineTransform transform = ...;
CGFloat scaleFactor = sqrt(fabs(transform.a * transform.d - transform.b * transform.c));
"Mirroring" or flipping coordinate directions will be ignored, that means (x --> -x; y --> y) will result in scaleFactor == 1 instead of -1.
http://en.wikipedia.org/wiki/Determinant
"A geometric interpretation can be given to the value of the determinant of a square matrix with real entries: the absolute value of the determinant gives the scale factor by which area or volume is multiplied under the associated linear transformation, while its sign indicates whether the transformation preserves orientation. Thus a 2 × 2 matrix with determinant −2, when applied to a region of the plane with finite area, will transform that region into one with twice the area, while reversing its orientation."
The article goes on to give formulas for the determinant of a 3x3 matrix and a 2x2 matrix. CGAffineTransforms are 3x3 matrices, but their right column is always 0 0 1. The result is the determinant will be equal to the determinant of the 2x2 upper left square of the matrix. So you can use the values from the struct and compute the scale yourself.
The Quartz 2D Programming Guide tells you where to find the scale values. Compare that with the definition of the CGAffineTransform structure.
Related
My binary image has rectangular rotated objects of known size on it. I'd like to get the object inclination using axis-aligned bounding box that MATLAB's regionprops returns. What are my suggestions:
Let bounding box width be W, side of rectangle be C and inclination alpha
Then
Using Weierstrass substitution
After some simplification:
Solving the equation for tan(alpha/2) with
For any nonzero inclination discriminant is positive.
Logic seems to be OK, so as math. Could you please point where I make a mistake, or what is a better way to get inclination?
Here is corresponding MATLAB code:
img = false(25,25);
img(5:16,5:16) = true;
rot_img = imrotate(img, 30, 'crop');
props = regionprops(bwlabel(rot_img),'BoundingBox');
bbox = cat(1,props.BoundingBox);
w = bbox(3);
h = 12;
a = -1*(1+w/h); b = 2; c = 1 - w/h;
D = b^2 - 4*a*c;
alpha = 2*atand((-b + sqrt(D))/(2*a));
%alpha = 25.5288
EDIT Thank you for trigonometry hints. They significantly simplify the calculations, but they give wrong answer. As I now understand, the question is asked in wrong way. The thing I really need is finding inclination of short lines (10-50 pixels) with high accuracy (+/- 0.5 deg), the lines' position is out of interest.
The approach used in the question and answers show better accuracy for long lines, for c = 100 error is less than 0.1 degree. That means we're into rasterization error here, and need subpixel accuracy. At the moment I have only one algorithm that solves the problem - Radon transform, but I hope you can recommend something else.
p = bwperim(rot_img);
theta=0:0.1:179.9;
[R,xp] = radon(p,theta); %Radon transform of contours
a=imregionalmax(R,true(3,3)); %Regional maxima of the transform
[r,c]=find(a); idx=sub2ind(size(a),r,c); maxvals=R(idx);
[val,midx]=sort(maxvals,'descend'); %Choose 4 highest maxima
mean(rem(theta(c(midx(1:4))),90)) %And average corresponding angles
%29.85
If rectangle is square:
w/c=sin(a)+cos(a)
(w/c)^2=1+sin(2a)
sin(2a)=(w/c)^2-1
a=0.5*arcsin((w/c)^2-1)
May be use regionprops function with 'Orientation' option...
I am trying to determine a players depth position on a plane, which defines the walkable ground in a 2D brawler game. The problem is depictured in the following drawing:
C represents the players current position. I need to find the magnitude of vector V. Since I am not strong on linear algebra, the one thing I can think of is: determining the intersection point P of L1 and L2, and then take the magnitude from AP. However, I get the feeling there must be an easier way to find V, since I already know the angle the vector should have, given by vector from AB.
Any input would be appreciated, since I am looking forward to step up my linear algebra game.
Edit: As it is unclear thanks to my lack of drawing skills: the geometry depicted above is a parallelogram. The vector V I am looking for is parallel to the left and right side of the parallelogram. Depth does not mean, that I am looking for the vector perpendicular to the top side, but it refers to the fake depth of a purely 2D game. The parallelogram is therefore used as a means for creating the feeling of walking along a z axis.
The depth of your player (length of V) as measured from the top line in your drawing, is just the difference between A.y and C.y. This is seperate from the slant in the parralelogram, as we're just looking at depth.
example:
float v;
Vector2 a = new Vector2(100, 100); //The point you're measuring from
Vector2 c = new Vector2(150, 150); //Your character position
v = c.y - a.y; // This is the length of V.
//In numbers: 50 = 150 - 100
Illustrated: image not to scale
This works for any coördinate in your plane.
Now if you'd want to get the length of AC is when you'd need to apply some pythagoras, which is a² + b² = c². In the example that would mean in code:
Vector2 a = new Vector2(100, 100);
Vector2 c = new Vector2(150, 150);
float ac1 = Mathf.Sqrt(Mathf.Pow(c.x - a.x, 2) + Mathf.Pow(c.y - a.y, 2));
Now that is quite a sore to have to type out every time, and looks quite scary. But Unity has you covered! There is a Vector method called Distance
float ac2 = Vector2.Distance(a, c);
Which both return 70.71068 which is the length of AC.
This works because for any point c in your area you can draw a right angled triangle from a to c.
Edit as per comment:
If you want your "depth" vector to be parallel with the sides of the paralellogram we can just create a triangle in the parallelogram of which we calculate the hypotenuse.
Since we want the new hypotenuse of our triangle to be parallel to the parallelogram we can use the same angle θ as point B has in your drawing (indicated by pink in mine), of which I understood you know the value.
We also know the length of the adjacent (indicated in blue) side of this new triangle, as that is the height we calculated earlier (c.y - a.y).
Using these two values we can use cosine to find the length of hypotenuse (indicated in red) of the triangle, which is equal to the vector V, in parallel with the parallelogram.
the formula for that is: hypotenuse = adjacent/cos(θ)
Now if we were to put some numbers in this, and for my example I took 55 for the angle θ. It would look like this
float v = 50/(cos(55));
image not to scale
Let's call the lower right vertex of the parallelogram D.
If the long sides of the parallelogram are horizontal, you can find magnitude of V vector by:
V.magnitude = (c.y - a.y) / sin(BAD)
Or if you prefer:
V.magnitude = AB.magnitude * (c.y - a.y)/(b.y - a.y)
Imagine a dome with its centre in the +z direction. What I want to do is to move that dome's centre to a different axis (e.g. 20 degrees x axis, 20 degrees y axis, 20 degrees z axis). How can I do that ? Any hint/tip helps.
Add more info:
I've been dabbling with rotation matrices in wiki for a while. The problem is, it is not a commutative operation. RxRyRz is not same as RzRyRx. So based on the way I multiple it I get a different final results. For example, I want my final projection to have 20 degrees from the original X axis, 20 degrees from original Y axis and 20 degrees from original Z axis. Based on the matrix, giving alpha, beta, gamma values 20 (or its corresponding radian) does NOT result the intended rotation. Am I missing something? Is there a matrix that I can just put the intended angles and get it at the end ?
Using a rotation matrix is an easy way to rotate a collection of (x,y,z) points. You can calculate a rotation matrix for your case using the equations in the general rotation section. Note that figuring out the angle values to plug into those equations can be tricky. Think of it as rotating about one axis at a time and remember that the order of your rotations (order of multiplications) does matter.
An alternative to the general rotation equations is to calculate a rotation matrix from axis and angle. It may be easier for you to define correct parameters with this method.
Update: After perusing Wikipedia, I found a simple way to calculate rotation axis and angle between two vectors. Just fill in your starting and ending vectors for a and b here:
a = [0.0 0.0 1.0];
b = [0.5 0.5 0.0];
vectorMag = #(x) sqrt(sum(x.^2));
rotAngle = acos(dot(a,b) / (vectorMag(a) * vectorMag(b)))
rotAxis = cross(a,b)
rotAxis =
-0.5 0.5 0
rotAngle =
1.5708
Suppose the current scale of my UIView is x. Suppose I apply a scale transformation to my UIView of the amount y ie:
view.transform = CGAffineTransformScale(view.transform, y, y);
. How do I determine what the value of the scale of the UIView after the scale transformation occurs (in terms of x and y?).
The scale transform multiplies the current scale with your scale y.
if the scale was 2.0 for retina, it is y* 2.0 afterwards.
So x*y is the answer. but dont forget x.achsis scale and y achsis can be different.
x, and y for scale is confusing, better use s1 and s2, or sx, sy if you have different scale on y and x achsis, in your code.
Scaling combines by multiplication, translation (movement) by addition, rotation is a matrix multiplication. All three can be combined into an AffineTransformation (a matrix with 1 more row than the dimensions of the space), these are combined by matrix multiplication. 2D AffineTransformations are 3x2 or 3x3 matrices, the extra column just makes them easier to work with.
Edit:
Using clearer names: if he current scale was currxs, currys and the scale applied was xs,ys the new scale would be currxs*xs, currys*ys. Note that applying a scale will also scale any translation component that is contained in the AffineTransformation, this is why order of application is important.
Its quite simple if you are just using the CGAffineTransformScale and not the other transformations like rotation, you can use the view frame and bounds size to calculate the resulting scale values.
float scaleX = view.frame.size.width/view.bounds.size.width;
float scaleY = view.frame.size.height/view.bounds.size.height;
I am creating an iPad app and want to update the user with the current size of a view. I used to be doing this by calculating the scale of a CGAffineTransform and multiplying the xScale by the width (of the related view) and the yScale by the height. I'd like to keep doing this, but I'm no longer doing 2d transformations, and I'm not sure what equation to use to extract the scale information from CATransform3D.
Can you help?
To get the current scale of the layer you just perform a valueForKeyPath: on the layer:
CGFloat currentScale = [[layer valueForKeyPath: #"transform.scale"] floatValue];
Other keys can be found in Apples Core Animation Programming Guide
I'm not familiar with the API but CATransform3D looks like a regular 4x4 transformation matrix for doing 3D transformations.
Assuming that it represents nothing more than a combination of scale, rotation and translation, the scale factors can be extracted by calculating the magnitudes of either the rows or columns of the upper left 3x3 depending on whether CATransform3D is row or column major respectively.
For example, if it is row-major, the scale in the x-direction is the square root of ( m11 * m11 ) + ( m12 * m12 ) + ( m13 * m13 ). The y and z scales would similarly be the magnitudes of the second and third rows.
From the documentation for CATransform3DMakeTranslation it appears that CATransform3D is indeed row-major.
Here's an update answer for swift 4+
guard let currentScaleX = view.layer.value(forKeyPath: "transform.scale.x") as? CGFloat {
return
}
print ("the scale x is \(currentScaleX)")
guard let currentScaleY = view.layer.value(forKeyPath: "transform.scale.y") as? CGFloat {
return
}
print ("the scale y is \(currentScaleY)")