The matrix in beginGradientFill , part 2
by ericlin

I discussed about matrix in beginGradientFill previously. In that article, I explained what those fields x,y,w,h,r mean and what effect will appear if we change those settings. I also explained those fields a,b,d,e,g,h and the effects they affect.

My days of Flash seem boring recently. So I decide to discuss in more details about "how to set those fields to get the gradient fill we want."

I will review those fields first and then shows cases how to use beginGradientFill to mimick the gradient fill done by graphic tools. I believe this will give much help to those who want to understand the matrix more.

It is a canvas with gradient colors. If we use beginGradientFill to draw something, the shape we draw effects as a mask and the gradient "field" is the background that masked.

Lets try to understand the gradient "field" - the canvas. Basically it is a 1x1 square containing the colors, alphas that are arranged by the ratios. After the square is constructed, it is transformed by the matrix data. Remember the matrix is for transforming the  1x1 square.

Practically, it is very hard to understand what is 1x1 square. It is a shrinkage of 100x100 square.

For linear type, the 1x1 square is constructed with ratio=0 at the left margin and ratio=255 at the right margin. You know that, the canvus is not just the gradient field. Beyond the left and right margin, the canvas spread the color to forever. You might think that, for linear gradient, there seems no setting about "height" ? That is not correct. I will explain this later.

For radial type, the 1x1 square is constructed with an "inside" circle. "Inside" circle means the "diameter" of the circle is 1 pixel; The ratio=0 is constructed at the center and ratio=255 is constructed at the periphery of the circle. Please note that, the 4 corners of the square are in fact out of the true gradient circle. However, the gradient "field" is the square that are to be transformed by matrix. Beyond the periphery of the circle, the color continues.

OK, lets review the transform matrix.

The box type:

The 1x1 square for box is a square of (0,0)-(0,1)-(1,1)-(1,0). Or a shrinkage of 100x100 square with the registation point (0,0) at left upper corner.

x,y: are the left-upper corner of the square. w,h are width and height that the square are going to be stretched to. r is the rotation of the gradient field.

Here is an important thing: the order to process these transformation data. First, the gradient is rotated by r within the square with center of the square as rotation axis. The square are not affected. The square does not rotate. Then, it stretch the width and height of the square to w and h setting. Please note that, the stretching take the left upper corner as the registration point. This is different from the process of "rotation". Finally, the square is move to right place with left-upper corner fitting the  x,y as specified in the matrix data.

Here are side effects. When we stretch the w and h, the coordinates of the center of the square gets changed. The second effects is that, stretch changes the appearance of the rotation. For example, initially we rotate our gradient field 45 degree. Then we stretch the width of 100x100 square to 100x200. The 45 degree rotation gets the stretch and appears like 30 degree rotation.

Are you still here ?

The skew type:

a=scaleX;
b=rotateSkew0;
d=rotateSkew1;
e=scaleY;
g=translateX;
h=translateY;

x' = x * ScaleX + y * RotateSkew1 + TranslateX
y' = x * RotateSkew0 + y * ScaleY + TranslateY
c, f,i are not used.

The help manual tells us that, the initial square is defined at (-1,-1) to (1,1). I think it is wrong. The square is not a 2x2 square. It is a 1x1 square. The gradient is defined at (-0.5,-0.5) to (0.5,0.5);

The transformation take the 1x1 square center as the registration point. This is different from the box type. So, the square is first stretched according to scaleX and scaleY. This part is similar to w and h in box type. Then it add the skew. Finally it moves the center of the square to (g,h) specified in matrix.

This transformation take the center as registration point and many consider this is more handy than the box type.

OK, lets begin our exercise.

case 1: linear gradient without rotation:

How to construct this gradient field by proper transformation matrix ?

It is simple. The transformation involves horizontal move and stretch. We can easily mark out the square of gradient field.

For box type, the x should be the left margin, the y can be anything. The w is calculated by right margin-left margin, the h can be anything except 0. We set it 100. There is no rotation.

The matrix = {matrixType:"box", x:125, y:0, w:150, h:100, r:0};

It is also simple for skew type matrix, The scaleX is the width. The scaleY can be anything but 0. We set it 100. The rotateSkew0 and rotateSkew1 are 0 because there is no skew. The translateX and translateY are different from box type. It is the center of the square. So, translateX is 125+w/2=200; the translateY can be anything.

