Tuesday, October 18, 2011

CGAffineTransform Additions

As you probably know, Apple provides a bunch of functionality for manipulating objects in 2D space using CGAffineTransform. Oddly, Apple doesn't provide you with a way to extract the scale, transform, and rotation information from a CGAffineTransform and they don't provide any shearing functionality at all.

Here are some additional inline functions that I use. This adds the ability to extract component values of the CGAffineTransform and also adds the ability to create and extract shear information.

As always, this code is free to use without restriction or limitation, but has no warranty whatsoever. If you fix a bug, feel free to let me know about the fix so I can incorporate the fix.


#import <CoreGraphics/CoreGraphics.h>

#ifdef __cplusplus
extern "C" {

#define degreesToRadian(x) (M_PI * x / 180.0)
#define radiansToDegrees(x) (180.0 * x / M_PI)

static inline CGAffineTransform CGAffineTransformMakeShear(CGFloat shearX, CGFloat shearY)
    return CGAffineTransformMake(1.f, shearY, shearX, 1.f, 0.f, 0.f);
static inline CGAffineTransform CGAffineTransformShear(CGAffineTransform transform, CGFloat shearX, CGFloat shearY)
    CGAffineTransform sheared = CGAffineTransformMakeShear(shearX, shearY);
    return CGAffineTransformConcat(transform, sheared);
static inline CGFloat CGAffineTransformGetDeltaX(CGAffineTransform transform) {return transform.tx;};
static inline CGFloat CGAffineTransformGetDeltaY(CGAffineTransform transform) {return transform.ty;};
static inline CGFloat CGAffineTransformGetScaleX(CGAffineTransform transform) {return sqrtf( (transform.a * transform.a) + (transform.c * transform.c) );};
static inline CGFloat CGAffineTransformGetScaleY(CGAffineTransform transform) {return sqrtf( (transform.b * transform.b) + (transform.d * transform.d) );};
static inline CGFloat CGAffineTransformGetShearX(CGAffineTransform transform) {return transform.b;};
static inline CGFloat CGAffineTransformGetShearY(CGAffineTransform transform) {return transform.c;};
static inline CGFloat CGPointAngleBetweenPoints(CGPoint first, CGPoint second) 
    CGFloat dy = second.y - first.y;
    CGFloat dx = second.x - first.x;
    return atan2f(dy, dx);
static inline CGFloat CGAffineTransformGetRotation(CGAffineTransform transform)
    // No exact way to get rotation out without knowing order of all previous operations
    // So, we'll cheat. We'll apply the transformation to two points and then determine the
    // angle betwen those two points
    CGPoint testPoint1 = CGPointMake(-100.f, 0.f);
    CGPoint testPoint2 = CGPointMake(100.f, 0.f);
    CGPoint transformed1 = CGPointApplyAffineTransform(testPoint1, transform);
    CGPoint transformed2 = CGPointApplyAffineTransform(testPoint2, transform);
    return CGPointAngleBetweenPoints(transformed1, transformed2);
#ifdef __cplusplus


Todd Bates said...

I came across this code for calculating the transform.

float rotation = atan2f(transform.b, transform.a);

It might be interesting to see if the two are equivalent. Guess I'll have to break out my sons math text.

Jeff LaMarche said...


I tried a few different algorithms for extracting rotation, including the one from XAffineTransform (a Java library that extends AffineTransform) but those approximations didn't work if skew had been applied and I found they weren't always very accurate. This approach, though a little hacky, gives very accurate results without much overhead.

But, if you try that approach out and it works well, let me know, I'll add it as an alternative method.

Heber said...

Wow - thanks so much for sharing, Jeff! I was just contemplating how to extract rotation, etc.. from a transformation matrix just the other day. Very helpful!

AlBlue said...


#define degreesToRadian(x) (M_PI * x / 180.0)
#define radiansToDegrees(x) (180.0 * x / M_PI)

it would be better to define:

#define degreesToRadian(x) (M_PI * (x) / 180.0)
#define radiansToDegrees(x) (180.0 * (x) / M_PI)

as otherwise expressions like degreesToRadiance(358+2) come out with completely the wrong answer.