如何在python PyOpenGL中旋转Rubik's Cube的切片? [英] How to rotate slices of a Rubik's Cube in python PyOpenGL?

查看:63
本文介绍了如何在python PyOpenGL中旋转Rubik's Cube的切片?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试用Python创建Rubik's Cube,到目前为止,我已经在视觉上表示了该立方体.在如何实现轮换方面苦苦挣扎.

I'm attempting to create a Rubik's Cube in Python, i have gotten as far as visually representing the cube. Struggling a bit with how to implement rotation.

我想我正在寻求有关如何执行此操作的反馈.我首先想到的是旋转每个多维数据集的顶点,但是运气不好.

I guess i'm asking for feedback as to how to go about doing this. I thought at first of, rotating each cubes set of vertices's, without much luck.

我基本上想从一组多维数据集对象(大小不同)中选择一个切片,并对每个对象执行旋转和平移操作.

I basically want to select a slice from an array of cube objects (of varying size), perform a rotation and a translation on each object.

import pygame
import random
from pygame.locals import *

from OpenGL.GL import *
from OpenGL.GLU import *

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), #Red
    (0,1,0), #Green
    (1,0.5,0), #Orange
    (1,1,0), #Yellow
    (1,1,1), #White
    (0,0,1), #Blue
    )

class Cube():
    '''set the vertices edges and surfaces(colored) for a Cube'''
    def __init__(self):
        '''initiate the display to show the cube'''
        pygame.init()
        display = (800,600)
        pygame.display.set_mode(display, DOUBLEBUF|OPENGL)
        glEnable(GL_DEPTH_TEST) 
        gluPerspective(45, (display[0]/display[1]), 0.1, 50.0)

        glTranslatef(1,1, -40)

    def setVertices(self, xmove, ymove, zmove):
        '''set predefined vertices'''
        xValueChange = xmove
        yValueChange = ymove
        zValueChange = zmove

        newVertices = []

        for vert in vertices:
            newVert = []

            newX = vert[0] + xValueChange
            newY = vert[1] + yValueChange
            newZ = vert[2] + zValueChange

            newVert.append(newX)
            newVert.append(newY)
            newVert.append(newZ)

            newVertices.append(newVert)

        return newVertices

    def CreateCube(self, vertices):
        '''create with OpenGL'''
        glBegin(GL_QUADS)
        x = 0
        for surface in surfaces:
            glColor3fv(colors[x])
            x+=1
            for vertex in surface:
                glVertex3fv(vertices[vertex])
        glEnd()

class EntireCube():
    def __init__(self,typeOfCube):
        self.typeOfCube = typeOfCube
        self.NewCube = Cube()

    def createEntireCube(self):
        '''for each dimension x,y,z make a dictionary containing the vertices to be displayed'''
        self.cubeDict = {}
        count = 0
        for x in range(self.typeOfCube):
            for y in range(self.typeOfCube):
                for z in range(self.typeOfCube):
                    self.cubeDict[count] = self.NewCube.setVertices(x*2.1,y*2.1,z*2.1)
                    count += 1

    def mainloop(self):
        '''key events, creates the matrix of cubes'''
        rotateUpKey, rotateDownKey, rotateLeftKey, rotateRightKey = False, False, False, False
        rotationalSensitivity = 2

        while True:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit()
                    quit()
                if event.type == KEYDOWN:
                    if event.key == K_UP:
                        rotateUpKey = True
                    if event.key == K_DOWN:
                        rotateDownKey = True
                    if event.key == K_LEFT:
                        rotateLeftKey = True
                    if event.key == K_RIGHT:
                        rotateRightKey = True

                if event.type == KEYUP:
                    if event.key == K_UP:
                        rotateUpKey = False
                    if event.key == K_DOWN:
                        rotateDownKey = False
                    if event.key == K_LEFT:
                        rotateLeftKey = False
                    if event.key == K_RIGHT:
                        rotateRightKey = False

            if rotateUpKey:
                glRotatef(rotationalSensitivity,-rotationalSensitivity,0,0)
            if rotateDownKey:
                glRotatef(rotationalSensitivity,rotationalSensitivity,0,0)
            if rotateLeftKey:
                glRotatef(rotationalSensitivity,0,-rotationalSensitivity,0)
            if rotateRightKey:
                glRotatef(rotationalSensitivity,0,rotationalSensitivity,0)

            #eventually implement keysbindings to call function to rotate a slice of the matrix created

            # x = glGetDoublev(GL_MODELVIEW_MATRIX)

            glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)

            for eachCube in self.cubeDict:
                self.NewCube.CreateCube(self.cubeDict[eachCube])

            # glPushMatrix()
            # glRotatef(1,3,1,1)
            # glPopMatrix()

            pygame.display.flip()
            pygame.time.wait(10)

