Rotating iso 3d cube
by ericlin

Years ago, in the Flash 5 era, I publish a movie about how to create an iso3d cube. The initial goal for that movie is to demonstrate the art of skew. The rotation of the cube is for displaying the skewed faces. It simply rotates around some X, Y and Z axis. No attemp is made to fully control the rotation in 3d space.

Besides, I create the cube on the stage. That proved to be a mistake for a tutorial. Who try to "use" that codes to generate a cube will meet trouble of confliction with their codes. I get emails about how to fix the script for their goals. Now, it is a movieClip now. You can add several cube on the stage without confliction.

All of my articles are discussions about some "mechanism" we can apply to Flash. Usually, those scripts or php codes can not be applied directly without modifications. They are not mean to be "components".

Anyway, here I re-wrote the script in Flash MX 2004 format. Some math codes are collected into an independent Math3d class. Only those scripts that are used to construct and rotate the cube are collected in Cube class. We access the holder array to add our textures. We use rotateCube around a arbiturary 3d axis to rotate the cube. It is easier to change the texture and rotate the cube now.

For example: To rotate "90" degree to move the next side face to face on us is simple. We just rotate the cube around the axis that passes through the center of the top face.

It is still not a good component. There is no UI stuff. I dont like a bunch of getter/setter function for custom tweaking. To tweak it, you still get to read and modify the script.

The Cube class is nothing mysterious. It just constructs 8 points and 6 faces. We rotate those points by some 3d function and then render the faces out.


When a face get click. that face turns to face on us

This is the feature that many ever wanted. 

When the cube gets rotated randomly for a while, the orientation of faces are changed to some "unknown" directions. Well, not really unknown but slightly complex. When we click a certain face, how do we turn the cube to make that clicked face facing on us ?

First, we find out what direction the clicked face is. Second, find out the angle between the that facing vector and the desired end direction vector (in this case, the Z axis). Third, find out the axis vertical to the rotation plane so that we can rotate. Fouth, rotate the cube around the axis with that angle amount.


The facing direction of the clicked face

If I clicked face 2, how do we know what direction my face 2 is facing now ? It is simple. 

Since we have registered what top_right and bottom_left point are for each face, we just pick the position of those two points and get the middle point of them to get the center of the face.


The angle between two 3d vectors

Thanks for jonMack who advised me to use dot_product for calculation of the angle. You can find resource on the net.

By definition, the dot product for 2 vectors {x: x1, y: y1, z: z1} and {x: x2, y: y2, z: z2} is 

dot_product=(x1*x2) +( y1*y2) + (z1*z2);

Pick out each value in each field x, y, z. Multiply the value with value in the same field. Sum the result.

To get the angle, we use this formula to get cos; Please google it for details.

cos(angle)=dot_product / (vector1_length * vector2_length);

Calculate the sin value from cos value and then convert them into radian angle:

sin=Math.sqrt(1-cos*cos);

angle=Math.atan2(sin, cos);

I wrote a function getAngle(vector1, vector2) in the Math3d class. Please read it for script details.


The bug of in getting angle

When I tested my getAngle codes, my cube crashed and broke apart in some occasions. It took me much time to find out the bug.

The bug is due to the floating point calculation. Or, put it more precisely, it is due to the function: Math.sqrt. For most of the number, the square root  is not a rational number. The result can not be stored precisely either in decimals or by bits of 2. Some in-accuracy is in-evitable. 

Try this script:

a=Math.sqrt(3);
trace(a*a-3);

It is not 0. That means, the product of square root is smaller than original number. 

Remember the formula to get the cos value ? We need the length of vector1 and vector2 which are obtained through Math.sqrt. Here is the bug. In many occasion, when the angle is 0, we expect the dot_product should be the same as the product of two length. Theoretically, the cos should be 1; 

No !  The product of two lenghts is slightly smaller than the dot_product  Our cos is slightly bigger than 1. And our sin turns out to be NaN and the returned angle is NaN.

If I rotate the cube by an angle of NaN, the cube crashes.

I made a simple fix. Just put a guard script to handle that condition,


Get the rotation axis

Theoretically, two non-paralell vectors form a "plane". Our rotation axis is the vector from the angle point and perpendicular to that plane. The angle between our axis and both vectors should be 90 degree. Or, in other words,  the "projection" of both vectors to our axis vector should be zero.

