我正在尝试用Python创建Rubik’s Cube,我已经从视觉上表示了这个立方体。如何实施轮换方面有些困难。
我想我正在寻求有关如何执行此操作的反馈。我首先想到的是旋转每个立方体的一组顶点,但是运气不好。
我基本上想从一组多维数据集对象(大小不一)中选择一个切片,对每个对象执行旋转和平移。
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()
我希望对此有更多了解的人可以为我提供一些有关如何进行的指导。
魔方可以通过 3x3x3 多维数据集的3维数组来组织。旋转多维数据集的一个切片似乎很容易,但是请注意,如果在切片上旋转,则多维数据集的位置会发生变化,必须重新组织。不仅位置发生变化,(旋转的)单个立方体的方向也发生变化。
首先,从类的构造函数中删除PyGame和OpenGL初始化Cube。这是错误的地方。在下面将生成27个Cube类型的对象。
Cube
每个多维数据集都必须知道它最初位于的位置(self.init_i)和经过一些旋转后当前的位置(self.current_i)。该信息被编码为一个包含3个元素的列表,每个元素一个。的值是在该立方体的索引 NxNxN 魔方在范围 [0,N [ 。 单个立方体的方向编码为3维旋转矩阵(self.rot)。旋转矩阵必须由恒等矩阵初始化。
self.init_i
self.current_i
self.rot
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
要旋转立方体,位置和方向必须围绕旋转90° axis。3维旋转矩阵由3个方向向量组成。可以通过交换向量的坐标,并向右旋转结果的x坐标,向左旋转结果的y坐标,来旋转d维向量:
axis
rotate right: (x, y) -> (-y, x) rotate left: (x, y) -> (y, -x)
由于旋转矩阵的所有向量都在轴对齐的平面中,因此该算法可用于更改立方体的方向和位置。axis旋转轴 (x = 0,y = 1,z = 2) 且dir是旋转方向( 1 右, -1 左)要旋转轴矢量,必须交换矢量的2个分量,并将其中之一反转。
dir
例如,绕Y轴向左旋转:
(x, y, z) -> (z, y, -x)
旋转位置时,必须交换索引。反转索引意味着将索引映射i到索引N-1-i:
i
N-1-i
(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]
用glPushMatrix分别glPushMatrix。通过glMultMatrix矩阵可以乘到当前矩阵。 以下函数绘制一个多维数据集。参数angle,axis,slice,dir它甚至可以应用动画的立方体,通过设置animate=True和参数设置angle,axis,slice,dir:
glPushMatrix
glMultMatrix
angle
slice
animate=True
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在循环中调用该方法即可:
draw
for cube in self.cubes: cube.draw(colors, surfaces, vertices, animate, animate_ang, *action)
该类的实现Cube适用于任何 NxNxN Rubik的多维数据集。
有关 3x3x3 立方体的信息,请参见示例程序。多维数据集的片由键向右旋转1到9和通过按键左F1到F9:
1
9
F1
F9
当然,对于原始代码,该代码使用 旧版 OpenGL。但是该方法Cube.transformMat为单个局部立方体设置了通用的4x4模型矩阵。因此,可以轻松地将此代码移植到现代OpenGL。
Cube.transformMat
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()