[英]How to rotate slices of a Rubik's Cube in python PyOpenGL?
我正在嘗試用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維數組來組織。 旋轉多維數據集的一個切片似乎很容易,但是請注意,如果在切片上旋轉了,則多維數據集的位置會發生變化,必須重新組織。 不僅位置發生變化,(旋轉的)單個立方體的方向也發生變化。
首先,從類Cube
的構造函數中刪除PyGame和OpenGL初始化。 這是錯誤的地方。 在下面將生成27個Cube類型的對象。
每個多維數據集都必須知道它最初位於何處( self.init_i
)以及經過一些旋轉后當前所處的位置( self.current_i
)。 此信息被編碼為一個包含3個元素的列表,每個元素一個。 這些值是在NxNxN魔方立方體的指數在范圍[0,N [。
單個立方體的方向編碼為3維旋轉矩陣 ( 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
要旋轉立方體,位置和方向必須繞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為左)要旋轉軸矢量,必須交換矢量的2個分量,並且其中之一他們倒轉了。
例如,繞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]
分別使用glPushMatrix
和glPushMatrix
。 通過glMultMatrix
可以將一個矩陣與當前矩陣相乘。
以下函數繪制一個多維數據集。 參數angle
, axis
, slice
, dir
,甚至可以通過設置animate=True
並設置參數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
:
for cube in self.cubes:
cube.draw(colors, surfaces, vertices, animate, animate_ang, *action)
在類的實現Cube
適用於任何NxNxN魔方。
有關3x3x3多維數據集,請參見示例程序。 通過鍵1到9向右旋轉立方體的切片,並通過鍵F1到F9向左旋轉立方體的切片:
當然,對於原始代碼,該代碼使用舊版 OpenGL 。 但是方法Cube.transformMat
為單個局部立方體設置通用的4x4模型矩陣。 因此,可以輕松地將此代碼移植到現代OpenGL。
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()
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.