加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

理解FLASH 8中的矩阵变换

发布时间:2020-12-15 17:38:28 所属栏目:百科 来源:网络整理
导读:原文网址:http://www.senocular.com/flash/tutorials/transformmatrix/ 以下是译文。 简介: Flash 8在Flash开发中,给Flash开发者带来了一个新的,令人兴奋的高度。现在开发者不仅仅能在Flash工具中中流畅的操作bitmaps,还能通过图片的变换矩阵,完整的控


原文网址:http://www.senocular.com/flash/tutorials/transformmatrix/

以下是译文。


简介:

Flash 8在Flash开发中,给Flash开发者带来了一个新的,令人兴奋的高度。现在开发者不仅仅能在Flash工具中中流畅的操作bitmaps,还能通过图片的变换矩阵,完整的控制图片的转换。这在Flash早期的版本中是不可能的。因此在早期,就可以实现特定的变换矩阵,例如矩阵的切变。如果你用ActionScript去实现的话,会经过复杂的数学包括嵌套的图片。而就当前的改进而言,这些都已经是历史了。


矩阵与矩阵变换:

在了解矩阵是如何变换之前,我们先理解什么是矩阵。一个矩阵是一个由几行几列数字组成的矩形数组(或者表)。一个由m行、n列组成的矩阵就是所知的m x n矩阵。这代表了矩阵的维度。常见的矩阵都是在行与列中包含一些数字,并且被两个巨大的中括号包起来。


矩阵有许多不同的用途,通常用来存储数据或者用特定的矩阵计算解决问题。Flash用矩阵定义仿射变换。

仿射变换就是在变换坐标系中,保持共线性和相对距离的变换。这就意味着在坐标系空间上,假如原先是在一条线上的点,经过仿射变换,还将继续在一条线上。同时,在平行线上的点继续是平行线,通过缩放,那些相对的空间和距离会一直保持在一个特定的比例。仿射变换允许重新定位,缩放,切变,旋转。不过,他们不能做椎体,扭曲,透视。如果你曾经做过Flash里面的符号变换,你应该会记得仿射变换的这些性质。


在Flash中的变换矩阵,用3行3列的3X3矩阵定义了这些放射变换。在矩阵中值的位置被标明为a、b、c、d、tx、ty、u、v还有w。在矩阵中,他们被命名为元素。


这些a、b、c、d、tx还有ty是矩阵变换中最重要的元素。其他的位置,u、v还有w虽然不重要,但必须存在在矩阵中。在Flash中,a、b、c、d、tx还有ty都对应一中特定的变换。

a - x轴 缩放?


b - y轴 切变?


c - x轴 切变?


d - y轴 缩放?


tx - x轴 平移?


ty - y轴 平移

?


而u、v还有w的位置的值,将分别以的静态的值0、0、1保留下来。


?注意:

在Flash的矩阵类中,u、v、w是不会像a、b、c、d、tx、ty元素那样可以修改的。

这些在以上观点中,a、b、c、d、tx、ty作为有效的元素或者属性。他们会在矩阵变换的公式中作为参数,求解新的x、y的值,这些变换公式如下:

x' = x*a + y*c + tx?
y' = x*b + y*d + ty


x和y代表原始的x和y所处的位置,而x'和y'(读成x-素和y-素)代表变换后的位置。这些公式是用来求解在图片或者图片中任意一个与矩阵变换相关联的点的新坐标位置的。

注意:

你也许会看到变换矩阵表示为:


或者类似的(注意u、v、w属性在底下,与右边相反)。这也是基础的相同矩阵,只是用了不同的元素朝向罢了。虽然,一些操作运算会因为这个不同采用不同的处理,但是变换在朝向上不会随这些不同而改变。最终的结果依然保持相同。

?在这篇文章书写的时候,Flash帮助文档,在这种方式下用c和b的调换,不正确的表示了一个矩阵的变换。


单位矩阵:

当一个矩阵运算的结果没有导致变换,那么这就是众所周知的单位矩阵。


如果,任意一个正方形矩阵?(行数与列数相等的矩阵叫正方形矩阵)从左上角到右下角的对角线上,值都是1(行坐标值与列坐标值相同的点),而非此对角线上的值都是0,这样的正方形矩阵称为单位矩阵。试想一下,假如用单位矩阵计算一个矩阵变换值的结果。

x' = x*1 + y*0 + 0
y' = x*0 + y*1 + 0


上面可以简化为:

x' = x

y' = y

发现没有,经过矩阵的计算,x和y的值没有发生变化。x'等于x,y'等于y。你可以把单位矩阵想象成一个数值上为1来缩放的矩阵的矩阵,或者常规的数值1。当你用1乘以任意数值,你会得到原来数值。相同的规则适用于单位矩阵变换。在Flash中,除了特定的,剩下的所有矩阵,都以单位矩阵开始--即,没有变换的矩阵。这包括参与图片的矩阵。如果一个图片最终没有改变或者变换,那么它的变换矩阵就是单位矩阵。


