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

下面我们就看一下三种仿射变换的效果图.

平移变换

缩放变换

旋转变换



Core Graphics框架仿射变换就说这么多了,后期,最后双手奉上Demo.
–>仿射变换Demo传送门💾
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 猿与汪的秘密 设计师:白松林 返回首页