Matrix 学习记录

写在前面:
本文主要通过学习安卓自定义View进阶-Matrix原理这篇文章去学习Matrix 的原理,因此大部分文字出自这篇文章,中间穿插了部分文字是学习中遇到的问题以及解释,并且稍微改变了一下排版,利于本人理解。

首先来看Matrix的定义。
Matrix
官方是这样描述的:

The Matrix class holds a 3x3 matrix for transforming coordinates.

机翻后意思是 Matrix类 拥有一个用于 转换坐标3x3 矩阵

重点:

1. 3x3 矩阵

2.作用是 转换坐标

1.先来看3x3 矩阵

官方文档中定义Constants如下。

MPERSP_0,Constant Value: 6
MPERSP_1,Constant Value: 7
MPERSP_2,Constant Value: 8
MSCALE_X,Constant Value: 0
MSCALE_Y,Constant Value: 4
MSKEW_X,Constant Value: 1
MSKEW_Y,Constant Value: 3
MTRANS_X,Constant Value: 2
MTRANS_Y,Constant Value: 5

上面的定义可能不是很好理解,下面的图其实更简单明了。3x3矩阵定义如下所示:

https://www.gcssloop.com/customview/Matrix_Basic

2.转换坐标

Matrix功能是 转换坐标 ,那么为什么需要转换坐标 呢? 举一个简单的例子:

以下文字出处:https://www.gcssloop.com/customview/Matrix_Basic
我的的手机屏幕作为物理设备,其物理坐标系是从左上角开始的,但我们在开发的时候通常不会使用这一坐标系,而是使用内容区的坐标系。
以下图为例,我们的内容区和屏幕坐标系还相差一个通知栏加一个标题栏的距离,所以两者是不重合的,我们在内容区的坐标系中的内容最终绘制的时候肯定要转换为实际的物理坐标系来绘制,Matrix在此处的作用就是转换这些数值。
假设通知栏高度为20像素,导航栏高度为40像素,那么我们在内容区的(0,0)位置绘制一个点,最终就要转化为在实际坐标系中的(0,60)位置绘制一个点。

https://www.gcssloop.com/customview/Matrix_Basic

以上是仅作为一个简单的示例,实际上不论2D还是3D,我们要将图形显示在屏幕上,都离不开Matrix,所以说Matrix是一个在背后辛勤工作的劳模。
Matrix特点:
1.作用范围更广,Matrix在View,图片,动画效果等各个方面均有运用,相比与之前讲解等画布操作应用范围更广。
2.更加灵活,画布操作是对Matrix的封装,Matrix作为更接近底层的东西,必然要比画布操作更加灵活。
3.封装很好,Matrix本身对各个方法就做了很好的封装,让开发者可以很方便的操作Matrix。
4.难以深入理解,很难理解中各个数值的意义,以及操作规律,如果不了解矩阵,也很难理解前乘,后乘。

简单了解了Matrix的定义后,需要看Matrix变换坐标的原理了。但是,在看原理之前,首先要了解Matrix变换坐标的四种基本类型。

以下文字出处:https://www.gcssloop.com/customview/Matrix_Basic
首先,我们所用到的变换 属于 仿射变换,仿射变换是 线性变换(缩放,旋转,错切) 和 平移变换(平移) 的复合。
这里 Matrix基本变换有 4种
1.缩放(scale)
2.错切(skew)
3.旋转(rotate)
4.平移(translate)
下面我们看一下四种变换都是由哪些参数控制的。


下面的知识点需要用到矩阵乘法,可以参考这里,简单复习一下矩阵乘法。
参考链接:
https://www.cnblogs.com/alantu2018/p/8528299.html
https://baike.baidu.com/item/%E7%9F%A9%E9%98%B5%E4%B9%98%E6%B3%95/5446029?fr=aladdin

以下大部分文字出自这篇文章:https://www.gcssloop.com/customview/Matrix_Basic

下面来看使用Matrix变换的原理。

1.缩放(Scale)
首先,给出一个简单的缩放例子。坐标(200,200)的点,缩放到(100,70)。