应用变换:

试想一下,假如一个单位矩阵的元素发生了变化。例如,Flash变换矩阵中,在一个以单位矩阵为开始的矩阵中,其中一个元素的值由1变为2,那么会发生什么呢?记住,单独的x缩放元素



x' = 2*x + 0*y + 0
y' = 0*x + 1*y + 0

x' = 2x
y' = y


你现在情况变成了,新的x等于旧的x的两倍;x' = 2x。经过以上的矩阵变换后,一个图片的宽度将是原来的两倍。


那假如,将b的值改为1会如何呢?


x' = 1*x + 1*y + 0?
y' = 0*x + 1*y + 0

x' = x + y?
y' = y


现在,你的新的x'不仅与旧x有关,还与y值有关,这就意味着,当y值变大,新的x'值也将跟着变大。这就导致x轴的切变由元素c来控制。


假如你想在任意一个方向上平移这个图片,你只要改变矩阵中tx和ty的值就可以了。


x' = 1*x + 1*y + 5?
y' = 0*x + 1*y + 10

x' = x + y + 5
y' = y + 10


变换的结果依然是切变。但是x和y的值相对原来的移动了5和10。

你也许会发现在矩阵中没有元素用来表示旋转。事实上,旋转混合缩放和切变的结果。通过缩放和切变的共同作用,可以扭曲坐标系,来达到旋转的效果。

在最简单的情况下,任何一个单一的旋转变换都可以用以下的矩阵来表示:


角度值表示变换中将要旋转的大小。

就变换矩阵而言,最难理解的就是旋转的概念。不仅仅是它不像缩放、切变那样有元素来表示,而且是旋转需要依赖四个其他的元素(缩放和切变)来实现。旋转的内部就已经很难理解了,更不用说在旋转之上改变那些元素的值,或者以旋转为基础的元素的值。如果,你非常熟悉二维空间的三角函数和旋转向量,那么接下来,你就会看到频繁的使用sine和cosine函数了。这些概念也使用。

假设有一个简单的例子,用于矩阵变换,它要求旋转30度。


x' = cosine(30)*x - sine(30)*y + 0
y' = sine(30)*x + cosine(30)*y + 0

x' = .87*x - .5*y
y' = .5*x + .87*y

假设已知点(4,0),旋转30度,大致的x、y值应当是(3.5,2)。


向公式传入 x = 4,y = 0:

x' = .87*4 - .5*0
y' = .5*4 + .87*0

x' = 3.46
y' = 2

下面体验一下图片的变换矩阵,看看变换是如何表现的。

矩阵演示


ActionScript的矩阵类:

ActionScript中的矩阵是存在Flash Matrix类的一个实例当中。这个类位于flash.geom 包中。假如你要创建你自己的矩阵类,你需要使用它的完全路径。

var my_matrix = new flash.geom.Matrix();
或者,你先引入该类,这样就可以直接使用“new Matrix”了

import flash.geom.Matrix;

var my_matrix = new Matrix();

矩阵的构造函数允许你在创建一个矩阵实例时,指定a、b、c、d、tx、ty的值。

import flash.geom.Matrix;

var my_matrix = new Matrix( a,b,c,d,tx,ty );

如果,没有指定参数,你就会得到一个单位矩阵。


矩阵中最主要的就是a、b、c、d、tx、ty。这些都是你在做矩阵变换时需要用到的属性。

Flash还提供了一些其他的函数来简化你的工作。其中一些常用的函数如下:

translate(tx:Number,ty:Number) : Void
scale(sx:Number,sy:Number) : Void
rotate(angle:Number) : Void
identity() : Void


在Flash帮助文档中还能找到矩阵类其他函数的说明和信息。因为切变不是典型的矩阵变换函数,所以你不一定能在矩阵类中找到函数skew()。可以通过调整矩阵中元素b和c,来生成一个切变函数。


以上提到的任意一个方法都是用来操作修改矩阵的;他们不会返回一个新的变换矩阵。这是一个基本概念。


矩阵平移

translate(tx:Number,ty:Number) : Void

到的任意一,移动的方法只是用tx和ty来移动位置。通过做加法运算,传入矩阵中的tx和ty,加到任意一个已经存在的值上。

my_matrix.translate( 5,10 );
与以下相同:

my_matrix.tx += 5;
my_matrix.ty += 10;

矩阵缩放

scale(sx:Number,sy:Number) : Void

函数缩放,通过sx的大小缩放变换矩阵中的x,通过sy的大小缩放变换矩阵中的y值。这些值是做乘法,所以数值1表示不缩放。用来缩放的值同样会改变矩阵中tx和ty的值,从而改变矩阵的位置。旋转与缩放的原理是一样的。

my_matrix.scale( 1.5,2 );
与一下相同:

my_matrix.a *= 1.5;
my_matrix.b *= 2;
my_matrix.c *= 1.5;
my_matrix.d *= 2;
my_matrix.tx *= 1.5;
my_matrix.ty *= 2;


