Core Graphics框架 :仿射變換與齊次座標

weixin_33935777發表於2016-09-08
1396375-7dbb3f79e082f1a9.png


Core Graphics框架中的圖形變換


在Core Graphics框架圖形繪製的時候,經常會有對圖形進行平移、縮放、旋轉這樣的要求.那麼我們該如何實現呢?這就需要Core Graphics框架中的CGAffineTransform(矩陣)這個結構體來進行實現了.下面我們就對CGAffineTransform這個矩陣結構體,進行逐一的說明.

1396375-43ecdfe2bf339630.jpg


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類.

1396375-ccaf1e4929a376d6.png

我們依然在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

效果如下.

1396375-5a070e086750883a.png

上面繪製的矩形是沒有經過任何圖形變換的.接下來我們就在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);

下面我們就看一下三種仿射變換的效果圖.

1396375-ca95ced881f8e4f7.png
平移變換
1396375-f5b1cce710057175.png
縮放變換
1396375-3e63575652e2000e.png
旋轉變換



Core Graphics框架仿射變換就說這麼多了,後期,最後雙手奉上Demo.

-->仿射變換Demo傳送門?

相關文章