Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

From CATransform3D to CGAffineTransform

I'm using the following function to apply a pulse effect to a view

- (void)pulse {

    CATransform3D trasform = CATransform3DScale(self.layer.transform, 1.15, 1.15, 1);
    trasform = CATransform3DRotate(trasform, angle, 0, 0, 0);

    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform"];
    animation.toValue = [NSValue valueWithCATransform3D:trasform];
    animation.autoreverses = YES;
    animation.duration = 0.3;
    animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    animation.repeatCount = 2;
    [self.layer addAnimation:animation forKey:@"pulseAnimation"];

}

I would like to obtain the same result using the CGAffineTransform self.transform instead of the CATransform3D self.layer.transform. Is this possible?

like image 854
Abramodj Avatar asked May 08 '12 11:05

Abramodj


2 Answers

It's possible convert a CATransform3D to a CGAffineTransform, but you will lose some capabilities. I found it useful to convert the aggregate transform of a layer and its ancestors into a CGAffineTransform so I could render it with Core Graphics. The constraints are:

  • Your input will be treated as flat in the XY plane
  • Your output will be treated as flat in the XY plane too
  • Perspective / foreshortening from .m34 will be neutralized

If that sounds OK for your purposes:

    // m13, m23, m33, m43 are not important since the destination is a flat XY plane.
    // m31, m32 are not important since they would multiply with z = 0.
    // m34 is zeroed here, so that neutralizes foreshortening. We can't avoid that.
    // m44 is implicitly 1 as CGAffineTransform's m33.
    CATransform3D fullTransform = <your 3D transform>
    CGAffineTransform affine = CGAffineTransformMake(fullTransform.m11, fullTransform.m12, fullTransform.m21, fullTransform.m22, fullTransform.m41, fullTransform.m42);

You will want to do all your work in 3D transforms first, say by concatenating from your superlayers, and then finally convert the aggregate CATransform3D to a CGAffineTransform. Given that layers are flat to begin with and render onto a flat target, I found this very suitable since my 3D rotations became 2D shears. I also found it acceptable to sacrifice foreshortening. There's no way around that because affine transforms have to preserve parallel lines.

To render a 3D-transformed layer using Core Graphics, for instance, you might concatenate the transforms (respecting anchor points!), then convert to affine, and finally:

    CGContextSaveGState(context);
    CGContextConcatCTM(context, affine);
    [layer renderInContext:context];
    CGContextRestoreGState(context);
like image 187
easeout Avatar answered Oct 27 '22 09:10

easeout


Of course. If you do a search on CGAffineTransform in the Xcode docs, you'll find a chapter titled "CGAffineTransform Reference". In that chapter is a section called "Functions". It includes functions that are equivalent to CATransform3DScale (CGAffineTransformScale ) and CATransform3DRotate (CGAffineTransformRotate).

Note that your call to CATransform3DRotate doesn't really make sense. You need to rotate around an axis, and you're passing 0 for all 3 axes. Typcially you want to use CATransform3DRotate(trasform, angle, 0, 0, 1.0) to rotate around the Z axis. To quote the docs:

If the vector has zero length the behavior is undefined.

like image 35
Duncan C Avatar answered Oct 27 '22 10:10

Duncan C