矩阵旋转

rotate(angle:Number) : Void

函数旋转,通过指定角度的大小来旋转一个变换矩阵,参数角度指的是弧度值。相对于缩放,旋转不仅仅要修改元素a、b、c、d的值,还要修改tx和ty的值。你应该猜到,在旋转上,数学公式会更复杂一些。

my_matrix.rotate( Math.PI/4 );
与一下相同:

var sin = Math.sin( Math.PI/4 );
var cos = Math.cos( Math.PI/4 );
var a = my_matrix.a;
var b = my_matrix.b;
var c = my_matrix.c;
var d = my_matrix.d;
var tx = my_matrix.tx;
var ty = my_matrix.ty;
my_matrix.a = a*cos - b*sin;
my_matrix.b = a*sin + b*cos;
my_matrix.c = c*cos - d*sin;
my_matrix.d = c*sin + d*cos;
my_matrix.tx = tx*cos - ty*sin;
my_matrix.ty = tx*sin + ty*cos;


单位矩阵

identity() : Void

不管之前的变换,单位矩阵方法会将一个矩阵转化为单位矩阵。本质上而言,调用该方法会清除矩阵中所有的变换。

my_matrix.identity();
与一下相同:

my_matrix.a = 1;
my_matrix.b = 0;
my_matrix.c = 0;
my_matrix.d = 1;
my_matrix.tx = 0;
my_matrix.ty = 0;

动画裁剪矩阵

当做动画裁剪的时候,你要调用变换矩阵中的transform对象的matrix属性,来达到动画裁剪的目的。

var my_matrix = my_mc.transform.matrix;

这个矩阵实际上是拷贝了真正的矩阵,用作动画裁剪。这就意味着,假如你尝试修改直接修改矩阵的元素,他们将不会在动画裁剪的矩阵上反应出来。正确的方法是,你操作拷贝的矩阵,然后重新赋值到真正的transform对象的matrix属性上,来实现裁剪。

my_mc.transform.matrix.translate(5,0); // no change

var my_matrix = my_mc.transform.matrix;
my_matrix.translate(5,0);
my_mc.transform.matrix = my_matrix; // changes my_mc's position

体验一下下面的动画裁剪,看看调用动画裁剪的变换矩阵,来操作矩阵会有什么效果。

矩阵裁剪


?:

和其他的Flash8的类包一样。Matrix累也有方法clone(),用来方便你拷贝一个已经存在的矩阵。从动画裁剪的变换对象访问矩阵,是可以自动拷贝该矩阵的。

当一个动画裁剪的子动画裁剪有矩阵变换,并且父动画裁剪也有矩阵变换时。子动画裁剪的可视结果,将是他自己的矩阵变化和父矩阵变化的混合。


矩阵乘法

矩阵更像一堆混合的数字。你可以做一些基础的数学运算,例如加法、以及乘法。当处理矩阵变换时,乘法是尤为重要的一个运算。矩阵乘法是将定义在矩阵中的变换,应用起来。事实上,之前公式中提到的从矩阵中得到的x'和y'是,是点或者行向量的做矩阵乘法的结果。


一个行向量是一个 1 x n的矩阵,一行,n个值的矩阵。一个向量常代表一个二维或三位空间中的点。对于我们而言,一个向量就是一个由x、y和数字1组成的1 x 3的矩阵,代表二维空间中的一个点。



虽然,x、y值才是向量中重要的数值。数字1仍然像u、v、w一样,必须存在在变换矩阵中。

这样一个向量可以与一个变换矩阵相乘,让变换作用在一个已经提供的点上。然而,就矩阵相乘而言,它要求第一个做乘法的矩阵的列数必须与第二个做乘法的矩阵的行数相同。因为向量现在是1 x 3并且变换矩阵是3 x 3,这对于等式左边的行向量,它是满足以上条件的。


这个规定,是由矩阵相乘的法则决定的。在矩阵乘法中,最终的矩阵是一个m x n矩阵,m是第一个矩阵的行数,n是第二个矩阵的列数。这个规则同样让顺序变得重要了。例如,给出A和B矩阵,A * B 不一定等于 B * A。在一些情况下,这些情况是存在的,你会在后面看到这些情况。


结果矩阵中的每一个新值,都是对应的第一个矩阵的列值和第二个矩阵的行值做积,之后再做和的结果。例如,给出一个矩阵 * 向量,结果向量中第一个值会是:


x*a + y*c + 1*tx

每一列都做类似的运算,每一列可以表示为:


or


你在结果中发现没有?与之前类似计算 x' 和 y' 相同的公式.

x' = x*a + y*c + tx
y' = x*b + y*d + ty

注意:

当用一个修改朝向的矩阵时,在乘法运算上会稍有不同。不是使用行向量 * 矩阵,你会用 矩阵 * 列向量。 一个列向量就如同一个行向量,只是在书写上,列向量是m x 1的矩阵的形式。


