I'm trying to write a script that animates a rubik's cube.
I want to rotate the left side by 90°, then the front by -90°
Step 1. (rotate left side by 90°):
Step 2. (rotate front side by -90°):
I've wrote a script that rotates the corresponding cubes (the whole rubik's cube is made out of 27 little cubs) around the corresponding axis. I'm using cube.rotation_euler[axis] += angle
to rotate the left cubes 90° around the Y-axis (green) and then the now front cubes -90° around the X-axis (red). Both axis are oriented so, that the pos. direction is in the right half of the image, going to the right. But my rubik's cube looks like this:
If check the rotation in the transform tab of the most top right cube, in this image, it shows:
X: -90°
Y: 90°
Z: 0°
This makes sense to me, since this cube got rotated twice.
I rotated the cubes again manually by selecting them in the 3D viewer and using the rotate tool. After Step 1. (90° around Y), the cube had a rotation of:
X: 0°
Y: 90°
Z: 0°
Exactly what I'd expect. But after Step 2. (-90° around X) it shows that the cube has a rotation of:
X: -90°
Y: 0°
Z: -90°
Can some one explain to me what is happening there? Why does the Y and Z rotation suddenly change? I assume some coordinate transformation from relative to absolut coordinates or the other way around maybe. What do I have to use in python to mimic the behaviour of the rotation tool in the 3D view. How do I figure out what euler angle I need? It should be scalable for many rotations back to back. Or is there maybe a different rotation mode so that I don't have to use euler?
EDIT: I found a way using bpy.ops.transform.rotate(value=angle, orient_axis=axis)
instead of cube.rotation_euler[axis] += angle
. But I got a new problem. I want to insert a key frame after each rotation. Which works with the following code:
global CURRENT_FRAME
CURRENT_FRAME = int(CURRENT_FRAME + FRAMES_PER_STEP)
bpy.context.scene.frame_set(CURRENT_FRAME)
# select
for cube in cube_list:
cube.select_set(True)
# rotate (global)
bpy.ops.transform.rotate(value=angle, orient_axis=axis)
# deselect
for cube in cube_list:
cube.select_set(False)
The keyframes end up how they are supposed to be, but the frames in between are all over the place. How can I fix that?
EDIT 2: I've implemented the Euler rotation like @Markus von Broady suggested. But I still have the same problem as before. The Keyframes are correct but the animation in between them is not. Here is my full code:
def execute_command(command):
angle = np.pi/2
# rotate one face
if len(command) <= 2:
if len(command) == 2: # invers
angle *= -1
if command[0] == 'u':
face = 'up'
rot = Euler((0, 0, -angle))
elif command[0] == 'd':
face = 'down'
rot = Euler((0, 0, angle))
elif command[0] == 'r':
face = 'right'
rot = Euler((0, -angle, 0))
elif command[0] == 'l':
face = 'left'
rot = Euler((0, angle, 0))
elif command[0] == 'f':
face = 'front'
rot = Euler((-angle, 0, 0))
elif command[0] == 'b':
face = 'back'
rot = Euler((angle, 0, 0))
cube_list = find_subcubes_in_face(face)
# flip whole cube
else:
if command == 'fl_u':
rot = Euler((0, -angle, 0))
elif command == 'fl_d':
rot = Euler((0, angle, 0))
elif command == 'fl_l':
rot = Euler((0, 0, -angle))
elif command == 'fl_r':
rot = Euler((0, 0, angle))
cube_list = SUBCUBES
global CURRENT_FRAME
CURRENT_FRAME = int(CURRENT_FRAME + FRAMES_PER_STEP)
bpy.context.scene.frame_set(CURRENT_FRAME)
for cube in cube_list:
# select
cube.select_set(True)
# rotate
mat = cube.rotation_euler.to_matrix().to_4x4()
rot_mat = rot.to_matrix().to_4x4()
mat = rot_mat @ mat
cube.rotation_euler = mat.to_euler()
# deselect
cube.select_set(False)
# keyframe
keyframe_all_cubes()