Presume  : The vector 1 as {x:a1, y:b1, z: c1} and the vector 2 as {x: a2, y: b2, z: c2}, and we need the axis {x: x, y: y, z: z}

Since the projection is 0, ( or the cos should be 0), the dot_product is zero. We get two equations.

a1*x+b1*y+c1*z=0;
a2*x+b2*y+c2*z=0;

Solve these two equation, we get our vertical axis.

There are 3 variables but we have only 2 equations, so the answers are not unique. Lets assume z=1, the two equations turns to

a1*x+b1*y+c1=0;
a2*x+b2*y+c2=0;

OK, 2 variables and 2 equations, we can get the correct answer by solving these equation.( I am not going to discuss how to solve it in detail now). Here is the result:

y = (c1*a2-c2*a1)/(a1*b2-a2*b1);
x = (c1*b2-c2*b1)/(b1*a2-b2*a1);

Please note that, this vector is not the only axis that is vertical to those two vectors.It is just the vector when z is set to 1.  Any vectors along this axis can serve as the desired axis to rotate our vectors. So any vector produced by multiplying this axis with a ratio shall suffice our condition. If the ratio is -1, the vector is still along our axis but pointing to the opposite direction. Anyway, what we want is an axis to rotate. One is enough.

Well, what if a1*b2==a2*b1 ? In this condition, our formula get a result "divided by zero" and returns NaN. In fact, in this condition, these two "points" are lying paralell to the Z axis (vertical to XY plane) and our desired axis shall lie on the XY plane. The z of our axis should be 0 not 1. So, we get our equations:

a1*x+b1*y=0;
a2*x+b2*y=0;

The answer is not unique. So we assume y=1, and we get the answer: x = -b1/a1;

Well, what if a1==0 ? In this condition, the formula gives NaN ! 

If those two points lies paralell to Z axis and one of the x is 0, then they are lying on the YZ plane. The axis should be the X axis, that is {x:1, y:0, z:0};

You might ask, what if these two vectors lie along a line, either in opposite direction or poiting the same direction, how do we get the vertical axis ? The correct answer should be any vector on a specific "plane" that are perpendicular to those vectors. However, our goal is to find "one" vector. One vector is enough. We have been assume z=1 and later y=1 and at last x=1. We just need one. One is enough.


Test the angle "endAngle==resultAngle"

The final job is to test our result. I have the angle between those two vector. I have got one axis that can be used to rotate that angle. However, a vector pointing the opposite direction is also the vector that can be use to rotate. I dont know which one is the correct one. In other words, I dont know whether we should rotate it clockwise or counter-clockwise. So, to confirm, I just test it.

I rotate that vector to see whether the resulting vector points to the desired vector direction.

Manipulate data in 3d space needs heavy calculation about float points. Some rounding process below 15 decimal is inevitable. If we have two angle values A and B, how do we know they are the same ?

For two integers A and B, we test whether they are the same by A==B. When we compare two float point numbers, it is wise not to use "==". The work around is to develop our comparison method. If the difference between float points A and B is less than 0.000001, we say the difference is zero. They are equal.


Rotate to correct the corners

Jobs are not done yet.

Our earth rotates itself and moves along the orbit path around the sun. What we did is bring the earth along the orbit path to the position in front of us. Now we want the earth rotate itself so that the equator is straightly horizontal.

We have rotate the cube so that the center of the face 2 is facing on us. But the top left corner is not yet at the supposed position. To acomplish this, we need to rotate the cube around the center of the face. When the face 2 is already facing on us, the axis is the Z axis. What is the angle we need to rotate it around this axis ? Is that the angle between current corner point and supposed corner point ? Can we obtain the angle by "getAngle(point1, point2) ?

Well, we need some modification. If we pick those "points" coordinate as vector, they are the vectors with tail at (0,0,0). Then the axis through the center of the face is not perpendicular to these two vector. In other word, if we get the rotation axis from those two vectors, the axis is by no means the axis through face center.

To make these two vectors lie on a plane perpendicular to our axis is not difficult. We pick the center of the face as the starting point of the vector. These two points are converted into vectors relative to the center point. Then calculate the angle, test the angle and rotate the angle around the axis passing through the center point.


Other example extends from this cube class


download the source files

email: ericlin@ms1.hinet.net

¡@