运算最终的结果是一样的,只是它最终以列向量存在,而不是行向量。

向量一般用在矩阵乘法中,来求解做变换的点的新坐标。但,其他矩阵也会与矩阵做乘法,来创造一个新的变换矩阵,这个矩阵包含了被乘的矩阵的变换。


假设两个矩阵做乘法。



因为第一个矩阵的列数等于第二个矩阵的行数,所以可以做矩阵乘法。在这里也用了矩阵与向量相乘相同的方法,只是,这里还要计算第二个矩阵中其他的列数。


在每一行每一列做完乘法和加法之后,你会得到下面这个:


可以简化为:



你会发现,这与之前的3 x 3矩阵的风格是一样的,u、v、w的值是0,0,1.


我们现在可以将这个定理用在相同的例子上。这里有两个变换矩阵,一个缩放x为百分之两百,一个切变元素y为1。







在最终的变换矩阵中,切变元素相乘得到了2。

现在让我们见证一下,交换相乘矩阵的位置。你觉得结果会和上面一样吗?




事实上结果是不相同的。缩放不再改变切变的值,并且,仅仅是将其包含在内。

注释:

记住,任意一个矩阵与单位矩阵相乘,将会得到原来的矩阵。

不用担心太多的矩阵相乘会改变太多。你不一定需要知道矩阵是如何相乘的。事实上,Flash的矩阵类,已经向开发者提供了这些操作函数。既有点(方法transformPoint())也有矩阵(方法concat())的。也许,你根本不需要使用以上的矩阵乘法。不过,知道矩阵乘法是如何运作的,可以帮助开发者有一个更好的全局了解。

import flash.geom.Matrix;

var scale_matrix,skew_matrix;

scale_matrix = new Matrix(2,1,0);
skew_matrix = new Matrix(1,0);
skew_matrix.concat(scale_matrix);
trace(skew_matrix); // (a=2,b=0,c=2,d=1,tx=0,ty=0)

scale_matrix = new Matrix(2,0);
scale_matrix.concat(skew_matrix);
trace(scale_matrix); // (a=2,c=1,ty=0)
例如,平移,缩放,旋转和单位,连接同样会改变当前的矩阵实例,而不是返回一个新的矩阵。

对于Flash的点实例,可以用矩阵的方法transformPoint()与行向量相乘

import flash.geom.Matrix;
import flash.geom.Point;

var scale_matrix,original_point,scaled_point;

scale_matrix = new Matrix(2,0);
original_point = new Point(10,10);
scaled_point = scale_matrix.transformPoint(original_point);
trace(scaled_point); // (x=20,y=10)
注意,通过方法transformPoint(),得到了一个新的变换点。同样要注意的是,在Flash中使用方法concat()和方法transformPoint()做乘法的书写的顺序,与矩阵做相乘时是相反的。例如,给出矩阵A、B和点P:

A*B -> B.concat(A);
P*A -> A.transformPoint(P);

就父子图片的矩阵相乘,你可以这样:

Child.concat(Parent);


级联矩阵

假如,我们有两个图片,第一个是第二个的子变换。首先第一个,子图片,切变元素x为1。然后第二个,父图片,缩放x元素200%。子变换矩阵的最后时间线表现结果是既有缩放也有切变的图片。也可以用一个简单的剪切来实现,不用嵌套一个父矩阵变换。这样一个剪切的矩阵变换,等同于一个原始的子图片的变换矩阵乘以父图片的矩阵变换。



父图片的变换矩阵,是为了矩阵乘法,在以后改变它的子图片的变换的。记住,真实的与子图片关联的矩阵,是不包含这些变化的。真实矩阵,相对于图片,包括直接影响真实矩阵的变换,都是特别的。对于任意的父图片矩阵,或者其他的父图片矩阵都不会被涵盖在这里。然后,Flash在图片的所有父类都提供了的transform对象里面,以一个矩阵属性的形式,提供了所有的变换。这就是concatenatedMatrix属性。它表示作用于嵌套图片的,所有相关矩阵乘积的结果。

var parent_matrix = parent_mc.transform.matrix;
var child_matrix = parent_mc.child_mc.transform.matrix;
var childconcat_matrix = parent_mc.child_mc.transform.concatenatedMatrix;

child_matrix.concat(parent_matrix);

trace(child_matrix); // (a=2,ty=0)
trace(childconcat_matrix); // (a=2,ty=0)
你可以把concatenatedMatrix属性对于矩阵,看成globalToLocal对于点。它提供了匹配目标矩阵的级联变换的变换。使用一个级联矩阵,你可以在主时间线上改变一个图片,来匹配父图片有多种变换的嵌套图片的变换。


逆矩阵

你也可以移除在嵌套图片中所有的变换,让它看起来像似一点也没有变换过。然而,唯独连接矩阵,是没有提供这样的功能的。你就需要一个逆矩阵了。


