Files
Python-2019/rubiks cube.py
marc 2a0c588e14 modif rubikcs cube
Test de l'affichage des cubes en cours....
2019-05-17 16:00:40 +02:00

346 lines
13 KiB
Python

from pyglet.gl import *
from pyglet.window import key
import numpy as np
import math
verticies = (
(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),
(0,0,1),
(0,1,0),
(1,1,1),
(0,1,1),
(1,0,0),
(0,1,0),
(0,0,1),
(1,0,0),
(1,1,1),
(0,1,1),
)
BLACK = ( 0, 0, 0)
WHITE = (255,255,255)
RED = (255, 0, 0)
GREEN = ( 0,255, 0)
YELLOW = (255,255, 0)
BLUE = ( 0, 0,255)
ORANGE = (200,100, 0)
BLACK = (0,0,0)
WHITE = (1,1,1)
RED = (1,0,0)
GREEN = (0,1,0)
BLUE = (0,0,1)
YELLOW = (1,1,0)
ORANGE = (0.8,0.5,0)
class Window(pyglet.window.Window):
def __init__(self,*args,**kwargs):
"""Create pyglet window."""
super().__init__(*args,**kwargs) #Default parameters of the pyglet window
self.set_minimum_size(400,300) #Minimum size of the window when resizing
self.fovy=70 #Field of view in degrees in the y direction
self.aspect=self.width/self.height #Field of view in x direction, ration width by height, needed to get the field of view in the x direction I guess
self.zNear=0.5 #distance of the viewer from the near clipping plane, because the camera send rays from a point and the clipping plane is a surface
self.zFar=1000 #distance of the viewer from the far clipping plane because the rays cannot reach infinity
self.keys = key.KeyStateHandler() #Create an object of the pyglet key type for event detection
self.push_handlers(self.keys) #Allow automatic update of the keys of the window each turn
pyglet.clock.schedule(self.update)
self.mouse_lock=True #Lock the mouse for further player controls
self.rubiks_cube=RubiksCube() #Rubiks cube to play with
#pyglet.clock.schedule(self.update) #Set the clock for loop turns using pyglet update attribute
self.player=Player((0.5,1.5,1.5),(-30,0)) #Create a player using default view parameters
def on_draw(self): #Main draw function
"""Draw the objects of the pyglet window."""
self.clear() #Clear the buffers
self.set3d() #3d environment for opengl
self.push(self.player.position,self.player.rotation) #Apply the matrix operations to set the world's position
self.rubiks_cube.draw() #Load the matrix on the buffer
glPopMatrix() #Pop the current matrix stack defined in the rubiks cube
def setLock(self,state):
"""Lock the mouse."""
self.lock = state
self.set_exclusive_mouse(state)
lock = False
mouse_lock = property(lambda self:self.lock,setLock)
def on_mouse_motion(self,x,y,dx,dy):
"""Allow the player to change its parameters."""
if self.mouse_lock: self.player.mouse_motion(dx,dy)
def on_key_press(self,KEY,MOD):
"""Allow the user to end the demonstration."""
if KEY == key.ESCAPE: self.close()
#elif KEY == key.E: self.mouse_lock = not self.mouse_lock
self.mouse_lock=True #I have no idea why this thing is needed
def Projection(self):
"""Load the projection.?"""
glMatrixMode(GL_PROJECTION) #Allow 2 levels of depth
glLoadIdentity() #Create the identity matrix ready to apply operations on in the push function
def Model(self):
"""Load the model."""
glMatrixMode(GL_MODELVIEW) #Allow any levels of depth
glLoadIdentity() #Create the identity matrix ready to apply operations on in the push function
def set2d(self):
"""Set a 2d environment."""
self.Projection()
gluOrtho2D(0,self.width,0,self.height)
self.Model()
def push(self,position,rotation):
"""Apply the graphical matrix operations depending on the player parameters."""
glPushMatrix()
glRotatef(-rotation[0],1,0,0) #Rotate the world position according to the view of the player
glRotatef(-rotation[1],0,1,0) #Rotate the world position according to the view of the player
glTranslatef(-position[0],-position[1],-position[2],) #Translate the world's position from the view of the player
def set3d2(self):
"""Set a 3d environment."""
self.Projection()
gluPerspective(self.fovy,self.aspect,self.zNear,self.zFar) #Set the camera
#self.Model()
def set3d(self): self.Projection(); gluPerspective(70,self.width/self.height,0.05,1000); self.Model()
def update(self,dt):
"""Update the player's view using time dt and knowning keys."""
self.player.update(dt,self.keys)
class RubiksCube:
"""The rubiks cube object has (n**3)-(n-2)**3 cubes."""
def __init__(self,n=3):
"""Create a rubiks cube using n the size of the cube."""
self.n=n #Set the size of the cube
self.generate() #Generate the cubes of the rubiks cube
self.batch=pyglet.graphics.Batch() #Only a single batch is created for efficiency
self.colors=[RED,WHITE,GREEN,YELLOW,BLUE,ORANGE,BLACK]
self.tex_coords = ('t2f',(0,0, 1,0, 1,1, 0,1, ))
#self.tex_coords = BLUE
#self.side = self.get_tex('grass_side.png')
#self.blue = self.get_tex('blue.jpg')
self.historic=[]
self.rotating=0
self.permutations=[]
def get_tex(self,file):
tex = pyglet.image.load(file).texture
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST)
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST)
return pyglet.graphics.TextureGroup(tex)
def generate(self):
"""Create all the outside cubes with their faces colors."""
self.matrix=np.zeros((3,3,3,6))
self.matrix.fill(-1)
ld=[0,self.n-1] #List of positions along any axis
for d in range(2): #Direction
lc=[list(range(self.n)),list(range(self.n)),[ld[d]]] #List of colors
for f in range(3): #Face
m=f
n=(m+1)%self.n
p=(m+2)%self.n
for z in lc[m]: #Z component
for y in lc[n]: #Y component
for x in lc[p]: #X component
self.matrix[x][y][z][f+3*d]=f+3*d #Color each case with the right color
def getFace(self,n): #Useless for now
"""Extract the face number 'n'."""
i=-1
if n==0: return self.matrix[:][i][:] #Top face
if n==1: return self.matrix[:][:][0] #Front face
if n==2: return self.matrix[i][:][:] #Right face
if n==3: return self.matrix[:][:][i] #Back face
if n==4: return self.matrix[0][:][:] #Left face
if n==5: return self.matrix[:][0][:] #Bottom face
def rotate(self,n,t=1,axis=(1,0)):
"""Execute the number 'n' rotation 't' times along axis 'axis' which means it turns in counter-clockwise-direction."""
if n==0: self.matrix[:][i][:]=np.rot90(self.matrix[:][i][:],t,axis) #Top rotation
if n==1: self.matrix[:][:][0]=np.rot90(self.matrix[:][i][:],t,axis) #Front rotation
if n==2: self.matrix[i][:][:]=np.rot90(self.matrix[:][i][:],t,axis) #Right rotation
if n==3: self.matrix[:][:][i]=np.rot90(self.matrix[:][i][:],t,axis) #Back rotation
if n==4: self.matrix[0][:][:]=np.rot90(self.matrix[:][i][:],t,axis) #Left rotation
if n==5: self.matrix[:][0][:]=np.rot90(self.matrix[:][i][:],t,axis) #Bottom rotation
def canCreateCube(self,x,y,z):
"""Determine if the cube can be created."""
return (x%self.n)+(y%self.n)+(z%self.n)==0
def drawCube(self,p1,p2,texture=None):
"""Draw a cube given its extremities."""
#texture=(0,1,1,1)
#texture=glColor(0,1,1,1)
texture=pyglet.graphics.OrderedGroup(1)
x,y,z=p1
X,Y,Z=p2
self.batch.add(4,GL_QUADS,self.side,('v3f',(x,y,z, x,y,Z, x,Y,Z, x,Y,z, )),self.tex_coords)
self.batch.add(4,GL_QUADS,self.blue,('v3f',(X,y,Z, X,y,z, X,Y,z, X,Y,Z, )),self.tex_coords)
self.batch.add(4,GL_QUADS,texture,('v3f',(x,y,z, X,y,z, X,y,Z, x,y,Z, )),self.tex_coords)
self.batch.add(4,GL_QUADS,texture,('v3f',(x,Y,Z, X,Y,Z, X,Y,z, x,Y,z, )),self.tex_coords)
self.batch.add(4,GL_QUADS,texture,('v3f',(X,y,z, x,y,z, x,Y,z, X,Y,z, )),self.tex_coords)
self.batch.add(4,GL_QUADS,texture,('v3f',(x,y,Z, X,y,Z, X,Y,Z, x,Y,Z, )),self.tex_coords)
def drawCubes2(self,texture=None):
"""Draw a cube given its extremities but without adding it to the batch, reducing efficiency."""
#texture=pyglet.graphicsOrderedGroup(1)
#px,py,pz=self.position
px,py,pz=[-self.n/2 for i in range(3)]
glBegin(GL_QUADS)
for s,surface in enumerate(surfaces):
x = 0
for vertex in surface:
vx,vy,vz=verticies[vertex]
x+=1
mx=x//9
my=x//3%3
mz=x%3
color=self.matrix[mx][my][mz][s]
if color:
glColor3fv(color)
glVertex3fv((vx+px,vy+py,vz+pz))
glEnd()
def drawCubes(self):
"""Draw the cube at given position."""
#Loop through the indexes to get the cubes
for x in range(self.n):
for y in range(self.n):
for z in range(self.n):
#Calculate the positions
p1,p2=self.getCube([x,y,z])
self.drawCube(p1,p2)
def getCube(self,position,size=[1,1,1]):
"""Return the extremities of the cube defined by the given position and size."""
offsets=[self.n/2 for i in range(3)]
p1=[p-s/2-f for (p,s,f) in zip(position,size,offsets)]
p2=[p-s/2+f for (p,s,f) in zip(position,size,offsets)]
return (p1,p2)
def draw(self):
"""Draw the batch."""
self.drawCubes2()
self.batch.draw()
def __str__(self):
"""Return the string representation of the object."""
return str(self.matrix)
class Cube:
"""These cubes are the ones of the rubiks cube."""
def __init__(self,position,size=[1,1,1],colors=[]):
"""The cubes are defined by their position."""
self.position=position
self.size=size
self.colors=colors
def rotate(self):
"""Rotate the cube."""
#Maybe useless
def G(self):
"""Return the point G(x,y,z) center of the cube by default."""
return self.position
def A(self):
"""Return the point A(xmin,ymin,zmin) by default."""
return [p-s/2 for (p,s) in zip(self.position,self.size)]
def B(self):
"""Return the point B(xmax,ymax,zmax) by default."""
return [p+s/2 for (p,s) in zip(self.position,self.size)]
class Player:
def __init__(self,position=(-100,0,0),rotation=(0,0)):
"""Create a player."""
self.position=list(position) #position of the player relative to the scene
self.rotation=list(rotation) #rotation of the player's view relative to the scene
def mouse_motion(self,dx,dy):
"""Allow the user to change its own view of the rubiks cube."""
dx/=8
dy/=8
self.rotation[0]+=dy
self.rotation[1]-=dx
if self.rotation[0]>90:
self.rotation[0]=90
elif self.rotation[0]<-90:
self.rotation[0]=-90
def update(self,dt,keys):
"""Update the position of the player."""
s=dt*10
rotationY = -self.rotation[1]/180*math.pi #The angles are converted from degrees in radians
dx,dz = s*math.sin(rotationY),s*math.cos(rotationY)
if keys[key.W]: self.position[0]+=dx; self.position[2]-=dz
if keys[key.S]: self.position[0]-=dx; self.position[2]+=dz
if keys[key.A]: self.position[0]-=dz; self.position[2]-=dx
if keys[key.D]: self.position[0]+=dz; self.position[2]+=dx
if keys[key.SPACE]: self.position[1]+=s
if keys[key.LSHIFT]: self.position[1]-=s
if __name__ == '__main__':
width=900
height=600
window=Window(width=width,height=height,caption='Rubiks Cube',fullscreen=False)
r,g,b,alpha=0,0,0,1 #Color of the background and transparency alpha, each component's value must be between 0 and 1
glClearColor(r,g,b,alpha) #Apply the background
#glEnable allows to change the parameters of the opengl interface
glEnable(GL_DEPTH_TEST) #Allow the objects not to be displayed when behing others, must be enabled to prevent weird bugs
#glEnable(GL_CULL_FACE)
pyglet.app.run()