Performing a Series of Transformations on a Display Object

In order to illustrate how to use the transform.matrix property effectively for a series of transformations (as in our cube menu applet), let's look at a few simple examples. Create a blank ActionScript 3 fla file, enter the following code on the first frame, and test your movie:

var square:Sprite=new Sprite();

this.addChild(square);

square.graphics.lineStyle(0x000000,1.0);

square.graphics.beginFill(0xCC0000,1.0);

square.graphics.drawRect(0,0,200,200);

square.graphics.endFill();

We see a red 100 by 100 square located at (0,0). Now, add the following statement and test the movie:

trace(square.transform.matrix);

The output window reads:

(a=1, b=0, c=0, d=1, tx=0, ty=0)

Initially, the transform matrix is the 'identity' matrix. Delete the 'trace' line.

Suppose that we want to scale our square by the factor of 2 in the x direction, the factor of 3 in the y direction and that is all we want to do. The following code works:

var square:Sprite=new Sprite();

this.addChild(square);

square.graphics.lineStyle(0x000000,1.0);

square.graphics.beginFill(0xCC0000,1.0);

square.graphics.drawRect(0,0,200,200);

square.graphics.endFill();

var curMatrix:Matrix=square.transform.matrix;

curMatrix.a=2;

curMatrix.d=3;

square.transform.matrix=curMatrix;

Note that you cannot access properties of square.transform.matrix directly. The code:

square.transform.matrix.a=2;

does not work. You have to make a copy the square.transform.matrix object, store in in a variable of type Matrix, alter the copy, and then assign the new matrix to square.transform.matrix.

If you want to set the values of a, b, c, d, tx, ty directly, then perhaps the more reliable is the following way in which we set all coefficients at once:

var square:Sprite=new Sprite();

this.addChild(square);

square.graphics.lineStyle(0x000000,1.0);

square.graphics.beginFill(0xCC0000,1.0);

square.graphics.drawRect(0,0,200,200);

square.graphics.endFill();

square.transform.matrix=new Matrix(2,0,0,3,0,0);

Lets try skewing our square vertically by 45 degrees (or pi/4 radians). We have: tan(Math.PI/4)=1. Thus the following matrix ought to do it:

var square:Sprite=new Sprite();

this.addChild(square);

square.graphics.lineStyle(0x000000,1.0);

square.graphics.beginFill(0xCC0000,1.0);

square.graphics.drawRect(0,0,200,200);

square.graphics.endFill();

square.transform.matrix=new Matrix(1,1,0,1,0,0);

It does, indeed. This all works if you want to perform one transformation at a time and your object is at [0,0]. That is, tx=0, ty=0. Suppose we want to:

  • Scale our square by the factor of 2 in both directions.
  • Skew vertically by 45 degrees.
  • Translate our square to the positon x=50, y=50.

One would expect the following code to work:

var square:Sprite=new Sprite();

this.addChild(square);

square.graphics.lineStyle(0x000000,1.0);

square.graphics.beginFill(0xCC0000,1.0);

square.graphics.drawRect(0,0,200,200);

square.graphics.endFill();

square.transform.matrix=new Matrix(2,1,0,2,50,50);

When we test the movie, the results are not exactly what we expected: the skew is by less than 45 degrees. That is, because we are dealing with compositions of transformations and, to make things more difficult, with compositions of affine and not linear transformations. The most reliable way of performing a series of operations is by using Matrix.concat(); method of the Matrix class. The method works perfectly provided you keep tx=0 and ty=0 and perform the desired translation at the very end. The following code does what we want:

var square:Sprite=new Sprite();

this.addChild(square);

square.graphics.lineStyle(0x000000,1.0);

square.graphics.beginFill(0xCC0000,1.0);

square.graphics.drawRect(0,0,200,200);

square.graphics.endFill();

var finMatrix:Matrix=new Matrix(1,0,0,1,0,0);

var curMatrix:Matrix=new Matrix(2,0,0,2,0,0);

finMatrix.concat(curMatrix);

curMatrix=new Matrix(1,1,0,1,0,0);

finMatrix.concat(curMatrix);

curMatrix=new Matrix(1,0,0,1,50,50);

finMatrix.concat(curMatrix);

square.transform.matrix=finMatrix;

We begin with finMatrix being the 'identity' matrix and create curMatrix as a matrix that scales by the factor of 2 in both x and y. As long as tx=0 and ty=0, the concatanation operation finMatrix.concat(curMatrix) creates a matrix that corresponds to the operations performed by finMatrix (none at the beginning) and then the operations defined by curMatrix (scaling by 2 in x and y in the first step). The result of
finMatrix.concat(curMatrix) is still stored in finMatrix; thus, finMatrix is being altered. In the second step, curMatrix is redefined as the matrix that performs vertical skewing by 45 degrees. Now, finMatrix.concat(curMatrix) performs scaling and then skewing by 45 degrees. The concatenation operation is cummulative. At last, curMatrix is defined as the matrix that translates to [50,50]. After the line:
finMatrix.concat(curMatrix), finMatrix performs now: scaling by 2, skewing by 45 degrees, and translation to x=50, y=50. This is what we wanted. Thus, we assign finMatrix as the value to square.transform.matrix.

Back to Advanced and Experimental              Back to Flash and Math Home

The site www.flashandmath.com is maintained by Doug Ensley (doug@flashandmath.com) and Barbara Kaskosz (barbara@flashandmath.com).
It has been developed with partial funding from the National Science Foundation and the Mathematical Association of America.