Core Graphics框架 :仿射变换与齐次坐标
Core Graphics框架中的图形变换
在Core Graphics框架图形绘制的时候,经常会有对图形进行平移、缩放、旋转这样的要求.那么我们该如何实现呢?这就需要Core Graphics框架中的CGAffineTransform(矩阵)这个结构体来进行实现了.下面我们就对CGAffineTransform这个矩阵结构体,进行逐一的说明.
CGAffineTransform与齐次坐标
首先,我们先看一下CGAffineTransform这个结构体是什么样子的,如下所示.
struct CGAffineTransform {
CGFloat a, b, c, d;
CGFloat tx, ty;
};
为了把二维图形的变化统一在一个坐标系里,引入了齐次坐标的概念,即把一个图形用一个三维矩阵表示,其中第三列总是(0,0,1),用来作为坐标系的标准.也就是z轴.是不发生改变的。接下来呢,我们就使用齐次坐标来表示这个结构体.如下所示.
|a b 0|
|c d 0|
|tx ty 1|
现在我们就看一下用齐次坐标是如何表示一个坐标的仿射变换的,假设现在有坐标 [X ,Y,1],运算原理如下所示.
|a b 0|
[X,Y, 1] |c d 0| = [aX + cY + tx bX + dY + ty 1] ;
|tx ty 1|
那么平移、缩放、旋转是怎么执行的呢?其实很简单,这三种形式的变换不过是齐次坐标的几个特殊情况,下面我们就一一道来.
平移变换
条件: a = d = 1 ,b = c = 0.
条件如上所述,接下来我们看一下坐标 [X ,Y,1]与我们设定好的齐次坐标的叉积会发生什么样的变化.
|1 0 0|
[X,Y, 1] |0 1 0| = [X + tx , Y + ty , 1] ;
|tx ty 1|
那么的坐标就变成了 [X + tx , Y + ty , 1],与原坐标相比,z轴没发生任何的改变,但是x轴方向平移了tx个单位,y轴方向平移了ty个单位.
那么如果我们把这种特殊情况进行封装,我们就得到了我们的平移函数.
CGAffineTransformMakeTranslation(CGFloat tx,CGFloat ty)
缩放变换
条件: b = c = 0 ,tx = ty = 0.
如平移变换一致,我们把条件输入进去,看一下坐标 [X ,Y,1]与我们设定好的齐次坐标的叉积会发生什么样的变化.
|a 0 0|
[X,Y, 1] |0 d 0| = [aX , dY ,1] ;
|0 0 1|
那么的坐标就变成了[aX , dY ,1],与原坐标相比,z轴仍然没发生任何的改变,但是x轴方向缩放了a倍,y轴方向缩放了d倍.
那么如果我们把这种特殊情况进行封装,我们就得到了我们的缩放函数.注意一点的是使用缩放函数的时候,sx和sy如果不想让其改变就设置为1,而不是0.
GAffineTransformMakeScale(CGFloat sx, CGFloat sy)
旋转变换
条件 : tx=ty=0,a=cosɵ,b=sinɵ,c=-sinɵ,d=cosɵ
跟上面的两种情况一样,我们首先条件带入我们的齐次坐标里面,看一下结果如何.
|cosɵ sinɵ 0|
[X,Y, 1] |-sinɵ cosɵ 0| = [Xcosɵ - Ysinɵ , Xsinɵ + Ycosɵ , 1] ;
|tx ty 1|
这个时候,ɵ就是旋转的角度,逆时针为正,顺时针为负。
那么如果我们把这种特殊情况进行封装,我们就得到了我们的缩放函数.
CGAffineTransformMakeRotation(CGFloat angle)
CGAffineTransform的使用
上面,我们已经对仿射变换有了大体的了解,知道了它的原理,那么接下来我们就做一个简单Demo.来看一下在Core Graphics框架中是如何使用仿射变换函数的.
首先还是创建SDView继承与UIView类.
我们依然在drawInRect:
这个方法中进行我们的操作.我们在方法中先在图形上下文中绘制一个矩形.代码如下.
//获取图形上下文
CGContextRef context = UIGraphicsGetCurrentContext();
//创建路径
CGPathRef path = CGPathCreateWithRect(CGRectMake(100, 100, 100, 100), nil);
//添加路径
CGContextAddPath(context, path);
//设置颜色
CGContextSetRGBStrokeColor(context, 1.0, 0, 0, 1);
//绘制
CGContextDrawPath(context, kCGPathFillStroke);
//删除路径
CGPathRelease(path);
接下来我们直接在ViewController中添加SDView这个视图.
#import "ViewController.h"
#import "SDView.h"
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
SDView *view = [[SDView alloc]initWithFrame:self.view.frame];
view.backgroundColor = [UIColor whiteColor];
[self.view addSubview:view];
}
@end
效果如下.
上面绘制的矩形是没有经过任何图形变换的.接下来我们就在drawInRect:
这个方法中创建三种仿射变换.代码如下.
//创建平移变化结构体
CGAffineTransform translationAffineTransform = CGAffineTransformMakeTranslation(100, 0);
//创建缩放变化结构体
CGAffineTransform scaleAffineTransform = CGAffineTransformMakeScale(3, 0);
//创建缩放变化结构体
CGAffineTransform rotationAffineTransform = CGAffineTransformMakeRotation(M_PI_2);
紧接着,我们就把创建路径这个代码替换成有仿射变换的路径.(当然了,我们要把多余的注释掉嗷~~)
// //创建路径
// CGPathRef path = CGPathCreateWithRect(CGRectMake(100, 100, 100, 100), nil);
//
//创建路径(平移)
CGPathRef path = CGPathCreateWithRect(CGRectMake(100, 100, 100, 100), &translationAffineTransform);
//创建路径(缩放)
CGPathRef path = CGPathCreateWithRect(CGRectMake(100, 100, 100, 100), &scaleAffineTransform);
//创建路径(旋转)
CGPathRef path = CGPathCreateWithRect(CGRectMake(100, 100, 100, 100), &rotationAffineTransform);
下面我们就看一下三种仿射变换的效果图.
平移变换 缩放变换 旋转变换