基本概念
错切:实际上是平面景物在投影平面上的非垂直投影。错切使图像中的图形产生扭变。
仿射变换:仿射变换是 线性变换(缩放,旋转,错切) 和 平移变换(平移) 的复合。
Matrix :是一个3维矩阵,最根本的作用就是坐标转换(属于仿射变换)。
基本变换有4种: 平移(translate)、缩放(scale)、旋转(rotate) 和 错切(skew)。
缩放
用矩阵表示:
你可能注意到了,我们坐标多了一个1,这是使用了齐次坐标系的缘故,在数学中我们的点和向量都是这样表示的(x, y),两者看起来一样,计算机无法区分,为此让计算机也可以区分它们,增加了一个标志位,增加之后看起来是这样:
(x, y, 1) - 点
(x, y, 0) - 向量
另外,齐次坐标具有等比的性质,(2,3,1)、(4,6,2)…(2N,3N,N)表示的均是(2,3)这一个点。(将MPERSP_2解释为scale这一误解就源于此)。
图例:
错切(Skew)
错切存在两种特殊错切,水平错切(平行X轴)和垂直错切(平行Y轴)。
水平错切
用矩阵表示:
图例:
垂直错切
用矩阵表示:
图例:
复合错切
水平错切和垂直错切的复合。
用矩阵表示:
图例:
旋转(Rotate)
假定一个点 A(x0, y0) ,距离原点距离为 r, 与水平轴夹角为 α 度, 绕原点旋转 θ 度, 旋转后为点 B(x, y) 如下:
用矩阵表示:
图例:
平移(Translate)
此处也是使用齐次坐标的优点体现之一,实际上前面的三个操作使用 2x2 的矩阵也能满足需求,但是使用 2x2 的矩阵,无法将平移操作加入其中,而将坐标扩展为齐次坐标后,将矩阵扩展为 3x3 就可以将算法统一,四种算法均可以使用矩阵乘法完成。
用矩阵表示:
图例:
Matrix复合原理
Matrix的多种复合操作都是使用矩阵乘法实现的,从原理上理解很简单,但是,使用矩阵乘法也有其弱点,后面的操作可能会影响到前面到操作,所以在构造Matrix时顺序很重要。
我们常用的四大变换操作,每一种操作在Matrix均有三类,前乘(pre),后乘(post)和设置(set)。
前乘(pre)
前乘相当于矩阵的右乘:
这表示一个矩阵与一个特殊矩阵前乘后构造出结果矩阵。
后乘(post)
后乘相当于矩阵的左乘:
这表示一个矩阵与一个特殊矩阵后乘后构造出结果矩阵。
设置(set)
设置使用的不是矩阵乘法,而是直接覆盖掉原来的数值,所以,使用设置可能会导致之前的操作失效。
组合
组合操作构造Matrix时,个人建议尽量全部使用后乘或者全部使用前乘,这样操作顺序容易确定,出现问题也比较容易排查。
当然,由于矩阵乘法不满足交换律,前乘和后乘的结果是不同的,使用时应结合具体情景分析使用。
Pre与Post的区别
主要区别其实就是矩阵的乘法顺序不同,pre相当于矩阵的右乘,而post相当于矩阵的左乘。
在实际操作中,我们每一步操作都会得出准确的计算结果,但是为什么还会用存在先后的说法? 难道真的能够用pre和post影响计算顺序? 实则不然,下面我们用一个例子说明:
Matrix matrix = new Matrix();
matrix.postScale(0.5f, 0.8f);
matrix.preTranslate(1000, 1000);
Log.e(TAG, "MatrixTest:3" + matrix.toShortString());
在上面的操作中,如果按照正常的思路,先缩放,后平移,缩放操作执行在前,不会影响到后续的平移操作,但是执行结果却发现平移距离变成了(500, 800)。
在上面例子中,计算顺序是没有问题的,先计算的缩放,然后计算的平移,而缩放影响到平移则是因为前一步缩放后的结果矩阵右乘了平移矩阵,这是符合矩阵乘法的运算规律的,也就是说缩放操作虽然在前却影响到了平移操作,相当于先执行了平移操作,然后执行的缩放操作,因此才有pre操作会先执行,而post操作会后执行这一说法。
下面我们用不同对方式来构造一个矩阵:
假设我们需要先缩放再平移。
注意:
1.由于矩阵乘法不满足交换律,请保证使用初始矩阵(Initial Matrix),否则可能导致运算结果不同。
2.注意构造顺序,顺序是会影响结果的。
3.Initial Matrix是指new出来的新矩阵,或者reset后的矩阵,是一个单位矩阵。
1.仅用pre:
Matrix m = new Matrix();
m.reset();
m.preTranslate(tx, ty); //使用pre,越靠后越先执行。
m.preScale(sx, sy);
用矩阵表示:
2.仅用post:
Matrix m = new Matrix();
m.reset();
m.postScale(sx, sy); //使用post,越靠前越先执行。
m.postTranslate(tx, ty);
用矩阵表示:
3.混合:
Matrix m = new Matrix();
m.reset();
m.preScale(sx, sy);
m.postTranslate(tx, ty);
或
Matrix m = new Matrix();
m.reset();
m.postTranslate(tx, ty);
m.preScale(sx, sy);
由于此处只有两步操作,且指定了先后,所以代码上交换并不会影响结果。
用矩阵表示:
注意: 由于矩阵乘法不满足交换律,请保证初始矩阵为空,如果初始矩阵不为空,则导致运算结果不同。
Matrix方法表
方法类 | 相关API | 摘要 |
---|---|---|
基本方法 | equals hashCode toString toShortString | 比较、 获取哈希值、 转换为字符串 |
数值操作 | set reset setValues getValues | 设置、 重置、 设置数值、 获取数值 |
数值计算 | mapPoints mapRadius mapRect mapVectors | 计算变换后的数值 |
设置(set) | setConcat setRotate setScale setSkew setTranslate | 设置变换、 |
前乘(pre) | preConcat preRotate preScale preSkew preTranslate | 前乘变换 |
后乘(post) | postConcat postRotate postScale postSkew postTranslate | 后乘变换 |
特殊方法 | setPolyToPoly setRectToRect rectStaysRect setSinCos | 一些特殊操作 |
矩阵相关 | invert isAffine isIdentity | 求逆矩阵、 是否为仿射矩阵、 是否为单位矩阵 … |
参考
[1]Matrix原理
[2] Android 手势检测实战 打造支持缩放平移的图片预览效果(上)