存在一个独特的正方形矩阵,并且与当前矩阵的乘积为单位矩阵,就称该矩阵为逆矩阵。这两个矩阵都被看成可逆的,并且是彼此的逆矩阵。所有的Flash变换矩阵都是可逆的,这就是说,存在另外一个矩阵与这些矩阵的积为单位矩阵。假设,单位矩阵 I 和 矩阵 A,存在一个矩阵B,那么:

A * B = I

并且

B * A = I


手动计算一个逆矩阵是比较复杂的。它涉及找出矩阵的共轭矩阵,除以共轭矩阵的行列式。用我们的仿射变换矩阵处理起来是比较容易的,但是具体的过程,就已经高于当前的教程了。它可以简化为。

假设矩阵:


它的逆矩阵可以通过以下来找到:



通过矩阵的乘法,还有方法concat(),Flash中的Matrix类,可以更加轻松的找到逆矩阵。用方法invert(),你可以非常轻松的转化一个矩阵为它的逆矩阵。

import flash.geom.Matrix;

var my_matrix = new Matrix(2,3,5,7,2,4);
trace(my_matrix); // (a=2,b=3,c=5,d=7,tx=2,ty=4)

my_matrix_inverse = my_matrix.clone();
my_matrix_inverse.invert();
trace(my_matrix_inverse); // (a=-7,d=-2,tx=-6,ty=2)

my_matrix_inverse.concat(my_matrix);
trace(my_matrix_inverse); // (a=1,c=0,ty=0)
以上可以看出,原始矩阵与逆矩阵相乘的结果是单位矩阵。

一个图片使用逆矩阵,可以用级联矩阵的逆乘以当前矩阵,移除来自所有父图片的变换。 ?

var concat_matrix = parent_mc.child_mc.transform.concatenatedMatrix;
concat_matrix.invert();
concat_matrix.concat(parent_mc.child_mc.transform.matrix);
parent_mc.child_mc.transform.matrix = concat_matrix;

使用矩阵变换

既然已经说了这么多矩阵变换,现在是时候来说说如何使用它们了。现在只说Flash 8,通过ActionScript,我们可以非常轻松的在IDE里面修改图片(集成了开发环境),这都归功于矩阵变换。


首先,从我们所知道的开始。基础的矩阵变换的方法有,平移,缩放,旋转。如果你已经忘记了,这里是没有切变的。要不要改改这个?


为了实现一个与其他相似的自定义切变方法。你需要考虑到,这个切变也许会用在其他变换的之上,包括平移。假设当前任意一个存在变换的矩阵中,如果你尝试去设置这个非单位矩阵中的切变属性(b 和 c)的值,来做一个简单的切变,是有可能破坏矩阵原有的变换的。幸运的是,矩阵乘法,是一个将变换,作用在已经存的变换的矩阵上的过程。这就是说,添加一个切变,就是将一个单位切变矩阵,乘以当前存在的矩阵罢了。


function skew(matrix,x,y){
	matrix.concat( new flash.geom.Matrix(1,y,0) );
}

这个简单的切变,传给了方法concat一个新的单位矩阵,并且矩阵的c和b的值设置成了x和y。结果是,原来的矩阵,乘以了新的切变矩阵,并且切变不会影响原来的矩阵的变换。像平移,缩放,旋转。这个切变函数,同样会改变当前的矩阵的平移元素。这里唯一不同的的是,因为这个切变方法不是Flash的Matrix类的一部分,用起这个方法的时候,目标矩阵会当成参数传入。


skew(my_matrix,0);

有一件事你会注意到,比如方法scale(并且这个新方法skew),是不以当前变换矩阵的为基准的那种变换。它们已经考虑到了之前的已经存在的变换,但他们的缩放和切变,不是以那些变换为基础的。更加清晰的表达我所说的情况是,例如,旋转一个图片45度。当考虑到对这个图片做一次缩放,概念上,会有两种结果;要么好像没有以旋转为基础的旋转,要么以旋转为基础的缩放。




用缩放函数,和Matrix类其他方法,你可以在那个旋转的基础上,就像看到的第一种变化那样,没有旋转过,不像看到的第二个那种。假如你想旋转的同时做矩阵缩放,像上面看到的第二个那样,那么你实际会看到先缩放,再旋转。就父-子关系考虑一下这种情况,当子动画缩放时,其实也跟着父矩阵的旋转而旋转的。这个与函数sacle()的缩放是相反的,函数sacle()更像父矩阵来缩放一个已经旋转过的子矩阵。矩阵变换的方法,像似父变换添加到已经存在的子矩阵上。为了获得相反的效果,你要做的就是用矩阵乘法,不过要改变相乘时的顺序。


对于这样的交换,要以方法的变换开始,然后在这之上用原始的相乘。对于刚开始的矩阵,只要在一个单位矩阵上,用需要的函数就行了。经过这些,我们可以创建一个新方法,来影响矩阵“内部”的变换。

