rubiks cube animation of face rotation rotating in weird ways

65 views Asked by At

Its kind of hard to explain whats wrong with my program so ive made a video to visually show you https://youtu.be/j8yrB2BQUrI

As the video shows the rotations which involve y and z axis rotations do not work properly. Ive added the code below and im aware ive added a lot of it so sorry.

The rotateX/Y/Z functions simply update the rotation struct for each individual cube and thats it so i havent added that code in. Sorry for the poorly written question ive just tried everything i can think of and cant get it to work

`

        private async void L(int n)         //This is a working function
        {
            for (int i = 0; i < 15 * n; i++)
            {
                cube[0].rotateX(6 *DEGREE);
                cube[1].rotateX(6 * DEGREE);        //rotates the left face of the cube 90 degrees
                cube[2].rotateX(6 * DEGREE);
                cube[9].rotateX(6 * DEGREE);
                cube[10].rotateX(6 * DEGREE);
                cube[11].rotateX(6 * DEGREE);
                cube[17].rotateX(6 * DEGREE);
                cube[18].rotateX(6 * DEGREE);
                cube[19].rotateX(6 * DEGREE);
                Invalidate(rec);
                await Task.Delay(1);
            }
            cube[0].rotateX(-90 * DEGREE);      // here it quickly rotates the cube back to how it was originally
            cube[1].rotateX(-90 * DEGREE);
            cube[2].rotateX(-90 * DEGREE);
            cube[9].rotateX(-90 * DEGREE);
            cube[10].rotateX(-90 * DEGREE);
            cube[11].rotateX(-90 * DEGREE);
            cube[17].rotateX(-90 * DEGREE);
            cube[18].rotateX(-90 * DEGREE);
            cube[19].rotateX(-90 * DEGREE);
            CubeFaces = Rotations.L(1, CubeFaces);      // this changes the colours of the left face and then updates the display
            Invalidate(rec);                            // to make it seem as if the cube has rotated
        }

        private async void R(int n)         // this is also a working function
        {
            for (int i = 0; i < 15 * n; i++)
            {
                cube[6].rotateX(-6 * DEGREE);
                cube[7].rotateX(-6 * DEGREE);
                cube[8].rotateX(-6 * DEGREE);
                cube[14].rotateX(-6 * DEGREE);
                cube[15].rotateX(-6 * DEGREE);
                cube[16].rotateX(-6 * DEGREE);
                cube[23].rotateX(-6 * DEGREE);
                cube[24].rotateX(-6 * DEGREE);
                cube[25].rotateX(-6 * DEGREE);
                await Task.Delay(1);
                Invalidate(rec);
            }
            cube[6].rotateX(90 * DEGREE);
            cube[7].rotateX(90 * DEGREE);
            cube[8].rotateX(90 * DEGREE);
            cube[14].rotateX(90 * DEGREE);
            cube[15].rotateX(90 * DEGREE);
            cube[16].rotateX(90 * DEGREE);
            cube[23].rotateX(90 * DEGREE);
            cube[24].rotateX(90 * DEGREE);
            cube[25].rotateX(90 * DEGREE);
            CubeFaces = Rotations.R(1, CubeFaces);
            Invalidate(rec);
        }

        private async void U(int n)         // this function does not work. you will see why when i run it
        {
            for (int i = 0; i < 15 * n; i++)
            {
                cube[0].rotateY(6 * DEGREE);
                cube[3].rotateY(6 * DEGREE);
                cube[6].rotateY(6 * DEGREE);
                cube[9].rotateY(6 * DEGREE);
                cube[12].rotateY(6 * DEGREE);
                cube[14].rotateY(6 * DEGREE);
                cube[17].rotateY(6 * DEGREE);
                cube[20].rotateY(6 * DEGREE);
                cube[23].rotateY(6 * DEGREE);

                //cube[0].rotateX(cube[0].GetRotation().z);
                //cube[3].rotateX(-rotation.x);
                //cube[6].rotateX(-rotation.x);
                //cube[9].rotateX(-rotation.x);
                //cube[12].rotateX(-rotation.x);
                //cube[14].rotateX(-rotation.x);
                //cube[17].rotateX(-rotation.x);
                //cube[20].rotateX(-rotation.x);
                //cube[23].rotateX(-rotation.x);
                await Task.Delay(1);
                Invalidate();
            }
        }

        private async void F(int n)     // this also doesnt work and you will see why when i run it
        {
            for (int i = 0; i < 15 * n; i++)
            {
                cube[0].rotateZ(6 * DEGREE);            // so the x axis rotations work however the y and z do not
                cube[1].rotateZ(6 * DEGREE);
                cube[2].rotateZ(6 * DEGREE);
                cube[3].rotateZ(6 * DEGREE);
                cube[4].rotateZ(6 * DEGREE);
                cube[5].rotateZ(6 * DEGREE);
                cube[6].rotateZ(6 * DEGREE);
                cube[7].rotateZ(6 * DEGREE);
                cube[8].rotateZ(6 * DEGREE);
                await Task.Delay(1);
                Invalidate();
            }
            //cube[0].rotateZ(-90 * DEGREE);
            //cube[1].rotateZ(-90 * DEGREE);
            //cube[2].rotateZ(-90 * DEGREE);
            //cube[3].rotateZ(-90 * DEGREE);
            //cube[4].rotateZ(-90 * DEGREE);
            //cube[5].rotateZ(-90 * DEGREE);
            //cube[6].rotateZ(-90 * DEGREE);
            //cube[7].rotateZ(-90 * DEGREE);
            //cube[8].rotateZ(-90 * DEGREE);
            //CubeFaces = Rotations.F(1, CubeFaces);
        }

`

