Justin McDonald
Justin McDonald

Reputation: 2166

Cannot rotate from one specific orientation to another

The example I have is a rotating, animated cube using CSS3 transformations and transitions.

The problem is that there seems to be no possible CSS3 rotation from the starting position in this jsfiddle to show the '4' face (one rotation left).

Basic Instructions for JSFiddle: the 'x' key styles the cube with the CSS3 rotateX:90deg, 'y' for rotateY and 'z' for rotateZ. Click the 'Reset' button to return to the starting position. NOTICE* No matter what rotation (x, y or z), I CANNOT get the cube to rotate left one position.

I am working with the CSS3 rotate3d properties rotateX, rotateY and rotateZ with a CSS3 transition affecting the transform attribute.

Also, it is worth mentioning that using the rotateX/Y/Z properties allow the cube to rotate up, left, right or down in almost ALL other positions/orientations.

Questions:

What is the problem here? Is this rotation impossible? Is there a problem with the CSS3 rotations, or is the problem in my code/logic?

The best answer would explain how the CSS3 rotate3d transformations work (behind the scenes; mathematically).

Upvotes: 4

Views: 771

Answers (2)

user1726343
user1726343

Reputation:

The transforms occurring here are compound transforms (in that the transform matrices undergo matrix concatenation). When you rotate the element around its x-axis, for example, the other axes become rotated by 90 degrees, so that rotation about the y-axis no longer has the right to left effect it originally did. Instead, it now behaves like an inverted z-axis. Consider the following diagram:

enter image description here

The math underlying the problem is this: when you apply multiple transformations to a point, all of their transformation matrices are multiplied together with the point itself, left to right in the order of application. So for example let us say the matrix for 90 degree rotation about x is Rx, the matrix for 90 degree rotation about y is Ry and the matrix for 90 degree rotation about z is Rz.

Let us say we pressed z twice and x once. Now, for any point P in the element, its new position P' will be calculated as:

P' = Rz . Rz . Rx . P

Unfortunately, the problem with this is that each subsequent transformation will apply to the reference frame that resulted from the last calculation. So in this case the x axis will be pointing in entirely the opposite direction by the time we rotate about it.

The solution to this problem (the mathematics of which I won't go into) is to multiply the matrices in reverse order. If you do this, each consecutive transformation will be relative to fixed axes, instead of relative to whatever state the previous transformation left the axes in. So our calculation would look like this:

P' = Rx . Rz . Rz . P

After fiddling around with WebKitCSSMatrix for a bit, I managed to come up with the following solution:

// Make a matrix out of the transformation currently in effect
var oldMatrix = new WebKitCSSMatrix(cube[0].style.webkitTransform);

//Make a matrix that applies the new rotation
var extraRotate = (new WebKitCSSMatrix()).rotate(anglex, angley, anglez);

//Multiply them (with the new one on the left)
var final = extraRotate.multiply(oldMatrix);

//I need to extract the elements of the final matrix into an array. 
//They are stored like this: 1st row 1st column in key m11, 2nd row
//1st column in key m12, etc.
var finalstring = [];
for (var i = 1; i < 5; i++) {
    for (var j = 1; j < 5; j++) {
        finalstring.push(Math.round(final['m' + i + j]));
    }
}

//Make a new style rule out of our final matrix and apply it to the element
cube[0].style.webkitTransform = 'matrix3d(' + finalstring.join(',') + ')';

This manages to get around the problem of a rotating reference frame, by pre-multiplying the existing transformation matrix with the additional rotation desired. Notice how I calculate the extraRotate matrix first, then multiply it to the existing transformation, effectively multiplying the matrices in reverse order of application:

P' = Rnew . Rold . P

This calculates new point vectors relative to world coordinates instead of relative to the coordinates post transformation.

As a bonus, this also obviates the need for global variables, since the current transformation matrix of the element is the only information we need.

TL;DR

I've included an updated fiddle below. Press y to rotate your cube 90 degrees to the right.

Here is a demonstration: http://jsfiddle.net/guruB/

(As before, use z,x and y to rotate about the corresponding axes)

Note: You can find out how to calculate rotation matrices here.

Upvotes: 8

bobthyasian
bobthyasian

Reputation: 943

Take a look at this demo

It uses this code to rotate left:

#cube.show-left {
      -webkit-transform: translateZ( -100px ) rotateY(   90deg );
         -moz-transform: translateZ( -100px ) rotateY(   90deg );
           -o-transform: translateZ( -100px ) rotateY(   90deg );
              transform: translateZ( -100px ) rotateY(   90deg );

Upvotes: -1

Related Questions