用线性方程式表示缩放的话,如下所示:



用矩阵表示:

你可能注意到了,我们坐标多了一个1,这是使用了齐次坐标系的缘故,在数学中我们的点和向量都是这样表示的(x, y),两者看起来一样,计算机无法区分,为此让计算机也可以区分它们,增加了一个标志位,增加之后看起来是这样:

(x, y, 1) - 点
(x, y, 0) - 向量
另外,齐次坐标具有等比的性质,(2,3,1)、(4,6,2)…(2N,3N,N)表示的均是(2,3)这一个点。(将MPERSP_2解释为scale这一误解就源于此)。
图例:


1.jpg

2.错切(Skew)
错切存在两种特殊错切,水平错切(平行X轴)和垂直错切(平行Y轴)。
首先看,什么叫错切。

在某方向上,按照一定的比例对图形的每个点到某条平行于该方向的直线的有向距离做放缩得到的平面图形。
参考链接:
https://baike.baidu.com/item/%E9%94%99%E5%88%87/7024094?fr=aladdin

这句话其实是很难看懂的,具体的图形结合公式更容易理解。

2.1 水平错切
用线性方程式表示:

image.png

用矩阵表示:
2.png


水平错切综合来看,图形由正方形变成了四边形,但是图形的y轴坐标点没有发生变化,简单看四个顶点的y坐标始终是跟之前一致,发生变化的只有x轴坐标。扩大范围去看整体,所有对应线条的y坐标也是没有变化的。
水平错切:x坐标改变。
2.2 垂直错切
用线性方程式表示:
image.png

用矩阵表示:

图例:
1.jpg

垂直错切与水平错切正好相反,图形的x坐标没有发生改变,y坐标发生了变化。
垂直错切:y坐标改变。

2.3 复合错切
水平错切和垂直错切的复合。
用线性方程式表示:

image.png

用矩阵表示:
png.png

图例:
2.jpg

复合错切:x、y坐标都改变。

一个简单的应用:
Android学习笔记进阶十之Matrix错切变换

3.旋转(Rotate)
假定一个点 A(x0, y0) ,距离原点距离为 r, 与水平轴夹角为 α 度, 绕原点旋转 θ 度, 旋转后为点 B(x, y) 。
用线性方程式表示:



image.png

用矩阵表示:


1.png

图例:
2.jpg

旋转相对错切应该算是用的比较多的了,而且也相对容易理解。
4.平移(Translate)

此处也是使用齐次坐标的优点体现之一,实际上前面的三个操作使用 2x2 的矩阵也能满足需求,但是使用 2x2 的矩阵,无法将平移操作加入其中,而将坐标扩展为齐次坐标后,将矩阵扩展为 3x3 就可以将算法统一,四种算法均可以使用矩阵乘法完成。

我觉得想出这种做法的人。。数学真好。

用线性方程式表示:


用矩阵表示:
1.png

图例:

这里定义了2个点:起点A(x0,y0),终点B(x,y)。
△x = x - x0,△y = y - y0。

其实看到后面,发现这4种变换的定义,都是一个套路。
x = k1 * x0 + xxx
y = k2 * y0 + xxx
到此Matrix 的基本原理已经有了一定的了解。下面来看Matrix 的复合操作。

Matrix 复合操作

首先,要知道什么是Matrix 复合操作。
前面,我们了解到了基本的常用矩阵变换操作有4种:平移、缩放、旋转、斜切。


https://www.cnblogs.com/xxr1/p/7482309.html

每种变换都对应一个变换矩阵,通过矩阵乘法,可以把多个变换矩阵相乘得到复合变换矩阵
简单来说,应该就是2个或2个以上的Matrix基本变换组合而成的操作吧。

https://www.cnblogs.com/xxr1/p/7482309.html

针对基本4种操作,每一种操作在Matrix类中均有三类,前乘(pre),后乘(post)和设置(set)这3个函数。

post
pre
set

使用矩阵乘法也有其弱点,后面的操作可能会影响到前面到操作,所以在构造Matrix时顺序很重要。

由于函数太多,所以通过