a=scaleX=125;
b=rotateSkew0=0;
d=rotateSkew1=0;
e=scaleY=100;
g=translateX=125+75;
h=translateY=0;

The matrix= {a:a, b:b, d:d, e:e, g:g, h:h};

You see, they are different only in the definition of registration point.

case 2: linear gradient with rotation

Lets try the box type.

We draw an orange square that contains our gradient field. We detected the center to be {x:214, y:91}; We measure the length of the side to be 108; The rotation is -Math.atan2(86,67);

Remember that, the transform box is always a orthodox box. What rotates is the gradient field. Do not take the coordinates of the "left upper" corner of this rotated square as x,y. We need to figure out the original orthodox box before rotation and then pick the left upper corner as the x,y.

Coordinate of the left upper corner can be calculated by the center and square size. It is  {x:center.x-w/2, y:center.y-h/2};

The white square is the orthodox square we want.

Here is the matrix:

matrix = {matrixType:"box", x:214-54, y:91-54, w:108, h:108, r:-Math.atan2(86, 67)};

We can draw similar square along the orientation of the gradient. We can even combine a different rotation value with stretched height to get the same result but that is too complicated.

Now we try the skew matrix:

We draw an orange rotated rectangle covering our gradient fill. The height of the rectangle does not matter much. This is the final transformed rectangle we need to achieve from the 1x1 gradient square.

To get the scale and skew, we draw a virtual orthodox rectangle containing the gradient rectangle with each side passing the 4 corners. This virtual rectangle only serves as a measurement .for the vertical and horizontal offsets. After you are familiar about this, you can get offsets directly without the need of this virtual orthodox rectangle.

For explanation, I made two colored triangles and measure the length of those side segments. They are just offset in the X axis and Y axis. Now, it is straight forward to get 69 as scaleX, -85 as rotateSkew0, 29 as scaleY and 37 as rotateSkew1. The center of the orange rectangle is the same as the center of the virtual orthodox rectangle. That is the  translateX and translateY; (Do you know why rotateSkew0 is -85 not 85 ?);

So, we get the matrix:

a = scaleX=69;
b = rotateSkew0=-85;
d = rotateSkew1=37;
e = scaleY=29;
g = translateX=210;
h = translateY=94;

matrix = {a:a, b:b, d:d, e:e, g:g, h:h};

After we are familiar with this rules, we need not draw that virtual rectangle any more. Those length of side segments are just "offsets" of corners.

This is simple. The principle is nearly the same as linear gradient. We draw a rectangle covering the radial gradient field.

For the box type, the w and h are the width and height, The x,y is the coordinates of the left upper corner. So the matrix is

matrix = {matrixType:"box", x:20, y:26, w:220, h:146, r:0};

For the skew type, the scaleX and scaleY are the w and h of the box type. The translateX and translateY is the center of the rectangle. So the matrix is:

matrix = {a:220, b:0, d:0, e:146, g:20+110, h:26+73};

Now we check the last example:

For the box type: It is impossible.

Think about that, rotation of radial gradient inside the 100x100 square does nothing. The r in the matrix has not any effect. What left are x,y,w and h; By those fields, it is impossible to construct a radial gradient like that.

Sure, it can be done very straight forward. The principles are the same as for linear gradient. Draw an orange rotated rectangle covering the gradient field. Check the center and those offsets.

a = scaleX=108;
b = rotateSkew0=-113;
d = rotateSkew1=67;
e = scaleY=70;
g = translateX=161;
h = translateY=97;
matrix = {a:a, b:b, d:d, e:e, g:g, h:h};

Again, this is another reason why Flasher love skew matrix more than the box type matrix.

For linear and radial gradient without rotation, you may well use the box type. But, as I described above, skew matrix is as simple as box type for such simple gradient.

For linear or radial gradient with rotation, I believe the skew matrix type is the choice.

The description of 3x3 matrix in the Help Manual supplied by Macromedia is very difficult to understand. Most people believe the box type is easier to grasp. If you read this article again and try to chew the content before you swallow it, you will be able to handle the a,b,d,e,g,h easily. And, you will believe that setting the a,b,d,e,g,h is easier than the box type.