1.座標變換的數學基礎
1.1 座標系
數學中我們使用的是笛卡兒座標系,如下圖所示:
X軸正方向向右,Y軸正方向向上,原點O,座標點A(x,y)。
1.2 仿射變換
已知座標點A,變換後新座標系中座標爲B ,則有:
B = AM
A爲1*3的矩陣[x,y,1]
B爲 1* 3的矩陣[x’,y’,1]
M爲3*3仿射矩陣
所以AM=B可以寫成:
展開後得到:
以下是常用的變換矩陣:
單位矩陣:
展開後得:
平移矩陣:
展開後得:
縮放矩陣:
展開後得:
旋轉矩陣:
展開後得:
1.3 座標變換類型
模型變換: 座標系固定不動,變換座標系中的模型
座標軸變換:座標系中模型固定不動, 變換座標系,相對於新座標系構造的變換矩陣和模型變換矩陣是一樣的
如上圖所示,A爲原座標系C中座標,A'爲新座標系中C'座標,在模型變換中新座標系和原座標系重疊在一起
二種變換方式A->A'變換矩陣都是相同的.
2. IOS中座標系
2.1 IOS中包含UIKit座標系(X軸正方向向右,Y軸正方向向下)和標準的Quartz 2D繪圖座標系(X軸正方向向右,Y軸正方向向上)
使用相關API時要仔細閱讀文檔,弄清座標系類型,原點位置等.
2.2 例子分析
2.2.1把中心點爲A(50,50)長爲20,寬爲10的矩形以X軸逆時針旋轉45度
- (void)drawRect:(CGRect)rect{ /** * UIKit座標系,原點在UIView左上角 */ CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSaveGState(context); CGAffineTransform transform; /** * 變換後 transform = CBA,顯然不是想要的結果. * 這是由於CGAffineTransform變換函數構造的矩陣在左邊,如: * t' = CGAffineTransformTranslate(t,tx,ty) * 結果爲:t' = [ 1 0 0 1 tx ty ] * t * 累積變換就會得到上面的結果 */ transform = CGAffineTransformIdentity; transform = CGAffineTransformTranslate(transform, -50, -50); //A transform = CGAffineTransformRotate(transform, M_PI_4); //B transform = CGAffineTransformTranslate(transform, 50, 50); //C /** * 爲了得到正確結果,調整順序如下: */ transform = CGAffineTransformIdentity; transform = CGAffineTransformTranslate(transform, 50, 50); //C transform = CGAffineTransformRotate(transform, M_PI_4); //B transform = CGAffineTransformTranslate(transform, -50, -50); //A /** * context函數變換 */ //CGContextTranslateCTM(context, 50, 50); //C //CGContextRotateCTM(context, M_PI_4); //B //CGContextTranslateCTM(context, -50, -50); //A CGContextConcatCTM(context, transform); /** * 繪製矩形 */ CGContextFillRect(context, CGRectMake(40, 45, 20, 10)); CGContextRestoreGState(context); }
2.2.2 繪製圖片,下面函數在工作線程裏面很有用
void drawImage(CGContextRef context, CGImageRef image , CGRect rect){ /** * 注意變換順序A->B->C->D */ CGContextSaveGState(context); /** * 矩形回到起始位置 */ CGContextTranslateCTM(context, rect.origin.x, rect.origin.y); //D /** * 矩形Y軸正方向平移rect.size.height */ CGContextTranslateCTM(context, 0, rect.size.height); //C /** * 垂直反轉矩形 */ CGContextScaleCTM(context, 1.0, -1.0); //B /** * 矩形平移到原點 */ CGContextTranslateCTM(context, -rect.origin.x, -rect.origin.y);//A /** * 繪製圖片 */ CGContextDrawImage(context, rect, image); CGContextRestoreGState(context); }
2.2.3 座標軸變換
/** * 原座標系爲Quartz 2D,目標座標系爲UKit,用原座標系中座標繪圖 */- (void)drawRect:(CGRect)rect{ //UKit座標系 CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSaveGState(context); CGRect bounds = self.bounds; /** * 座標軸變換A->B */ /** * 平移座標軸 */ CGContextTranslateCTM(context, 0, bounds.size.height); // B /** * 翻轉Y座標軸 */ CGContextScaleCTM(context, 1, -1); //A /** * 繪製矩形 */ CGContextFillRect(context, CGRectMake(10, 10, 20, 20)); CGContextRestoreGState(context);}