如何在python PyOpenGL中旋转Rubik's Cube的切片?

时间:2018-05-12 06:46:17

标签: python python-3.x opengl pygame pyopengl

我正在尝试用Python创建一个魔方,我已经在视觉上代表了多维数据集。在如何实现旋转方面苦苦挣扎。

我想我正在征求关于如何做到这一点的反馈。我首先想到,旋转每个立方体顶点组,没有太多运气。

我基本上想从一组立方体对象(不同大小)中选择一个切片,在每个对象上执行旋转和平移。

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

我希望有更多了解此事的人可以就如何继续提供一些指导。

1 个答案:

答案 0 :(得分:3)

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

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

每个多维数据集都必须知道它的初始位置(self.init_i)和经过一些旋转后当前的位置(self.current_i)。此信息被编码为一个包含3个元素的列表,每个元素一个。值是 NxNxN 魔方的立方体的索引,范围为 [0,N []。
单个多维数据集的方向以3维Rotation matrixself.rot)进行编码。旋转矩阵必须由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个立方体的列表

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

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

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

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

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

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

例如,绕Y轴向左旋转:

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

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

例如,绕Y轴向左旋转:

(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转换矩阵:

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]

分别使用glPushMatrixglPushMatrix。通过glMultMatrix,可以将矩阵与当前矩阵相乘。
以下函数绘制一个多维数据集。参数angleaxisslicedir,甚至可以通过设置animate=True和参数{{1}将动画应用于立方体},angleaxisslice

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

for cube in self.cubes: cube.draw(colors, surfaces, vertices, animate, animate_ang, *action) 的实现适用于任何 NxNxN Rubik的多维数据集。

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

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

Cube.transformMat
相关问题