function innerScale (matrix,y){
	var scale_matrix = new flash.geom.Matrix();
	scale_matrix.scale(x,y);
	scale_matrix.concat(matrix);
	return scale_matrix;
}

function innerSkew (matrix,y){
	var skew_matrix = new flash.geom.Matrix();
	// assume skew function for matrices is defined
	skew(skew_matrix,y);
	skew.concat(matrix);
	return skew_matrix;
}

function innerRotate (matrix,angle){
	var rotate_matrix = new flash.geom.Matrix();
	rotate_matrix.rotate(angle);
	rotate_matrix.concat(matrix);
	return rotate_matrix;
}

请注意,这些方法,事实上会返回一个新矩阵,而不是改变原有矩阵。这些例子的目的,比上文会简单一些。以下是一个运用在图片上的例子。

var my_matrix = my_mc.transform.matrix;
my_matrix = innerScale(my_matrix,1);
my_mc.transform.matrix = my_matrix;

在用这些变换是,你也许会注意到,平移不受影响。这是因为基础的变换实际上没有平移,所以,无论在原有矩阵上如何使用变换,最终矩阵的平移元素还是会返回原来的值。


然而,假如你也不想原始矩阵的平移元素受到改变?Flash已经提供了一个方法,在移动点的时候忽略平移元素的改变。就是方法deltaTransformPoint(),而且它就像方法transformPoint一样,除了transformPoint在变换时不修改平移元素。其他的变换矩阵方法没有类似的情况,但是你可以通过存储原来的平移位置,在变换之后重新赋值给矩阵,来轻松的提供类似的方法。是的,不涉及复杂的矩阵运算。

function deltaScale (matrix,y){
	var position = new flash.geom.Point(matrix.tx,matrix.ty);
	matrix.scale(x,y);
	matrix.tx = position.x;
	matrix.ty = position.y;
}

function deltaSkew (matrix,matrix.ty);
	// assume skew function for matrices is defined
	skew(matrix,y);
	matrix.tx = position.x;
	matrix.ty = position.y;
}

function deltaRotate (matrix,angle){
	var position = new flash.geom.Point(matrix.tx,matrix.ty);
	matrix.rotate(angle);
	matrix.tx = position.x;
	matrix.ty = position.y;
}

这些方法,像原来的变换方法那样,但不同于内部的方法,会改变在正在使用的矩阵内部的值,而不是返回一个新的矩阵。


另外,你可以根据已经定义好的原有的方法,来改变矩阵中的值,不过,更简单的方法是,保存原有的值,在变换过后,重新赋值,特别是当操作非常低效的时候,比如旋转。

function deltaRotate (matrix,angle){
	var sin = Math.sin( angle );
	var cos = Math.cos( angle );
	var a = matrix.a;
	var b = matrix.b;
	var c = matrix.c;
	var d = matrix.d;
	matrix.a = a*cos - b*sin;
	matrix.b = a*sin + b*cos;
	matrix.c = c*cos - d*sin;
	matrix.d = c*sin + d*cos;
}

转换转化

有时候,你可能需要与已经存在在Flash IDE中的矩阵比较矩阵的值,或者与ActionScript更加普遍的属性比较。不过,变换矩阵的值,不总是与其他的Flash副本相关联。例如,平移直接与Flash中的位置x(_x)和y(_y)相关,但是,缩放,切变,旋转处理起来有一些不同。


平移

对大多数情况而言,平移是简单的。属性tx和ty,在矩阵中直接与ActionScript提供的_x 和 ?_y相关联,_x和_y,是父时间线中图片的位置。不过,在Flash IDE中,这些值在属性检查时可以不同。


Flash IDE允许位置以两个不同的地点为基准。可以在图片的左上角,或者在图片的中心。使用哪个的选项,会出现在面板上的表格中。




但是,这个中间选项,并不是永远表示问题中图片的本地的0,0位置,或者原点。它实际上与变换工具的变换中心点相关,并且在Flash中,它是表示为小的黑点和白的圆圈。默认情况下,这是位于图片的中间,但是可以通过变换工具来调整。针对变换的值,或者只是那种情况下的_x 和 _y值,来匹配这个变换中心点,这会要求放在图片中的原始位置,以黑色和白色表示的十字。


ActionScript访问不到变换的中心点的,所以当你在Flash中操作图片时,记住这个。最好保证注册点与图片原始点对齐,这样你就可以,在Flash IDE中,直接关联看到的值,并且这些值是可以通过ActionScript访问到的。



var my_matrix = my_mc.transform.matrix;

trace("x: " + my_matrix.tx);
trace("y: " + my_matrix.ty);

缩放

缩放,大多数与变换矩阵的a和b直接相关,但在切变的变换后会变复杂。没有切变,a和b直接关联Flash提供的缩放元素——那些在IDE的变换面板和ActionScript中已经存在的_xscale和_yscale——用一个缩放比例为1的,矩阵缩放的最简单的例外来说,不过在Flash中,缩放是以100为基准的,对于缩放100%。


