Home > database >  How to draw animation in pyopengltk framework
How to draw animation in pyopengltk framework

Time:12-03

I am using pyopengl, tkinter, pyopengltk to draw a Rubik's cube and am going to implement a Rubik's cube recovery animation, now I have implemented to display a Rubik's cube in tkinter with this quiz.

import tkinter as tk
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
from pyopengltk import OpenGLFrame

vertices = (
    (1, -1, -1), (1, 1, -1), (-1, 1, -1), (-1, -1, -1),
    (1, -1, 1), (1, 1, 1), (-1, -1, 1), (-1, 1, 1)
)
edges = ((0, 1), (0, 3), (0, 4), (2, 1), (2, 3), (2, 7), (6, 3), (6, 4), (6, 7), (5, 1), (5, 4), (5, 7))
surfaces = ((0, 1, 2, 3), (3, 2, 7, 6), (6, 7, 5, 4), (4, 5, 1, 0), (1, 5, 7, 2), (4, 0, 3, 6))
colors = ((1, 0, 0), (0, 1, 0), (1, 0.5, 0), (1, 1, 0), (1, 1, 1), (0, 0, 1))

rot_cube_map = {'Up': (-1, 0), 'Down': (1, 0), 'Left': (0, -1), 'Right': (0, 1)}
rot_slice_map = {
    '1': (0, 0, 1), '2': (0, 1, 1), '3': (0, 2, 1), '4': (1, 0, 1), '5': (1, 1, 1),
    '6': (1, 2, 1), '7': (2, 0, 1), '8': (2, 1, 1), '9': (2, 2, 1),
    'F1': (0, 0, -1), 'F2': (0, 1, -1), 'F3': (0, 2, -1), 'F4': (1, 0, -1), 'F5': (1, 1, -1),
    'F6': (1, 2, -1), 'F7': (2, 0, -1), 'F8': (2, 1, -1), 'F9': (2, 2, -1),
}

class Cube():
    def __init__(self, id, N, scale):
        self.N = 3
        self.scale = scale
        self.init_i = [*id]
        self.current_i = [*id]  # 表示填充,一个变量值代替多个
        self.rot = [[1 if i == j else 0 for i in range(3)] for j in range(3)]

    def isAffected(self, axis, slice, dir):
        return self.current_i[axis] == slice

    def update(self, axis, slice, dir):

        if not self.isAffected(axis, slice, dir):
            return

        i, j = (axis   1) % 3, (axis   2) % 3
        for k in range(3):
            self.rot[k][i], self.rot[k][j] = -self.rot[k][j] * dir, self.rot[k][i] * dir

        self.current_i[i], self.current_i[j] = (
            self.current_i[j] if dir < 0 else self.N - 1 - self.current_i[j],
            self.current_i[i] if dir > 0 else self.N - 1 - self.current_i[i])

    def transformMat(self):
        scaleA = [[s * self.scale for s in a] for a in self.rot]
        scaleT = [(p - (self.N - 1) / 2) * 2.1 * self.scale for p in self.current_i]
        return [*scaleA[0], 0, *scaleA[1], 0, *scaleA[2], 0, *scaleT, 1]

    def draw(self, col, surf, vert, animate, angle, axis, slice, dir):

        glPushMatrix()
        if animate and self.isAffected(axis, slice, dir):
            glRotatef(angle * dir, *[1 if i == axis else 0 for i in range(3)])  # 围着这个坐标点旋转
        glMultMatrixf(self.transformMat())

        glBegin(GL_QUADS)
        for i in range(len(surf)):
            glColor3fv(colors[i])
            for j in surf[i]:
                glVertex3fv(vertices[j])
        glEnd()

        glPopMatrix()


class mycube():
    def __init__(self, N, scale):
        self.N = N
        cr = range(self.N)
        self.cubes = [Cube((x, y, z), self.N, scale) for x in cr for y in cr for z in cr]  # 创建27

    def maindd(self):
        for cube in self.cubes:
            cube.draw(colors, surfaces, vertices, False, 0, 0, 0, 0)

class GLFrame(OpenGLFrame):
    def initgl(self):
        self.animate = True
        self.rota = 0
        self.count = 0

        self.ang_x, self.ang_y, self.rot_cube = 0, 0, (0, 0)
        self.animate1Cube, self.animate_ang, self.animate_speed = False, 0, 2
        self.action = (0, 0, 0)
        glClearColor(0.0, 0.0, 0.0, 0.0)  # 背景黑色
        # glViewport(400, 400, 200, 200)  # 指定了视口的左下角位置

        glEnable(GL_DEPTH_TEST)  # 开启深度测试,实现遮挡关系
        glDepthFunc(GL_LEQUAL)  # 设置深度测试函数(GL_LEQUAL只是选项之一)

        glMatrixMode(GL_PROJECTION)  
        glLoadIdentity()  # 恢复原始坐标
        gluPerspective(30, self.width / self.height, 0.1, 50.0)

        self.N = 3
        cr = range(self.N)
        self.cubes = [Cube((x, y, z), self.N, 1.5) for x in cr for y in cr for z in cr]

    def keydown(self, event):
        if event.keysym in rot_slice_map:
            self.animate1Cube, self.action = True, rot_slice_map[event.keysym]
        if event.keysym in rot_cube_map:
            self.rot_cube = rot_cube_map[event.keysym]

    def keyup(self, event):
        if event.keysym in rot_cube_map:
            self.rot_cube = (0, 0)

    def redraw(self):
        self.ang_x  = self.rot_cube[0] * 2
        self.ang_y  = self.rot_cube[1] * 2

        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity()
        glTranslatef(0, 0, -40)
        glRotatef(self.ang_y, 0, 1, 0)
        glRotatef(self.ang_x, 1, 0, 0)

        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

        if self.animate1Cube:
            if self.animate_ang >= 90:
                for cube in self.cubes:
                    cube.update(*self.action)
                self.animate1Cube, self.animate_ang = False, 0

        for cube in self.cubes:
            cube.draw(colors, surfaces, vertices, self.animate, self.animate_ang, *self.action)
        if self.animate1Cube:
            self.animate_ang  = self.animate_speed


class App(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title('rubiks cube')
        self.glframe = GLFrame(self, width=800, height=600)
        #self.bind("<Key>", self.glframe.key)
        self.bind("<KeyPress>", self.glframe.keydown)
        self.bind("<KeyRelease>", self.glframe.keyup)
        self.glframe.pack(expand=True, fill=tk.BOTH)
        # self.glframe.focus_displayof()
        # self.animate = True

App().mainloop()

If you want to animate the cube automatically, you need to animate instead of keyboard events. Define a list of animationg. e.g.:

animation_list = ['1', '3', '5', 'F2']

Set self.action from the list. e.g.:

class GLFrame(OpenGLFrame):
    # [...]

    def redraw(self):
        if not self.animate1Cube and animation_list:
            self.animate1Cube, self.action = True, rot_slice_map[animation_list[0]]
            del animation_list[0]

        # [...]
  • Related