def main():
    NewEntireCube = EntireCube(3) #create a 3x3x3 cube
    NewEntireCube.createEntireCube()
    NewEntireCube.mainloop()

if __name__ == '__main__':
    main()
    pygame.quit()
    quit()

我希望对此有更多了解的人可以为我提供一些有关如何进行的指导.

I'm hoping someone who knows much more about this can give me some guidance as to how to proceed.

推荐答案

魔方可以由3个3x3x3的3维数组组成.旋转多维数据集的一个切片似乎很容易,但是请注意,如果在切片上旋转了,则多维数据集的位置会发生变化,必须重新组织.不仅位置发生了变化,(旋转的)单个立方体的方向也发生了变化.

A rubik's cube can be organized by an 3 dimensional array of 3x3x3 cubes. It seems to be easy to rotate a slice of the cube, but note if on slice is rotated the positions of the cube change and have to be reorganized. Not only the position changes, also the orientation of the (rotated) single cubes changes.

首先,从类Cube的构造函数中删除PyGame和OpenGL初始化.这是错误的地方.下面将生成27个Cube类型的对象.

First of all remove the PyGame and OpenGL initialization form the constructor of the class Cube. That is the wrong place for this. In the following will generate 27 objects of type Cube.

每个多维数据集都必须知道它最初位于何处(self.init_i),以及经过一些旋转后当前所处的位置(self.current_i).此信息被编码为一个包含3个元素的列表,每个元素一个.值是 NxNxN 魔方的立方体索引,范围为 [0,N [].
单个多维数据集的方向以3维旋转矩阵(self.rot)进行编码.旋转矩阵必须由恒等矩阵初始化.

Each cube has to know where it is initially located (self.init_i) and where it is current located after some rotations (self.current_i). This information is encoded in a list with 3 elements, one for each axis. The values are indices of cube in the NxNxN rubik's cube in range [0, N[.
The orientation of a single cube is encoded in 3 dimensional Rotation matrix (self.rot). The rotation matrix has to be initialized by the identity matrix.

class Cube():
    def __init__(self, id, N, scale):
        self.N = N
        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)]

创建27个多维数据集的列表

Create a list of the 27 cubes

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

如果旋转魔方的一部分,则必须检查哪个魔方受到了影响.这可以通过检查切片是否与当前位置的旋转轴输入匹配来完成.

If a slice of the rubik's cube is rotated, then it has to be checked which of the single cubes is affected. This can be done by checking if the slice matches the entry of the rotation axis of the current position.

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

要旋转立方体,位置和方向必须围绕axis旋转90°. 3维旋转矩阵由3个方向向量组成.可以通过交换矢量坐标并向右旋转结果的x坐标,向左旋转结果的y坐标,来旋转d维向量:

To rotate a cube, the position and the orientation has to be rotated by 90° around an axis. A 3 dimension rotation matrix consists of 3 direction vectors. A d dimensional vector can be rotated by swapping the coordinates of the vector and inverting the x coordinate of the result for a right rotation and inverting the y coordinate of the result for a left rotation:

rotate right: (x, y) -> (-y, x)
rotate left:  (x, y) -> (y, -x)

由于旋转矩阵的所有向量都在轴对齐的平面中,因此该算法可用于更改立方体的方向和位置. axis旋转轴(x = 0,y = 1,z = 2)并且dir是旋转方向( 1 是右方向,- 1 左) 要旋转轴矢量,必须交换矢量的两个分量,并将其中之一反转.

Since all the vectors of the rotation matrix are in an axis aligned plane this algorithm can be used to change the orientation and the position of the cube. axis the rotation axis (x=0, y=1, z=2) and dir is the rotation direction (1 is right and -1 left) To rotate the axis vector, 2 components of the vector have to be swapped and one of them inverted.

例如,绕Y轴向左旋转:

e.g rotate left around the Y-axis:

(x, y, z) -> (z, y, -x)

旋转位置时,必须交换索引.反转索引意味着将索引i映射到索引N-1-i:

When the position is rotated, then the indices have to be swapped. Inverting an index means to map the index i to the index N-1-i:

例如,绕着Y轴向左旋转:

e.g rotate left around the Y-axis:

(ix, iy, iz) -> (iz, iy, N-1-ix)

单个立方体的旋转:

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] )

当必须绘制多维数据集时,可以使用多维数据集的当前位置(self.current_i)和方向self.rot来设置4x4转换矩阵:

When the cube has to be drawn, then the current position of the cube (self.current_i) and the orientation self.rot can be used to set up 4x4 transformation matrix:

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]

分别使用 glPushMatrix glPushMatrix .通过 glMultMatrix 可以乘以一个矩阵到当前矩阵.
以下函数绘制一个多维数据集.参数angleaxisslicedir甚至可以通过设置animate=True并设置参数angleaxisslice:

With glPushMatrix respectively glPushMatrix. By glMultMatrix a matrix can be multiplied to the current matrix.
The following function draws a single cube. The parameters angle, axis, slice, dir and it can even apply an animation to the cube, by setting animate=True and setting parameters angle, axis, slice, dir:

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()

要绘制立方体,只需在循环中调用方法draw即可:

To draw the cubes, it is sufficient to call the method draw in a loop:

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

Cube的实现适用于任何 NxNxN Rubik的多维数据集.

The implementation of the class Cube works for any NxNxN Rubik's cube.

请参见示例程序,了解 3x3x3 多维数据集.通过 1 9 键向右旋转多维数据集的切片,并通过 F1 F9键向左旋转多维数据集:

See the example program for a 3x3x3 cube. The slices of the cube are rotated to the right by the keys 1 to 9 and to the left by the keys F1 to F9:

当然,代码使用 Legacy OpenGL 关于您的原始代码.但是方法Cube.transformMat为单个局部立方体设置通用的4x4模型矩阵.因此,可以轻松地将此代码移植到现代OpenGL.

Of course the code uses the Legacy OpenGL in regard to your original code. But the method Cube.transformMat sets a general 4x4 model matrix for a single partial cube. Thus it is possible to port this code to modern OpenGL with ease.

import pygame
import random
from pygame.locals import *

from OpenGL.GL import *
from OpenGL.GLU import *

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))

class Cube():
    def __init__(self, id, N, scale):
        self.N = N
        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 EntireCube():
    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]

    def mainloop(self):

        rot_cube_map  = { K_UP: (-1, 0), K_DOWN: (1, 0), K_LEFT: (0, -1), K_RIGHT: (0, 1)}
        rot_slice_map = {
            K_1: (0, 0, 1), K_2: (0, 1, 1), K_3: (0, 2, 1), K_4: (1, 0, 1), K_5: (1, 1, 1),
            K_6: (1, 2, 1), K_7: (2, 0, 1), K_8: (2, 1, 1), K_9: (2, 2, 1),
            K_F1: (0, 0, -1), K_F2: (0, 1, -1), K_F3: (0, 2, -1), K_F4: (1, 0, -1), K_F5: (1, 1, -1),
            K_F6: (1, 2, -1), K_F7: (2, 0, -1), K_F8: (2, 1, -1), K_F9: (2, 2, -1),
        }  

        ang_x, ang_y, rot_cube = 0, 0, (0, 0)
        animate, animate_ang, animate_speed = False, 0, 5
        action = (0, 0, 0)
        while True:

            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit()
                    quit()
                if event.type == KEYDOWN:
                    if event.key in rot_cube_map:
                        rot_cube = rot_cube_map[event.key]
                    if not animate and event.key in rot_slice_map:
                        animate, action = True, rot_slice_map[event.key]
                if event.type == KEYUP:
                    if event.key in rot_cube_map:
                        rot_cube = (0, 0)

            ang_x += rot_cube[0]*2
            ang_y += rot_cube[1]*2

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

            glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)

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

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

            pygame.display.flip()
            pygame.time.wait(10)

def main():

    pygame.init()
    display = (800,600)
    pygame.display.set_mode(display, DOUBLEBUF|OPENGL)
    glEnable(GL_DEPTH_TEST) 

    glMatrixMode(GL_PROJECTION)
    gluPerspective(45, (display[0]/display[1]), 0.1, 50.0)

    NewEntireCube = EntireCube(3, 1.5) 
    NewEntireCube.mainloop()

if __name__ == '__main__':
    main()
    pygame.quit()
    quit()

这篇关于如何在python PyOpenGL中旋转Rubik's Cube的切片?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