仿射变换
在几何上定义为两个向量空间之间的一个仿射变换或者仿射映射由一个线性变换接上一个平移组成。
原理
在有限维的情况,每个仿射变换可以由一个矩阵A和一个向量b给出,它可以写作A和一个附加的列b。一个仿射变换对应于一个矩阵和一个向量的乘法,而仿射变换的复合对应于普通的矩阵乘法,只要加入一个额外的行到矩阵的底下,这一行全部是0除了最右边是一个1,而列向量的底下要加上一个1。
AffineTransform类描述了一种
二维仿
射变换的功能,它是一种二维坐标到二维坐标之间的
线性变换
,保持二维图形的“平直性”(译注: straightness,即变换后直线还是直线不会打弯,圆弧还是圆弧)和“平行性”(译注:parallelness,其实是指保二维图形间的相对位置关系不变,
平行线
还是平行线,而直线上点的位置顺序不变,另特别注意向量间夹角可能会发生变化。)仿射变换可以通过一系列的原子变换的复合来实现,包括:平移(Translation)、缩放(Scale)、翻转(Flip)、旋转(Rotation)和错切(Shear)。
此类变换可以用一个3×3的矩阵来表示,其最后一行为(0, 0, 1)。该
变换矩阵
将原坐标(x, y)变换为新坐标(x', y'),这里原坐标和新坐标皆视为最末一行为(1)的三维列向量,原列向量左乘变换矩阵得到新的列向量:
仿射流程:
几种典型的仿射变换:
1、平移变换
public static
AffineTransform
getTranslateInstance(double tx, double ty)
将每一点移动到(x+tx, y+ty),变换矩阵为:
[ 1 0 tx]
[ 0 1 ty]
[ 0 0 1]
(译注:平移变换是一种“
刚体
变换”,rigid-body transformation,中学学过的物理,都知道啥叫"刚体"吧,就是不会产生形变的理想物体,平移当然不会改变二维图形的形状。同理,下面的"
旋转变换"
也是刚体变换,而"缩放"、"错切"都是会改变图形形状的。)
2、缩放变换
public static AffineTransform getScaleInstance(double sx, double sy)
将每一点的横坐标放大(缩小)至sx倍,纵坐标放大(缩小)至sy倍,变换矩阵为:
[sx 0 0]
[ 0 sy 0]
[ 0 0 1]
当sx=sy时,称为尺度缩放,sx不等于sy时,这就是我们平时所说的拉伸变换。
3、
剪切变换
public static AffineTransform getShearInstance(double shx, double shy)
变换矩阵为:
[ 1 shx 0]
[shy 1 0]
[ 0 0 1]
相当于一个横向剪切与一个纵向剪切的复合
[ 1 0 0] [ 1 shx 0]
[shy 1 0] [ 0 1 0]
[ 0 0 1] [ 0 0 1]
4、旋转变换一
public static AffineTransform getRotateInstance(double theta)
目标图形围绕原点逆时针旋转theta弧度,变换矩阵为:
[cos(theta) -sin(theta) 0]
[sin(theta) cos(theta) 0]
[ 0 0 1]
5、
旋转变换二
public static AffineTransform getRotateInstance(double theta, double x, double y)
目标图形以(x, y)为轴心逆时针旋转theta弧度,变换矩阵为:
[cos(theta) -sin(theta) x-x*cos+y*sin]
[sin(theta) cos(theta) y-x*sin-y*cos]
[ 0 0 1]
相当于两次平移变换与一次原点
旋转变换
的复合:
[1 0 x] [cos(theta) -sin(theta) 0] [1 0 -x]
[0 1 y] [sin(theta) cos(theta) 0] [0 1 -y]
[0 0 1] [ 0 0 1] [0 0 1]
这里是以空间任一点为圆心旋转的情况。
CGAffineTransformMake(a,b,c,d,tx,ty) 矩阵运算的原理
- CGAffineTransformMake(a,b,c,d,tx,ty)
ad缩放bc旋转tx,ty位移,基础的2D矩阵
公式
x=ax+cy+tx
y=bx+dy+ty
矩阵的基本知识:
struct CGAffineTransform
{
CGFloat a, b, c, d;
CGFloat tx, ty;
};
CGAffineTransform CGAffineTransformMake (CGFloat a,CGFloat b,CGFloat c,CGFloat d,CGFloat tx,CGFloat ty);
为了把二维图形的变化统一在一个坐标系里,引入了齐次坐标的概念,即把一个图形用一个三维矩阵表示,其中第三列总是(0,0,1),用来作为坐标系的标准。所以所有的变化都由前两列完成。
以上参数在矩阵中的表示为:
|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|
通过矩阵运算后的坐标[aX + cY + tx bX + dY + ty 1],我们对比一下可知:
第一种:设a=d=1, b=c=0.
[aX + cY + tx bX + dY + ty 1] = [X + tx Y + ty 1];
可见,这个时候,坐标是按照向量(tx,ty)进行平移,其实这也就是函数 CGAffineTransform CGAffineMakeTranslation(CGFloat tx,CGFloat ty)的计算原理。
第二种:设b=c=tx=ty=0.
[aX + cY + tx bX + dY + ty 1] = [aX dY 1];
可见,这个时候,坐标X按照a进行缩放,Y按照d进行缩放,a,d就是X,Y的比例系数,其实这也就是函数 CGAffineTransform CGAffineTransformMakeScale(CGFloat sx, CGFloat sy)的计算原理。a对应于sx,d对应于sy。
第三种:设tx=ty=0,a=cosɵ,b=sinɵ,c=-sinɵ,d=cosɵ。
[aX + cY + tx bX + dY + ty 1] = [Xcosɵ - Ysinɵ Xsinɵ + Ycosɵ 1] ;
可见,这个时候,ɵ就是旋转的角度,逆时针为正,顺时针为负。其实这也就是函数 CGAffineTransform CGAffineTransformMakeRotation(CGFloat angle)的计算原理。angle即ɵ的弧度表示。
类的介绍
typedef struct CGAffineTransform CGAffineTransform;
struct CGAffineTransform {
CGFloat a, b, c, d;
CGFloat tx, ty;
};
};
/* The identity transform: [ 1 0 0 1 0 0 ]. */
CG_EXTERN const CGAffineTransform
CGAffineTransformIdentity
CG_AVAILABLE_STARTING
(__MAC_10_0, __IPHONE_2_0);
线性代数里面讲的矩阵变换,这个是恒等变换
,当你改变过一个view.transform属性或者view.layer.transform的时候需要恢复默认状态的话,记得先把他们重置可以使用view.transform = CGAffineTransformIdentity,或者view.layer.transform = CATransform3DIdentity
/* Return the transform [ a b c d tx ty ]. */
CG_EXTERN CGAffineTransform
CGAffineTransformMake(CGFloat a, CGFloat b, CGFloat c, CGFloat d, CGFloat tx, CGFloat ty)
CG_AVAILABLE_STARTING
(__MAC_10_0, __IPHONE_2_0);
返回一个transform对象
/* Return a transform which translates by `(tx, ty)’: t' = [ 1 0 0 1 tx ty ] */
CG_EXTERN CGAffineTransform
CGAffineTransformMakeTranslation(CGFloat tx, CGFloat ty)
CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
移动,tx,ty横纵坐标的更改数值
/* Return a transform which scales by `(sx, sy)’: t' = [ sx 0 0 sy 0 0 ] */
CG_EXTERN CGAffineTransform
CGAffineTransformMakeScale(CGFloat sx, CGFloat sy)
CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
缩放,sx、sy为缩放比例
/* Return a transform which rotates by `angle' radians: t' = [ cos(angle) sin(angle) -sin(angle) cos(angle) 0 0 ] */
CG_EXTERN CGAffineTransform
CGAffineTransformMakeRotation(CGFloat angle)
CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
旋转,angle为旋转角度
/* Return true if `t' is the identity transform, false otherwise. */
CG_EXTERN bool
CGAffineTransformIsIdentity(CGAffineTransform t)
CG_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_2_0);
当调用者是一个[1 0 0 1 0 0]对象返回true,否则为false
/* Translate `t' by `(tx, ty)' and return the result:
t' = [ 1 0 0 1 tx ty ] * t */
CG_EXTERN CGAffineTransform
CGAffineTransformTranslate(CGAffineTransform t, CGFloat tx, CGFloat ty)
CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
移动,在已有的对象t上进行动画。返回一个CGAffineTransform对象。tx、ty分别为横纵坐标移动量
/* Scale `t' by `(sx, sy)' and return the result:
t' = [ sx 0 0 sy 0 0 ] * t */
CG_EXTERN CGAffineTransform
CGAffineTransformScale(CGAffineTransform t, CGFloat sx, CGFloat sy)
CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
缩放,在已有的对象t上进行动画。返回一个CGAffineTransform对象。sx、sy是长宽缩放比
/* Rotate `t' by `angle' radians and return the result:
t' = [ cos(angle) sin(angle) -sin(angle) cos(angle) 0 0 ] * t */
CG_EXTERN CGAffineTransform
CGAffineTransformRotate(CGAffineTransform t, CGFloat angle)
CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
旋转,在已有的对象t上进行动画。返回一个CGAffineTransform对象。angle是旋转角度
/* Invert `t' and return the result. If `t' has zero determinant, then `t'
is returned unchanged. */
CG_EXTERN CGAffineTransform
CGAffineTransformInvert(CGAffineTransform t)
CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
反转,在已有的对象t上进行动画。返回一个CGAffineTransform对象。
/* Concatenate `t2' to `t1' and return the result:
t' = t1 * t2 */
CG_EXTERN CGAffineTransform
CGAffineTransformConcat(CGAffineTransform t1, CGAffineTransform t2)
CG_AVAILABLE_STARTING
(__MAC_10_0, __IPHONE_2_0);
连接,在已有的对象t1、t2上进行动画。
/* Return true if `t1' and `t2' are equal, false otherwise. */
CG_EXTERN bool
CGAffineTransformEqualToTransform(CGAffineTransform t1,CGAffineTransform t2)
CG_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_2_0);
判断两个对象是否相等。
/* Transform `point' by `t' and return the result:
p' = p * t
p' = p * t
where p = [ x y 1 ]. */
CG_EXTERN CGPoint
CGPointApplyAffineTransform(CGPoint point,CGAffineTransform t)
CG_AVAILABLE_STARTING
(__MAC_10_0, __IPHONE_2_0);
对CGPoint对象进行矩阵运算。 [t.a t.b 0]
[p.x p.y 1] [t.c t.d 0] = p—>(p.x*t.a + p.y*t.c + 1*t.tx, p.x*t.b + p.y*t.d + 1*t.ty)
[t.tx t.ty 1]
/* Transform `size' by `t' and return the result:
s' = s * t
s' = s * t
where s = [ width height 0 ]. */
CG_EXTERN CGSize
CGSizeApplyAffineTransform(CGSize size, CGAffineTransform t)
CG_AVAILABLE_STARTING
(__MAC_10_0, __IPHONE_2_0);
对CGSize对象进行矩阵运算。 [t.a t.b 0]
[s.w s.h 0] [t.c t.d 0] = s—>(s.w*t.a + s.h*t.c, s.w*t.b + s.h*t.d)
[t.tx t.ty 1]
/* Transform `rect' by `t' and return the result. Since affine transforms do
not preserve rectangles in general, this function returns the smallest
rectangle which contains the transformed corner points of `rect'. If `t'
consists solely of scales, flips and translations, then the returned
rectangle coincides with the rectangle constructed from the four
transformed corners. */
not preserve rectangles in general, this function returns the smallest
rectangle which contains the transformed corner points of `rect'. If `t'
consists solely of scales, flips and translations, then the returned
rectangle coincides with the rectangle constructed from the four
transformed corners. */
CG_EXTERN CGRect
CGRectApplyAffineTransform(CGRect rect, CGAffineTransform t)
CG_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_2_0);
对CGRect对象进行矩阵运算,分别拆分成CGPoint对象和CGSize对象。
方法底层定义:
__CGAffineTransformMake(CGFloat a, CGFloat b, CGFloat c, CGFloat d, CGFloat tx, CGFloat ty)
{
CGAffineTransform t;
t.a = a; t.b = b; t.c = c; t.d = d; t.tx = tx; t.ty = ty;
return t;
}
__CGPointApplyAffineTransform(CGPoint point, CGAffineTransform t)
{
CGPoint p;
p.x = (CGFloat)((double)t.a * point.x + (double)t.c * point.y + t.tx);
p.y = (CGFloat)((double)t.b * point.x + (double)t.d * point.y + t.ty);
return p;
}
__CGSizeApplyAffineTransform(CGSize size, CGAffineTransform t)
{
CGSize s;
s.width = (CGFloat)((double)t.a * size.width + (double)t.c * size.height);
s.height = (CGFloat)((double)t.b * size.width + (double)t.d * size.height);
return s;
}