当调用切变时,缩放的一个轴发生了变换,会在另外一个相关的轴上继续拉伸,无论x到y或者相反顺序。因为这些关系,只要使用毕达哥拉斯定理来获得正确的缩放因子就行了。


var my_matrix = my_mc.transform.matrix;

trace("x scale: " + Math.sqrt(my_matrix.a * my_matrix.a + my_matrix.b * my_matrix.b));
trace("y scale: " + Math.sqrt(my_matrix.c * my_matrix.c + my_matrix.d * my_matrix.d));

切变

在Flash 8中之前的变换矩阵,切变的值是不能被ActionScript访问到的。不过,Flash IDE,确实依然能够在变换面板上提供切变的值,以度数的形式显示。这与真实变换矩阵求解变换的值有一些不同。值是代表比例的,在一个轴上与另外一个轴相关的缩放值。他们不是旋转或者角度值。


在Flash中,表示角度的旋转值,当一个边发生切变时,会在另外一个轴上创建该角度值。例如,x轴上的切变,在倾斜的y轴上和他的原来的垂直方向之间,创建了一个角度。


角度值是与切变的大小相关的。用轴上任意一个点,用图片矩阵转换它,可以通过arctangent让我们得到旋转的值。x轴切变到y轴朝向,则需要一个90度差值。

var my_matrix = my_mc.transform.matrix;

var px = new flash.geom.Point(0,1);
px = my_matrix.deltaTransformPoint(px);
var py = new flash.geom.Point(1,0);
py = my_matrix.deltaTransformPoint(py);

trace("x skew: " +((180/Math.PI) * Math.atan2(px.y,px.x) - 90));
trace("y skew: " +((180/Math.PI) * Math.atan2(py.y,py.x)));

旋转

如果你知道切变,你就知道旋转了。Flash用x切变角度来旋转。

 

应用

基础叙述完毕。是时候让矩阵变换的知识用在真实世界的应用程序中了。既包括改变图片,也包括BitmapData操作。


摇动的笑脸组

这里,有一组无需的笑脸组成的动画,已经嵌套存在在的图片内了,并不断变换着。当选中一个时,它会冲出父类的变换图,通过使用级联矩阵反转,让他自己真实的显示出来。

【查看 |?下载】


根据以上的展示,一个嵌套的,变换的动画可以通过使用invert和级联矩阵,非常轻松的脱离它的变换。这个例子是这样,但是在图片的动画上添加了,从原始的变化,到新的翻转的变化。可以通过使用自定义的方法matrixInterpolate()来实现。


Flash中的点类,有一个方法interpolate(),允许你根据线定理,连接两个点。基本上,这是那些取“两者之间”的方法,来取两者之间的值。Matrix类是没有这样的方法的,因此,为这个例子创建了一个方法。它所做的就是返回一个新矩阵,给定一个t参数,取值在0和1之间,使新矩阵的值在两个矩阵的值中选择。

function matrixInterpolate(m1,m2,t){
	var mi = new flash.geom.Matrix();
	mi.a = m1.a + (m2.a - m1.a)*t;
	mi.b = m1.b + (m2.b - m1.b)*t;
	mi.c = m1.c + (m2.c - m1.c)*t;
	mi.d = m1.d + (m2.d - m1.d)*t;
	mi.tx = m1.tx + (m2.tx - m1.tx)*t;
	mi.ty = m1.ty + (m2.ty - m1.ty)*t;
	return mi;
}

用事件onEnterFrame绑定这个,动画就可以从原始嵌套变换,到反转变换,如此重复了。

然而,关于反转变换的一件事情是,它不只是反转级联矩阵。只是反转会让矩阵成为一个单位矩阵,让图片移动到0,0位置,缩放到普通比例(这个例子中的动画的小版本)。相反,另外一个,位于右边的一个大区域里,放置了绑定了反转单位矩阵结果的矩阵。


地板上走动的老鼠

这个例子显示,一个变换矩阵通过方法beginBitmapFill(),当鼠标拖动屏幕会歪曲一个瓦片图片(当做地板),一个等同的歪曲的老鼠动画行走在上面。还有,它还显示了一个对象(一罐奶酪)会随着变换的的地板一起变换。

查看?|?下载

接下来的图片是例子中用来做瓦片地板的。



使用方法beginBitmapFill(),它会被画在一个宽高为(300 x 300)的动画的正方形上。对于切变的影响,一个用方法beginBitmapFill来切变变换的矩阵,会以老鼠的x位置的基准来改变。


当在beginBitmapFill中处理矩阵变换是,比如任意的bitmap操作,变换的“中心”将处在现有的bitmap的左上角。在这个例子中,变换的中心,需要被设置在动画的中心。使用方法translate(),中心会从左上角变换到中心位置。因为方法beginBitmapFill()将铺在被画的图片上,在位置改变的时候,没有图像会丢失。相反,你只是得到了一个图像移动,这个移动也许没有明显的初始化过。然而,在用切变的时候,这种不同变得更加明显。