postConcat(Matrix other)
preConcat(Matrix other)
setConcat(Matrix a, Matrix b)

这3个去简单理解pre、post、set。
preConcat (Matrix other)
Preconcats the matrix with the specified matrix. M' = M * other
前乘

postConcat(Matrix other)
Postconcats the matrix with the specified matrix. M' = other * M
后乘

setConcat (Matrix a, Matrix b)
Set the matrix to the concatenation of the two specified matrices and return true.Either of the two matrices may also be the target matrix, that is matrixA.setConcat(matrixA, matrixB); is valid.
设置
设置使用的不是矩阵乘法,而是直接覆盖掉原来的数值。set用于设置单位矩阵中的值。我们通过new Matrix()得到的是一个单位矩阵,后续的矩阵变换都是针对这个单位矩阵进行变换。如Matrix.setRotate(90)、Matrix.setTranslate(10,20)等。

关于pre 和post,以前存在2个错误的理论。

错误结论一:pre 是顺序执行,post 是逆序执行。
错误结论二:pre 是先执行,而 post 是后执行。

现在这个理论应该很少见。其实看函数定义就知道了。总之,记住一点,不要去管什么先后论,顺序论,就按照最基本的矩阵乘法理解。

使用 pre 和 post

在构造 Matrix 时,尽量使用一种乘法,前乘或者后乘,这样操作顺序容易确定,出现问题也比较容易排查。当然,由于矩阵乘法不满足交换律,前乘和后乘的结果是不同的,使用时应结合具体情景分析使用。
下面我们用不同对方式来构造一个相同的矩阵:
注意:
1.由于矩阵乘法不满足交换律,请保证使用初始矩阵(Initial Matrix),否则可能导致运算结果不同。
2.注意构造顺序,顺序是会影响结果的。
3.Initial Matrix是指new出来的新矩阵,或者reset后的矩阵,是一个单位矩阵。

其实我觉得,前乘是初始M在前,函数的形参
后乘是初始M在后。

1.仅用pre:

// 使用pre, M' = M*T*S = T*S
Matrix m = new Matrix();
m.reset();
m.preTranslate(tx, ty); 
m.preScale(sx, sy);

上面的代码,用矩阵表示:



2.仅用post:

// 使用post, M‘ = T*S*M = T*S
Matrix m = new Matrix();
m.reset();
m.postScale(sx, sy);  //,越靠前越先执行。
m.postTranslate(tx, ty);

上面的代码,用矩阵表示:



3.pre和post混合:

// 混合 M‘ = T*M*S = T*S
Matrix m = new Matrix();
m.reset();
m.preScale(sx, sy);  
m.postTranslate(tx, ty);
// 混合 M‘ = T*M*S = T*S
Matrix m = new Matrix();
m.reset();
m.postTranslate(tx, ty);
m.preScale(sx, sy);  

由于此处只有两步操作,且指定了先后,所以代码上交换并不会影响结果。
用矩阵表示:


1.png

前面的部分其实我稍微有一个疑惑,为什么T*M = T
其实是因为还设计到一个知识点:IDENTITY_MATRIX(单位矩阵)
单位矩阵I和矩阵M相乘,得到的结果还是M。所以有 I*M= M*I=M
据这篇文章 Matrix源码解析 所说
Matrix的构造方法:

   /**
     * Create an identity matrix
     */
    public Matrix() {
        native_instance = native_create(0);
    }

构造出的矩阵长这样:


https://www.jianshu.com/p/dbfae6f27173

即一个单位矩阵。
因此,这种情况下pre和post操作的结果是一样的。
需要说明的是,虽然看上去pre和post操作可以写成矩阵链相乘的形式,但是实际上还是按照出现的先后顺序计算的。

Matrix原理部分内容就到这里为止了,下面是api的使用,由于这部分较为容易理解就不再重复,直接看就好。
安卓自定义View进阶-Matrix详解
https://www.gcssloop.com/customview/Matrix_Method

参考链接:
安卓自定义View进阶-Matrix原理
浅谈矩阵变换——Matrix
Matrix源码解析

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342