Get just the scaling transformation out of CGAffineTransform
I found a similar question about getting just the rotation, but as I understand scaling and rotating work different in the transform开发者_开发百科 matrix.
Matrixes are not my strength, so if anybody would hint me how to get only the scaling out of a CGAffineTransform I'd greatly appreciate.
btw. I tried applying the CGAffineTransform on a CGSize and then get the height x width to see how much it scaled, but height x width have strange values (depending on the rotation found in the CGAffineTransform, so ... hm that does not work)
Assuming that the transformation is a scaling followed by a rotation (possibly with a translation in there, but no skewing) the horizontal scale factor is sqrt(a^2+c^2)
, the vertical scale factor is sqrt(b^2+d^2)
, and the ratio of the horizontal scale factor to the vertical scale factor should be a/d = -c/b
, where a
, b
, c
, and d
are four of the six members of the CGAffineTransform
, per the documentation (tx
and ty
only represent translation, which does not affect the scale factors).
| a b 0 |
| c d 0 |
| tx ty 1 |
- (CGFloat)xscale {
CGAffineTransform t = self.transform;
return sqrt(t.a * t.a + t.c * t.c);
}
- (CGFloat)yscale {
CGAffineTransform t = self.transform;
return sqrt(t.b * t.b + t.d * t.d);
}
That is an old question, but I still add more information in case someone needs.
For me, the good answer and sample code for getting scale, rotation of transformation and for reproducing it are from article:
http://www.informit.com/articles/article.aspx?p=1951182
I'm not familiar with CGAffineTransform or Objective-C (you caught me with the math tag). In general, you need to back out the transforms individually. For instance if the affine transform A performs scaling, rotation and translation only (the order of scaling & rotation isn't important in the method below, but translation should be definitely be last):
Translation: Applying A to the vector (0,0) will return the result (tx, ty) where tx and ty are the translations in the X and Y directions respectively.
Scaling in X: Apply A to the vector (1, 0) and get (sx0 + tx, sx1 + ty). The scaling in X will be sqrt(sx0^2 + sx1^2)
Scaling in Y: Apply A to the vector (0, 1) and get (sy0 + tx, sy1 + ty). The scaling in Y will be sqrt(sy0^2 + sy1^2)
Since affine transformations are implemented by a simple trick with linear transformations and since linear transformations are not commutative, you need to understand how the transformations are ordered before actually working through how to pull the individual transformation out.
It's an old (pre-Swift) question so for Swift folk arriving here based on a search for CGAffineTransform
here's a Swift 3 convenience extension based on the other answers:
extension CGAffineTransform {
var xScale: CGFloat { return sqrt(self.a * self.a + self.c * self.c) }
var yScale: CGFloat { return sqrt(self.b * self.b + self.d * self.d) }
var rotation: CGFloat { return CGFloat(atan2(Double(self.b), Double(self.a))) }
// .tx and .ty are already available in the transform
}
[Edit: Updated rotation to use Doubles in light of the comment. Thx!]
Let me propose different solution. I first rotate the affine transform in the opposite direction and just read scale from t.a and t.d:
- (CGPoint)scaleFromTransform {
CGAffineTransform t = self.transform;
CGFloat angle = atan2(t.b, t.a);
// calculate rotation transform (by -angle)
CGAffineTransform r = CGAffineTransformMakeRotation(-angle);
// calculate main transform without rotation
CGAffineTransform t0 = CGAffineTransformConcat(t, r);
CGFloat xScale = t0.a;
CGFloat yScale = t0.d;
return CGPointMake(xScale, yScale);
}
Swift 4 Extension (thanks to Robin Macharg's answer)
extension CGAffineTransform
{
var xScale: CGFloat { return sqrt(a * a + c * c) }
var yScale: CGFloat { return sqrt(b * b + d * d) }
var rotation: CGFloat { return CGFloat(atan2(Double(b), Double(a))) }
var xOffset: CGFloat { return tx }
var yOffset: CGFloat { return ty }
}
Note that CGFloat equals a Double on most platforms, thus conversions need to be to Double.
I recommend Slyv's answer. And this is a swift version.
extension CGAffineTransform {
var scales:CGPoint {
let t = self
let angle: CGFloat = atan2(t.b, t.a)
// calculate rotation transform (by -angle)
let r = CGAffineTransform(rotationAngle: -angle)
// calculate main transform without rotation
let t0 = t.concatenating(r)
let xScale = t0.a
let yScale = t0.d
return CGPoint(x: xScale, y: yScale)
}
var sx:CGFloat {
self.scales.x
}
var sy:CGFloat {
self.scales.y
}
}
I think there is one problem if you use the function below.
var xScale: CGFloat { return sqrt(a * a + c * c) }
var yScale: CGFloat { return sqrt(b * b + d * d) }
The problem is you cannot get a minus value. In UIKit or others, you set the minus value to the scale of transform for flipping views or layers, don't you? "sqrt" will convert the negative value to positive.
If I misunderstood about this please let me know.
精彩评论