演示地址


你会想知道,如果移动切变的中心点,地板是如何移动的?这个与移动无关联吗?事实上,除了通过方法beginBitmapFill,它是有关的。两个用方法beginBitmapFill 来画事实上作用在地板上。一个是用在中心,切变地板;另外一个用于事先的绘画,用来改变原始的bitmap,根据需要的大小,移动它的图片——平移它。这改变bitmap是当在bitmap画在切变的地板上。

演示地址


通过以上的,地板开始正确的移动了,不管切变,并且总是来自动画的中心。

动画里,另外的元素是一只老鼠,还有一罐奶酪。老鼠只是放在地板的中心,设置成与地板的切变相匹配。这只跟随地板改变它。

奶酪有一些不同。事实上,没有任意一个变换作用在它身上。这是因为,它只是坚持直线上升的,而不是放在地板上。要是放在地板上,那就会随着地板移动,不过因为(感知到)垂直的轴没有改变,奶酪罐也没有改变。然后,它需要随地板移动。


当奶酪随着地板垂直移动,在同一个作用在bitmap上的移动节奏上,它需要确保它的水平线,保持在正确的位置。这个位置会以切变的大小而变化,还有与切变中心的距离有关——中心,由切变方法beginBitmpFill()摆放的位置。事实上,当垂直放置那个中心,水平的位置是不会受到切变的影响的。只有从那个位置递增才会收到切变的影响。你最终得到的公式是有点像下面这个:

cheese_x_offset = center_x + (cheese_y - center_y) * skewAmount;
由于奶酪的y值和中心的y值的不同,切变的大小,切变的大小,会使奶酪的中心x值变化更大。


黑洞

通过旋转和缩放一个用在BitmapData的方法draw中的矩阵,可以创建一个黑洞的效果,吸收和包括所有屏幕上看到的图片。

【展示 |下载】


有旋转、缩放和平移这三种变换作用在矩阵上,产生了黑洞的效果。旋转给出了漩涡的效果,缩放给出了图形缩小和向内的下沉的效果,平移保证了它们都在屏幕中央。代表持续的黑洞的bitmap,不仅从鼠标的位置发射出了星星,同时,它持续的表现出尾巴效果,最终被吸到变换之中。


旋转和缩放是非常简单的,但是,当与平移一起时,就需要做一些额外的工作,考虑到旋转和缩放时,来保证平移的正确性。记住bitmap的中心是在左上角。这个黑洞的效果是位于bimap/屏幕的中间的。这是通过多步过程的处理,涉及到缩放,中心缩放,旋转,重定位。

? ? ? ? ? ? ? ? ? ??


1、首先,缩放矩阵 ? ? ? ? ? ? 2、以缩放为基础,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?矩阵被平移到中心点0,0位置

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?(将要影响到的,相对于bitmap的中心)


? ? ? ? ? ? ? ? ?


3、从那个位置开始旋转, ? ?4、最终,矩阵的位置开始平移,

因为之前的平移,旋转是? 它的中心最后以效果中心为中心。

以中心为基准旋转的。


3D 立方体图片

典型的旋转立方体通过使用它的边上的图片。这要归功于Flash 8,有访问图片的变换矩阵的能力,这个例子实现起来变得更加简单了。

【查看 |下载】


旋转这样的立方体图片,在Flash 5就有了,还有可能更早的版本也有。变换图片的效果的思想,是平移图片,使他们匹配3D旋转的立方体的每一边(sans的视角)。在Flash 8之前,为了变换的需要,才需要图片嵌套的。特别是在切变图片的时候。不过,现在有了Flash 8,归功于开发者可以直接访问图片的变化矩阵,让这个效果更容易取出了。事实上,你一旦理解了3D,改变变换矩阵是很容易的——没有复杂的公式,或者需要三角函数。你要做的就是找到一些点的位置的不同,组成立方体的每一面,基本上就搞定了。


用3个点,来改变下面的图片。定义变换,和见证位置与变换矩阵的明显不同的关系。

【查看】


总结

教程已经涵盖了变换矩阵是什么,怎么使用;讨论了Flash中的Matrix类和它的操作矩阵的方法。虽然也包含了一些操作中的数学公式,这些公式,在你编码矩阵时,有可能不会用到。


希望你能够通过本文,更加理解变换矩阵,在这个基础上,对Flash中的元素的控制,有一个新的高度。


(完) ?!!!!


行百里者半九十。文章全文的翻译,我差点就没坚持下来。不过最终还是克服了困难,用了接近十天的时间,每天下班以后,一直在翻译着文章。国外的人写的长句,看起来比较难翻译。不过,仔细斟酌,还是能弄懂其中的意思的。翻译的时候,碰到难点,可以先跳过,等整篇文章都过了一遍之后,反过头来看原先不理解的地方,就会变得更加明白了。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读