21
votes

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)

7

7 Answers

48
votes

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 |
34
votes
- (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);
}
10
votes

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

5
votes

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.

4
votes

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!]

1
votes

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.

0
votes

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);
}