i tried to do what is commented out in the U function and i was thinking it could counteract the cube rotating in its weird ways but it just made it worse

2

There are 2 answers

0
L Chougrani On

You'll need to use Quaternion rotation, else you'll end up with either "guimbal effetc". Quaternion can be calculated from axis/angle and are cumulative. you'll just need to either do:

  • Define a quaternion rotation for your model "MyQuaternion"
  • Compute the quaternion dedicated to the rotation you want to perform "RotationQuaternion".

then on rotating :

  • MyQuaternion = MyQuaternion * RotationQuaternion
  • MyQuaternion = RotationQuaternion * MyQuaternion

depending if you want the rotation in local or global space.

you can find examples on Git.

0
robthebloke On

Ok, so let's write down a few definitions here.

  1. You have 9 separate planes of rotation.
  2. i.e. 3 in x, 3 in y, and 3 in z.
  3. Each of these planes of rotation have their own unique pivot point (however the middle planes along each axis are already centred on the origin, so those cases are easier, given the pivot is already at the origin)
  4. For any of these rotations, you wish to apply an animated rotation around a given pivot.
  5. You have 6 centre faces (with 1 colour)
  6. You have 8 corner pieces (with 3 colours)
  7. You have 12 edge pieces (with 2 colours)
  8. There is a centre piece that has no colour, which you can probably ignore, however to make the code a little easier to follow, I'm going to pretend it exists...

How you track and identify which of the 26/27 pieces is up to you really, so I'm going to assume you have that working....

Each of those pieces has a current position, and a current orientation. There is a nice way to represent all of this information in one complete entity, and that's a 4x4 matrix (storing separate translation and Euler rotations is probably your error here!). It should be mentioned that you could represent those with a position + quaternion, but that's not really needed here.

Initially, for those 27 pieces, the all have the same orientation, but differing translation values. So first off, initialise those transforms.

mat4x4 block_orientations[3 * 3 * 3];

for(int z = 0; z < 3; ++z) {
  for(int y = 0; y < 3; ++y) {
    for(int x = 0; x < 3; ++x) {
       int index = z * 9 + y * 3 + x;
       block_orientations[index] = mat4x4_translate_matrix(x-1, y-1, z-1); 
    }
  }
}

You also have the 9 rotation planes, and associated pivot points.

enum CubeRotationPlane {
   XMin,
   XMid,
   XMax,

   YMin,
   YMid,
   YMax,

   ZMin,
   ZMid,
   ZMax,
};

vec4 pivot_points[9] {
   // pivots along the x axis
   {-1, 0, 0, 1},
   {0, 0, 0, 1},
   {1, 0, 0, 1},
   // pivots along the y axis
   {0, -1, 0, 1},
   {0, 0, 0, 1},
   {0, 1, 0, 1},
   // pivots along the z axis
   {0, 0, -1, 1},
   {0, 0, 0, 1},
   {0, 0, 1, 1},
};

In a mathematically correct sense, in order to transform those blocks you should do:

  • Translate the blocks to the pivot point (of the plane!)
  • Rotate
  • Translate the blocks back from the pivot (of the plane!)

In code this would look like:

mat4x4 inverse_pivot = translateMatrix(-pivot);
mat4x4 rotation = computeYourRotationOffsetAsMatrix();
mat4x4 pivot = translateMatrix(pivot);

// this assumes the multiplication order of (child * parent),
// reverse if your lib is (parent * child).
mat4x4 final = inverse_pivot * rotation * pivot;

In this case however, since you are only using axis aligned rotations, you can actually skip the pivot points entirely, and just jump straight to the rotations....

void compute_transforms_for_9_blocks_to_be_moved(
    CubeRotationPlane plane, ///< the plane of the cube being rotated
    int block_indices[9],    ///< the 9 blocks that need to be rotated
    mat4x4 initial_block_orientations[3 * 3 * 3],
    mat4x4 current_block_orientations[3 * 3 * 3],
    float degrees) {

    // the rotation to apply
    mat4x4 rotate_matrix;

    if(plane < yMin) {
        // rotating around X
        rotate_matrix = mat4x4_rotateX(degrees * M_PI / 180);
    }
    else
    if(plane < zMin) {
        // rotating around Y
        rotate_matrix = mat4x4_rotateY(degrees * M_PI / 180);
    }
    else
    if(plane) {
        // rotating around Z
        rotate_matrix = mat4x4_rotateZ(degrees * M_PI / 180);
    }

    // copy prior values
    memcpy(current_block_orientations, 
           initial_block_orientations, 
           sizeof(mat4x4) * 27);

    // now update the 9 blocks that are in motion... 
    for(int i = 0; i < 9; ++i) {
        int index = block_indices[i];
        current_block_orientations[index] = 
              initial_block_orientations[index] * rotate_matrix;
    }
}

This assumes that once your animation has been completed, you'll update the entire set of matrices.

Alternatively, you could specify the rotations above for 1 degree, and just return the final matrix (if you just want to accumulate the transforms when animating, avoiding the temporary array). Doing this Might however accumulate floating point error over time.

It is essential to realise that you MUST store the accumulated transforms for your blocks. The problem you are seeing in your code above is that you have an XYZ Euler rotation order (and you are just updating the Euler values). So this will work for the X and Z rotations, however as soon as you touch the middle rotation, the Z axis is no longer in it's original plane, and stuff starts going wrong!