Add files via upload

This commit is contained in:
JeanMarieMineau 2017-05-13 18:49:33 +02:00 committed by GitHub
parent 845468e869
commit ad2d020b19
12 changed files with 2193 additions and 0 deletions

510
Cube1x1.py Normal file
View file

@ -0,0 +1,510 @@
'''
Created on 9 mars 2017
Copyright 2017 Jean-Marie Mineau, Maxime Keller
This file is part of "ISN's Cube".
"ISN's Cube" is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
"ISN's Cube" is distributed in the hope that it will be useful and
recreative, but WITHOUT ANY WARRANTY; without even the implied
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with "ISN's Cube". If not, see <http://www.gnu.org/licenses/>.
@author: <mineau.jean.marie@gmail.com>
Programme inspiré d'un programme dévellopé par Leonel Machava <leonelmachava@gmail.com>
http://codeNtronix.com
'''
import sys, math, pygame
from operator import itemgetter
class Point3D:
def __init__(self, x = 0, y = 0, z = 0):
self.x, self.y, self.z = float(x), float(y), float(z)
def rotationX(self, angle):
""" Fait pivoter le point autour de l'axe X d'une valeur donnée en degrés. """
rad = angle * math.pi / 180
cosa = math.cos(rad)
sina = math.sin(rad)
y = self.y * cosa - self.z * sina #Formules d'addition de trigo.
z = self.y * sina + self.z * cosa
return Point3D(self.x, y, z)
def rotationY(self, angle):
""" Fait pivoter le point autour de l'axe Y d'une valeur donnée en degrés. """
rad = angle * math.pi / 180
cosa = math.cos(rad)
sina = math.sin(rad)
z = self.z * cosa - self.x * sina #Formules d'addition de trigo.
x = self.z * sina + self.x * cosa
return Point3D(x, self.y, z)
def rotationZ(self, angle):
""" Fait pivoter le point autour de l'axe Z d'une valeur donnée en degrés. """
rad = angle * math.pi / 180
cosa = math.cos(rad)
sina = math.sin(rad)
x = self.x * cosa - self.y * sina #Formules d'addition de trigo.
y = self.x * sina + self.y * cosa
return Point3D(x, y, self.z)
def projection(self, largeur_ecran, hauteur_ecran, zoom, distance):
""" Transforme ce point 3D en point 2D (avec la profondeur conservé). """
factor = zoom / (distance + self.z)
x = self.x * factor + largeur_ecran / 2
y = -self.y * factor + hauteur_ecran / 2
return Point3D(x, y, self.z)
def __eq__(self, point):
"""opérateur d'égalité."""
x = abs(self.x - point.x) < 0.01
y = abs(self.y - point.y) < 0.01
z = abs(self.z - point.z) < 0.01
return x and y and z
def dans(self, points):
"""Test si un point est dans une liste de points avec une impressition."""
for point in points:
x = False
y = False
z = False
if abs(self.x - point.x) < 0.01:
x = True
if abs(self.y - point.y) < 0.01:
y = True
if abs(self.z - point.z) < 0.01:
z = True
if x and y and z:
return True
return False
def copie(self):
"""Retourne une copie de l'objet."""
return Point3D(self.x, self.y, self.z)
class Peintre:
"""Objet de tri pour les faces."""
def __init__(self, LARGEUR_ECRAN = 500, HAUTEUR_ECRAN = 500, ZOOM = 254, DISTANCE = 50, ECART_INTER = 0.01):
self.LARGEUR_ECRAN = LARGEUR_ECRAN # DISTANCE 10
self.HAUTEUR_ECRAN = HAUTEUR_ECRAN
self.ZOOM = ZOOM
self.DISTANCE = DISTANCE
self.ECART_INTER = ECART_INTER
self.coin = []
self.faces = []
def intersection(self, A, B, C, D):
"""Retourne l'intersection des secgments [point1 point2] et [point3 point4] en 2 dimentions"""
# Segments paralleles
if A.x == B.x and C.x == D.x: return None
elif (A.x == B.x):
c = (C.y - D.y)/(C.x - D.x)
d = C.y - (c*C.x)
x = A.x
y = c*x + d
elif (C.x == D.x):
a = (A.y - B.y)/(A.x - B.x)
b = A.y - (a*A.x)
x = C.x
y = a*x + b
else:
a = (A.y - B.y)/(A.x - B.x)
b = A.y - (a*A.x)
c = (C.y - D.y)/(C.x - D.x)
d = C.y - (c*C.x)
if a == c:
return None # Segment parallèles
elif a == 0:
y = B.y
x = (y - d)/c
elif c == 0:
y = D.y
x = (y - b/a)
else:
x = (d - b)/(a - c)
y = a*x + b
pointDansSegment1 = (min(A.x, B.x) + self.ECART_INTER < x < max(A.x, B.x) - self.ECART_INTER) \
and (min(A.y, B.y) + self.ECART_INTER < y < max(A.y, B.y) - self.ECART_INTER)
pointDansSegment2 = (min(C.x, D.x) + self.ECART_INTER < x < max(C.x, D.x) - self.ECART_INTER) \
and (min(C.y, D.y + self.ECART_INTER) < y < max(C.y, D.y) - self.ECART_INTER)
if pointDansSegment1 and pointDansSegment2:
return Point3D(x, y, 0)
else:
return None
def profondeurIntersection(self, A, B, I):
"""Retourne le point avec sa profondeur"""
x = I.x
y = I.y
if A.z == B.z:
z = A.z
return Point3D(x, y, z)
a = (A.x - B.x)/(A.z - B.z)
b = B.x - (a*B.z)
c = (A.y - B.y)/(A.z - B.z)
d = B.y - (c*B.z)
# Attention grosse formule!
denominateur = ((x - self.LARGEUR_ECRAN/2) - a*self.ZOOM)
if denominateur != 0:
z = (b*self.ZOOM - (x - self.LARGEUR_ECRAN/2)*self.DISTANCE)/denominateur
return Point3D(x, y, z)
denominateur = (c*self.ZOOM + y - self.HAUTEUR_ECRAN/2)
if denominateur != 0:
z = (-d*self.ZOOM - (y - self.HAUTEUR_ECRAN/2)*self.DISTANCE)/denominateur
return Point3D(x, y, z)
denominateur = (y - self.HAUTEUR_ECRAN/2 + (c+d)*self.ZOOM)
if denominateur != 0:
z = -(y - self.HAUTEUR_ECRAN/2)*self.DISTANCE/denominateur
return Point3D(x, y, z)
## Formules qui MARCHENT:
#z = - (y - HAUTEUR_ECRAN/2)*DISTANCE / (y - HAUTEUR_ECRAN/2 + (c+d)*ZOOM)
#z = (-d*ZOOM - (y - HAUTEUR_ECRAN/2)*DISTANCE)/(c*ZOOM + y - HAUTEUR_ECRAN/2)
#z = (b*ZOOM - (x - LARGEUR_ECRAN/2)*DISTANCE)/((x - LARGEUR_ECRAN/2) - a*ZOOM)
## Formules FAUSSENT z = ((y - HAUTEUR_ECRAN/2)*a/ZOOM + b) / (1- (a/ZOOM)*(y - HAUTEUR_ECRAN/2))
##z = ((x - LARGEUR_ECRAN/2)*DISTANCE - b*ZOOM)/(a*ZOOM - x - LARGEUR_ECRAN/2)
z = 0
return Point3D(x, y, z)
def tri(self, faces, intersections, segments, points3D, point2D):
"""Trie les faces en fonction des points d'intersection"""
# pointsSegments contient des listes avec en première valeur le point d'intersection
# en deuxième et troisième les extrèmitès du segment auquel il appartient.
# Ces listes vont normalement par paire.
while intersections:
# Selectionne deux points d'intersections de même position
intersection1 = intersections[0]
intersections = intersections[1:]
segment1 = segments[0]
segments = segments[1:]
for intersection2 in intersections:
if abs(intersection1.x - intersection2.x) < 0.01 and abs(intersection1.y - intersection2.y) < 0.01:
segment2 = segments[intersections.index(intersection2)]
segments.remove(segment2)
intersections.remove(intersection2)
break
# verifie la validite du point
if intersection1.x - intersection2.x < 0.01 and intersection1.y - intersection2.y < 0.01:
faces1 = []
faces2 = []
for face in faces:
i = faces.index(face)
pointsFaces = [points3D[j] for j in face]
if segment1[0].dans(pointsFaces) and \
segment1[1].dans(pointsFaces):
faces1.append(i)
if segment2[0].dans(pointsFaces) and \
segment2[1].dans(pointsFaces):
faces2.append(i)
# Nous avons associer deux points d'intersection de profondeur differentes
# aux faces qui contiennent le segment
# Il faut que les faces donc le segment est le plus proche soit avans les
# deux autres.
interTpm = [intersection1, intersection2]
facesTpm = [faces1, faces2]
# Le point le plus éloigné en premier, donc les premieres faces a afficher en premier
if interTpm[0].z > interTpm[1].z:
tmp = facesTpm[0]
facesTpm[0] = facesTpm[1]
facesTpm[1] = tmp
# les premieres faces doivent etre avant la limite
limite = min(facesTpm[0])
indice1, indice2 = facesTpm[1]
if indice1 > limite and indice2 > limite:
faces1Liste = faces[:limite]
faces2Liste = faces[limite:]
faces1Liste.append(faces[min(indice1, indice2)])
faces1Liste.append(faces[max(indice1, indice2)])
faces2Liste.remove(faces[indice1])
faces2Liste.remove(faces[indice2])
faces = faces1Liste + faces2Liste
elif indice1 > limite:
faces1Liste = faces[:limite]
faces2Liste = faces[limite:]
faces1Liste.append(faces[indice1])
faces2Liste.remove(faces[indice1])
faces = faces1Liste + faces2Liste
elif indice2 > limite:
faces1Liste = faces[:limite]
faces2Liste = faces[limite:]
faces1Liste.append(faces[indice2])
faces2Liste.remove(faces[indice2])
faces = faces1Liste + faces2Liste
return faces
def peintre1(self, faces, points2D):
"""Algorithme du peintre traditionnel."""
moy_z = []
i = 0
for f in faces:
z = (points2D[f[0]].z + points2D[f[1]].z + points2D[f[2]].z + points2D[f[3]].z) / 4.0
moy_z.append([i,z])
i = i + 1
nouvellesFaces = []
# Trie lessurfaces en utlisant l'algorithme du peintre
# Les faces les plus éloignées sont tracées avant les plus proches.
for tmp in sorted(moy_z,key=itemgetter(1),reverse=True):
indiceFace = tmp[0]
nouvellesFaces.append(faces[indiceFace])
return nouvellesFaces
def peintre2(self):
"""Retourne une liste de surface clasés selon l'algorythme du peintre, ainsi que leur couleur"""
points3D = self.coins
points2D = [i.projection(self.LARGEUR_ECRAN, self.HAUTEUR_ECRAN, self.ZOOM, self.DISTANCE) for i in self.coins]
faces = self.faces
intersections = []
segments = []
for face1 in self.faces:
#Test l'intersection de tous les point de chaque face
for face2 in self.faces:
for j in [-1, 0, 1, 2]:
for i in [-1, 0, 1, 2]:
A3D = points3D[face1[j]]
B3D = points3D[face1[j+1]]
C3D = points3D[face2[i]]
D3D = points3D[face2[i+1]]
A = points2D[face1[j]]
B = points2D[face1[j+1]]
C = points2D[face2[i]]
D = points2D[face2[i+1]]
inter = self.intersection(A, B, C, D)
if inter:
intersections.append(self.profondeurIntersection(A3D, B3D, inter))
segments.append([A3D, B3D])
intersections.append(self.profondeurIntersection(C3D, D3D, inter))
segments.append([C3D, D3D])
faces = self.peintre1(faces, points2D)
faces = self.tri(faces, intersections, segments, points3D, points2D)
return faces
class Cubie3D(Peintre):
"""Class définissant un cube en 3D"""
def __init__(self, pos):
"""Définit le cube de centre pos et de coté 2"""
Peintre.__init__(self)
self.centre = Point3D(pos[0], pos[1], pos[2])
self.coins = [
Point3D(pos[0]-2,pos[1]+2,pos[2]+2), # droit haut arrière
Point3D(pos[0]+2,pos[1]+2,pos[2]+2), # gauche haut arrière
Point3D(pos[0]-2,pos[1]+2,pos[2]-2), # droit haut avant
Point3D(pos[0]+2,pos[1]+2,pos[2]-2), # gauche haut avant
Point3D(pos[0]-2,pos[1]-2,pos[2]+2), # droit bas arrière
Point3D(pos[0]+2,pos[1]-2,pos[2]+2), # gauche bas arrière
Point3D(pos[0]-2,pos[1]-2,pos[2]-2), # droit bas avant
Point3D(pos[0]+2,pos[1]-2,pos[2]-2)] # gauche bas avant
#Liste des faces, les valeurs sont les indices du point correspondant dans la liste coins.
self.faces = [
(0,1,3,2), # Haut
(4,5,7,6), # Bas
(0,2,6,4), # Droite
(1,3,7,5), # Gauche
(0,1,5,4), # Arrière
(2,3,7,6)] # Avant
#Liste des couleurs, leur indices sont les mêmes que ceux de la faces associer.
self.couleurs = [
(0,0,0), #(255,0,0), # Haut
(0,0,0), #(255,70,0), # Bas
(0,0,0), #(255,255,0), # Gauche
(0,0,0), #(255,255,255), # Droite
(0,0,0), #(0,255,0), # Arrière
(0,0,0)] #(0,0,255)] # Avant
self.couleursResolution = []
def getMinZ(self):
"""Retourne la profondeur du point le plus proche."""
z = self.coins[0].z
for pt in self.coins:
if pt.z < z:
z = pt.z
return z
minZ = property(fget = getMinZ)
def rotationX(self, angle):
""" Fait pivoter le cube autour de l'axe X d'une valeur donnée en degrés. """
newCoins = []
for point in self.coins:
newCoins.append(point.rotationX(angle))
newCube = self.copie()
newCube.coins = newCoins
newCube.centre = self.centre.rotationX(angle)
return newCube
def rotationY(self, angle):
""" Fait pivoter le cube autour de l'axe Y d'une valeur donnée en degrés. """
newCoins = []
for point in self.coins:
newCoins.append(point.rotationY(angle))
newCube = self.copie()
newCube.coins = newCoins
newCube.coins = newCoins
newCube.centre = self.centre.rotationY(angle)
return newCube
def rotationZ(self, angle):
""" Fait pivoter le cube autour de l'axe Z d'une valeur donnée en degrés. """
newCoins = []
for point in self.coins:
newCoins.append(point.rotationZ(angle))
newCube = self.copie()
newCube.coins = newCoins
newCube.centre = self.centre.rotationZ(angle)
return newCube
def getOrientationFace(self, couleur):
"""Retourn l'orientation de la face de la couleur donnée.
Elle est donné sous la forme x, y, z, seul l'axe perpendiculaire
à la face est différent ce 0, et le signe son de quel côté du cube il ce trouve."""
indiceFace = self.couleurs.index(couleur)
pointsFace = [(self.coins[i].x, self.coins[i].y, self.coins[i].z) for i in self.faces[indiceFace]]
pointsFaceOposee = [(self.coins[i].x, self.coins[i].y, self.coins[i].z)
for i in range(len(self.coins)) if i not in self.faces[indiceFace]]
orientation = [0, 0, 0]
for i in [0, 1, 2]:
#on compart les coordonnées du premier point des deux faces, pour x, y puis z
if pointsFace[0][i] > pointsFaceOposee[0][i]: orientation[i] = 1
elif pointsFace[0][i] < pointsFaceOposee[0][i]: orientation[i] = -1
for point in pointsFace:
for i in [0, 1, 2]:
if abs(point[i] - pointsFace[0][i]) > 0.01:
#if point[i] != pointsFace[0][i]:
orientation[i] = 0
# pour donner l'axe de la face,
#on compart les coordonnées e x,y et z entre les points de la place.
if orientation == [0, 0, 0]:
raise Exception("Cette face ne semble pas avoir d'orientation, what?")
return tuple(orientation)
def affichage(self, screen):
"""Affiche le cube sur l'écran screen en utilisant l'algorithme du peintre"""
t = []
for coin in self.coins:
p = coin.projection(self.LARGEUR_ECRAN, self.HAUTEUR_ECRAN, self.ZOOM, self.DISTANCE)
# Place le point dans une liste de coins transformés
t.append(p)
# Trace la surface en utlisant l'algorithme du peintre
# Les faces les plus éloignées sont tracées avant les plus proches.
faces = self.peintre2()
for f in faces[-3:]:
pointlist = [(t[f[0]].x, t[f[0]].y), (t[f[1]].x, t[f[1]].y),
(t[f[1]].x, t[f[1]].y), (t[f[2]].x, t[f[2]].y),
(t[f[2]].x, t[f[2]].y), (t[f[3]].x, t[f[3]].y),
(t[f[3]].x, t[f[3]].y), (t[f[0]].x, t[f[0]].y)]
pygame.draw.polygon(screen,(0,0,0),pointlist, 3)
#Pour que les faces caches les traits
#en arrière, ils faut les affichers après les arrètes
pygame.draw.polygon(screen,self.couleurs[self.faces.index(f)],pointlist)
#pygame.display.flip()
#pygame.time.wait(100)
def copie(self):
"""Retourne une copie de l'objet."""
newCubie = Cubie3D((0,0,0))
newCubie.centre = self.centre
newCubie.coins = self.coins
newCubie.faces = self.faces
newCubie.couleurs = self.couleurs
newCubie.couleursResolution = self.couleursResolution
return newCubie
if __name__ == "__main__":
###nb_image = 0 #######
pygame.init()
cubes = [Cubie3D((-2,-2,-2))]
angle = 5
screen = pygame.display.set_mode((500, 500))
pygame.display.set_caption("Rubick's Cube")
rot = True
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
if rot:
rot = False
else:
rot = True
if event.key == pygame.K_LEFT:
angle = 5
if event.key == pygame.K_RIGHT:
angle = -5
screen.fill((255,255,255))
for cube in cubes:
cube.affichage(screen)
#if nb_image == 40:
# pygame.quit()
# sys.exit()
#pygame.image.save(screen,"./img/" + str(nb_image)+".png")
#nb_image+=1################################################
pygame.display.flip()
pygame.time.wait(100)
if rot:
for i, cube in enumerate(cubes):
cubes[i] = cube.rotationZ(angle)#.rotationY(angle).rotationX(angle)