#!/usr/bin/python2.7 # -*- coding: utf-8 -*- # # Programme simple d'ouverture d'une fenêtre graphique et d'affichage de pixels # utilisant le module pygame # ################################################################################ ################################################################################ # Chargement des bibliothèques utilisées et initialisation de pygame ################################################################################ from __future__ import division from math import * from time import sleep import pygame ################################################################################ # Initialisation du module pygame ################################################################################ pygame.init() pygame.font.init() police = pygame.font.SysFont("Quicksand,Comfortaa,Courrier,Arial", 18) ################################################################################ # Définition des constantes par défaut ################################################################################ Largeur = 1365 # Largeur par défaut de la fenêtre Hauteur = 768 # Hauteur par défaut de la fenêtre AutoRes = False # Active le réglage auto de la taille de fenêtre if AutoRes: # Réglage auto de la taille de la fenêtre selon l'écran infos = pygame.display.Info() Largeur = 2 * infos.current_w // 3 Hauteur = 2 * infos.current_h // 3 MARGE = 50 # Marge entre bord écran et fenêtres image CUBE = 0 # Suite des actions d'affichage successives TRIS = CUBE+1 FUSEAU1 = TRIS+1 FUSEAU2 = FUSEAU1+1 FUSEAU3 = FUSEAU2+1 FUSEAU4 = FUSEAU3+1 PARAM = FUSEAU4+1 FONCTION = PARAM+1 BEZIER1 = FONCTION+1 BEZIER2 = BEZIER1+1 NBACTIONS = BEZIER2+1 nbActions = 6 # Nombre d'actions d'affichage valides (une seule au départ) ################################################################################ # DÉCLARATION DES TYPES DE DONNÉES # ################################################################################ # Description d'une couleur à partir des composantes Rouge, Vert et Bleu ################################################################################ class Couleur: R = None # Canal rouge V = None # Canal vert B = None # Canal bleu def __init__(self, r=255, v=255, b=255): # Blanc par défaut self.R = r self.V = v self.B = b ################################################################################ # Description d'un point dans l'espace image ################################################################################ class PointImage: col = None # Colonne dans l'image lig = None # Ligne dans l'image def __init__(self, col=-1, lig=-1): # Point hors image par défaut self.col = col self.lig = lig ################################################################################ # Description d'un point dans l'espace réel ################################################################################ class PointReel: x = None # Abscisse du point y = None # Ordonnée du point def __init__(self, x=0.0, y=0.0): # Point à l'origine par défaut self.x = x self.y = y ################################################################################ # Description d'une fenêtre dans l'espace image ################################################################################ class FenetreImage: bg = None # Point image en bas à gauche de la fenêtre hd = None # Point image en haut à droite de la fenêtre def __init__(self, bg=PointImage(0,Hauteur), hd=PointImage(Largeur,0)): # La fenêtre image prend toute la fenêtre par défaut self.bg = bg self.hd = hd ################################################################################ # Description d'une fenêtre dans l'espace réel ################################################################################ class FenetreReel: bg = None # Point réel en bas à gauche de la fenêtre hd = None # Point réel en haut à droite de la fenêtre def __init__(self, bg=PointReel(-1.0,-1.0), hd=PointReel(1.0,1.0)): # La fenêtre réelle par défaut est un carré centré à l'origine self.bg = bg self.hd = hd ################################################################################ # Description des infos de transformation entre les fenêtres réelle et image ################################################################################ class TransfoImages: fr = None # Fenêtre réelle fi = None # Fenêtre image riA, riB, riC, riD = None, None, None, None # Coefficients de transfo réel -> image irA, irB, irC, irD = None, None, None, None # Coefficients de transfo image -> réel # # ################################################################################ ################################################################################ # Déclaration des variables globales ################################################################################ ########################################### ## Informations globales ########################################### dejaFait = False # Indique si dessin est déjà fait ou non action = TRIS # Numéro de l'action courante dans la suite des affichages successifs (définit ici la 1ère action effectuée) fini = False # Indique si le programme est terminé ou non fond = (0, 0, 0) # Couleur de fond (r, v, b) traceS = False # Indique si l'on trace avec la souris posS = PointImage() # Position de la souris pleinEcran = False # Indique si on est en plein écran ou non ################################################################################ # Construction de la fenêtre et initialisation avec fond noir ################################################################################ fen = pygame.display.set_mode([Largeur, Hauteur]) # , pygame.FULLSCREEN) pygame.display.set_caption("TP Informatique graphique") fen.fill(fond) ################################################################################ # Gestion des touches du clavier ################################################################################ def gestionTouches(): events = pygame.event.get([pygame.KEYDOWN, pygame.KEYUP]) global pleinEcran, dejaFait, action, nbActions, fini for event in events: if event.type == pygame.KEYUP: if event.key == pygame.K_q or event.key == pygame.K_ESCAPE: fini = True elif event.key == pygame.K_BACKSPACE: Effacer() elif event.key == pygame.K_F11: if not pleinEcran: pygame.display.set_mode([Largeur, Hauteur], pygame.FULLSCREEN) pleinEcran = True else: pygame.display.set_mode([Largeur, Hauteur]) pleinEcran = False dejaFait = False Affichage(action) else: # Cas par défaut : toutes les touches et pas seulement espace : if event.key == pygame.K_SPACE: action = (action + 1) % nbActions dejaFait = False Affichage(action) ################################################################################ # Gestion de la souris ################################################################################ def gestionSouris(): events = pygame.event.get([pygame.MOUSEMOTION, pygame.MOUSEBUTTONDOWN, pygame.MOUSEBUTTONUP]) global traceS, posS coul = Couleur() posSn = PointImage() for event in events: if event.type == pygame.MOUSEBUTTONDOWN: if event.button == 1: traceS = True posS.col = event.pos[0] posS.lig = event.pos[1] ColoriePixel(event.pos[0], event.pos[1], coul) if traceS == True and event.type == pygame.MOUSEMOTION: posSn.col = event.pos[0] posSn.lig = event.pos[1] DessineSegmentImage(posS, posSn, coul) posS = posSn ColoriePixel(event.pos[0], event.pos[1], coul) if event.type == pygame.MOUSEBUTTONUP: traceS = False ################################################################################ # Sortie du programme ################################################################################ def sortie(): print("Programme terminé.") pygame.quit() ################################################################################ # Mise à jour de l'affichage ################################################################################ def MiseAJour(zone=None): if zone == None: pygame.display.update() else: pygame.display.update(zone) ################################################################################ # Coloriage d'un pixel ################################################################################ def ColoriePixel(col, lig, coul): fen.set_at((col, lig), (coul.R,coul.V,coul.B)) ################################################################################ # Coloriage de tous les pixels en noir ("vide" la fenêtre) ################################################################################ def Effacer(zone=None): if zone == None: fen.fill(fond) else: fen.fill(fond, zone) ################################################################################ # Affichage d'un texte à une position donnée ################################################################################ def AfficheTexte(texte, pos, coul): rgba = (coul.R, coul.V, coul.B, 255) surfTxt = police.render(texte.decode('utf-8'), True, rgba) # surfIm = pygame.display.get_surface() # surfIm.blit(surfTxt, pos) fen.blit(surfTxt, pos) ################################################################################ # Dessin des faces du cube des couleurs ################################################################################ def DessineFacesCubeCouleurs(): # DESSINER DANS UN PREMIER TEMPS UN RECTANGLE UNI BLEU DE CÔTÉ 25x25 # À PARTIR DU POINT (10,10) Bleu = Couleur(0, 0, 255) # Définition de la couleur bleu for lig in range(25): for col in range(25): ColoriePixel(10+col, 10+lig, Bleu) # DESSINER ENSUITE LA FACE DU CUBE DES COULEURS # À PARTIR DU POINT (10,35) AVEC # LES ROUGES EN ABSCISSES # LES VERTS EN ORDONNÉES # LA VALEUR DE BLEU À 0 coul = Couleur() coul.B = 0 # Face B = 0 for lig in range(256): coul.V = 255-lig for col in range(256): coul.R = col ColoriePixel(10+col, 35+lig, coul) MiseAJour() # Mise à jour de l'affichage sleep(2) # Attente de 2 secondes Effacer() # Vidage de la fenêtre avant d'afficher les 6 faces du cube bleu = 0 rouge = 0 vert = 255 for lig in range (266, 521): for col in range (266, 521): ColoriePixel(lig, col, Couleur(rouge, vert, 0)) vert -= 1 vert = 255 rouge += 1 #2eme face bleu = 255 vert = 255 rouge = 0 for lig in range (266, 521): for col in range (10, 266): ColoriePixel(col, lig, Couleur(0, vert, bleu)) bleu -= 1 bleu = 255 vert -= 1 #3eme face bleu = 0 vert = 0 rouge = 0 for lig in range (266, 521): for col in range (521, 776): ColoriePixel(lig, col, Couleur(rouge, 0, bleu)) bleu += 1 bleu = 0 rouge += 1 #4eme face rouge = 255; vert = 255; bleu = 0; for lig in range (521, 776): for col in range (266, 521): ColoriePixel(lig, col, Couleur(rouge, vert, bleu)) vert -= 1 bleu += 1 vert = 255 #5eme face rouge = 0 vert = 255 bleu = 255 for lig in range (10, 266): for col in range (266, 521): ColoriePixel(col, lig, Couleur(rouge, vert, bleu)) rouge += 1 rouge = 0 bleu -= 1 #6eme face rouge = 255 vert = 255 bleu = 255 for lig in range (776, 1031): for col in range (266, 521): ColoriePixel(lig, col, Couleur(rouge, vert, bleu)) vert -= 1 vert = 255 rouge -= 1 ################################################################################ # Dessin d'un segment image (version avec variables entières uniquement) ################################################################################ def DessineSegmentImage(pi1, pi2, coul): dc = pi2.col - pi1.col # Variation en colonnes dl = pi2.lig - pi1.lig # Variation en lignes absdc = abs(dc) # Valeur absolue de dc absdl = abs(dl) # Valeur absolue de dl signe = 1 # Sens (signe) de parcours de la boucle de tracé cumul = 0.0 # Cumul (réel) des déplacements selon l'axe non parcouru col = pi1.col # Initialisation de la colonne de départ lig = pi1.lig # Initialisation de la ligne de départ sdl = sdc = 1 # Sens (signe) des variations selon l'axe non parcouru if dc < 0: sdc = -1 if dl < 0: sdl = -1 if absdc >= absdl: # Variation en colonnes supérieure à variation en lignes # => Parcours des colonnes signe = sdc cumul = absdc / 2 # On commence au centre du pixel for col in range(pi1.col, pi2.col+signe, signe): ColoriePixel(col, lig, coul) cumul += absdl if cumul >= absdc: # Détection d'un changement de ligne lig += sdl cumul -= absdc else: # Variation en lignes supérieure à variation en colonnes # => Parcours des lignes signe = sdl cumul = absdl / 2 # On commence au centre du pixel for lig in range(pi1.lig, pi2.lig+signe, signe): ColoriePixel(col, lig, coul) cumul += absdc if cumul >= absdl: # Détection d'un changement de colonne col += sdc cumul -= absdl ################################################################################ # Dessin d'un point réel ################################################################################ def DessinePointReel(pr, coul, transfo): pi = PointImage() pi = TransformationRvI(pr, transfo) ColoriePixel(pi.col, pi.lig, coul) ################################################################################ # Dessin d'un segment réel # Le paramètre 'decoupe' peut être omis et vaut alors False ################################################################################ def DessineSegmentReel(pr1, pr2, coul, transfo, decoupe=False): pi1, pi2 = PointImage(), PointImage() pr1dec, pr2dec = PointReel(), PointReel() if decoupe == True : pr1dec, pr2dec = DecoupeSegmentReel(pr1, pr2, transfo.fr ) else: pr1dec, pr2dec = pr1, pr2 pi1 = TransformationRvI(pr1dec, transfo) pi2 = TransformationRvI(pr2dec, transfo) DessineSegmentImage(pi1, pi2, coul) ################################################################################ # Découpage d'un segment réel ################################################################################ def DecoupeSegmentReel(pr1, pr2, fr): npr1 = PointReel(pr1.x, pr1.y) npr2 = PointReel(pr2.x, pr2.y) #Positions relatives pr1 gauche1 = npr1.x < fr.bg.x print "gauche1 = ", gauche1 #Si pr1 est à gauche du bord gauche ####DEBUGAGE#### droite1 = npr1.x > fr.hd.x print "droite1 = ", droite1 #Si pr1 est droite du bord droit ####DEBUGAGE#### bas1 = npr1.y < fr.bg.y print "bas1 = ", bas1 #Si pr1 est sous le bord bas ####DEBUGAGE#### haut1 = npr1.y > fr.hd.y print "haut1 = ", haut1 #si pr1 est au dessus du bord haut ####DEBUGAGE#### #positions relatives pr2 gauche2 = npr2.x < fr.bg.x print "gauche2 = ", gauche2 #Si pr2 est à chauche du bord gauche ####DEBUGAGE#### droite2 = npr2.x > fr.hd.x print "droite2 = ", droite2 #Si pr2 est droite du bord droite ####DEBUGAGE#### bas2 = npr2.y < fr.bg.y print "bas2 = ", bas2 #Si pr2 est sous le bord bas ####DEBUGAGE#### haut2 = npr2.y > fr.hd.y print "haut2 = ", haut2 #si pr2 est au dessus du bord haut ####DEBUGAGE#### tdedans = False print "\ntdedans avant boucle = ", tdedans ####DEBUGAGE#### tdehors = False print "tdehors avant boucle = ", tdehors, "\n" ####DEBUGAGE#### dx = npr2.x - npr1.x dy = npr2.y - npr1.x # !!!!! ERREUR !!!!! while tdedans == False & tdehors == False : #Si tout est en dehors de la fenêtre (le segment et ses points, donc on ne trace rien) if ((gauche1 & gauche2) or (droite1 & droite2) or (bas1 & bas2) or (haut1 & haut2)) : npr1.x = fr.bg.x - 1 npr1.y = fr.bg.y - 1 tdehors = True print "tdehors = ", tdehors, "\n" if (gauche1 == False & droite1 == False & bas1 == False & haut1 == False) & (gauche2 == False & haut2 == False & droite2 == False & bas2 == False) : tdedans = True print "tdedans = ", tdedans, "\n" ####DEBUGAGE#### elif gauche1 : npr1.y = (fr.bg.x - npr1.x)*((dy/dx) - npr1.y)# !!!!! ERREUR !!!!! npr1.x = (fr.bg.x) gauche1 = False print "gauche1 = ", gauche1, "\n" ####DEBUGAGE#### elif droite1 : npr1.y = (fr.hd.x - npr1.x)*((dy/dx) - npr1.y)# !!!!! ERREUR !!!!! npr1.x = fr.hd.x droite1 = False # !!!!! INCOMPLET !!!!! print "droite1 = ", droite1, "\n" ####DEBUGAGE#### elif haut1 : npr1.y = fr.hd.y # !!!!! ERREUR !!!!! npr1.x = (fr.hd.y - pr1.y)*((dy/dx) - npr1.x)# !!!!! ERREUR !!!!! haut1 = False # !!!!! INCOMPLET !!!!! print "haut1 = ", haut1, "\n" ####DEBUGAGE#### elif bas1 : npr1.y = fr.bg.y# !!!!! ERREUR !!!!! npr1.x = (fr.bg.y - npr1.y)*((dy/dx) - npr1.x)# !!!!! ERREUR !!!!! bas1 = False # !!!!! INCOMPLET !!!!! print "bas1 = ", bas1, "\n" ####DEBUGAGE#### elif gauche2 : npr2.y = (fr.bg.x - npr2.x)*((dy/dx) - npr2.y)# !!!!! ERREUR !!!!! npr2.x = (fr.bg.x) gauche2 = False # !!!!! INCOMPLET !!!!! print "gauche2 = ", gauche2, "\n" ####DEBUGAGE#### elif droite2 : npr2.y = (fr.hd.x - npr2.x)*((dy/dx) - npr2.y)# !!!!! ERREUR !!!!! npr2.x = fr.hd.x droite2 = False # !!!!! INCOMPLET !!!!! print "droite2 = ", droite2, "\n" ####DEBUGAGE#### elif haut2 : npr2.y = fr.hd.y# !!!!! ERREUR !!!!! npr2.x = (fr.hd.y - npr2.y)*((dy/dx) - npr2.x)# !!!!! ERREUR !!!!! haut2 = False # !!!!! INCOMPLET !!!!! print "haut2 = ", haut2, "\n" ####DEBUGAGE#### elif bas2 : npr2.y = fr.bg.y# !!!!! ERREUR !!!!! npr2.x = (fr.bg.y - npr2.y)*((dy/dx) - npr2.x)# !!!!! ERREUR !!!!! bas2 = False # !!!!! INCOMPLET !!!!! print "bas2 = ", bas2, "\n" ####DEBUGAGE#### return (npr1, npr2) ################################################################################ # Dessin du contour de la fenêtre image ################################################################################ def DessineContours(fi, coul): bg = PointImage(fi.bg.col, fi.bg.lig) # Point bas-gauche hg = PointImage(fi.bg.col, fi.hd.lig) # Point haut-gauche hd = PointImage(fi.hd.col, fi.hd.lig) # Point haut-droite bd = PointImage(fi.hd.col, fi.bg.lig) # Point bas-droite DessineSegmentImage(bg, hg, coul) # Bord gauche DessineSegmentImage(hg, hd, coul) # Bord supérieur DessineSegmentImage(hd, bd, coul) # Bord droit DessineSegmentImage(bd, bg, coul) # Bord bas ########################################### # Fonction de dessin des axes avec graduations selon le pas ########################################### def DessineAxes(pas, transfo, couleur): pr1, pr2 = PointReel(), PointReel() # Points pour le dessin des axes pi1, pi2 = PointImage(), PointImage() # Points image pour les graduations taille = 3 # Taille des graduations en pixel au-dessus et au-dessous des axes # À COMPLÉTER # ... pass ################################################################################ # Transformation réel vers image ################################################################################ def TransformationRvI(pr, transfo): pi = PointImage() # Point image résultant de la transformation pi.col = int(round(transfo.riA * pr.x + transfo.riB)) pi.lig = int(round(transfo.riC * pr.y + transfo.riD)) return pi # Renvoie le point image obtenu par la transformation du point réel ################################################################################ # Transformation image vers réel ################################################################################ def TransformationIvR(pi, transfo): pr = PointReel() # Point réel résultant de la transformation # À COMPLÉTER # ... return pr # Renvoie le point réel obtenu par la transformation du point image ################################################################################ # Calcul de coefficients de transformations réel <-> image ################################################################################ def CalculTransfosFenetres(fr, fi): transfo = TransfoImages() # Structure de transformation dcol, dlig = int(0), int(0) # Variations en colonnes et lignes dx, dy = float(0.0), float(0.0) # Variations en x et y dxbis, dybis = float(0.0), float(0.0) # Idem pour égalisation des ratios des fenêtres # Recopie des fenêtres dans la structure de transformation transfo.fr = FenetreReel(fr.bg, fr.hd) transfo.fi = FenetreImage(fi.bg, fi.hd) # Calcul des coefficients de transformation Réel -> Image transfo.riA = (transfo.fi.hd.col - transfo.fi.bg.col) / (transfo.fr.hd.x - transfo.fr.bg.x) transfo.riB = transfo.fi.bg.col - transfo.fr.bg.x * transfo.riA transfo.riC = (transfo.fi.hd.lig - transfo.fi.bg.lig) / (transfo.fr.hd.y - transfo.fr.bg.y) transfo.riD = transfo.fi.bg.lig - transfo.fr.bg.y * transfo.riC return transfo # Renvoie la structure de transformation ################################################################################ # Affichage principal : dessins successifs des différents TPs ################################################################################ def Affichage(action): global dejaFait, Hauteur, Largeur, MARGE # Variables définies globalement Bleu = Couleur(0, 0, 255) # Définition de la couleur bleu Jaune = Couleur(255, 255, 0) # Définition de la couleur jaune Rouge = Couleur(255, 0, 0) # Définition de la couleur rouge couleur = Couleur(200, 150, 250) # Couleur de dessin des segments pi1, pi2, pi3 = PointImage(), PointImage(), PointImage() # Points dans fenêtre image pr1, pr2, pr3 = PointReel(), PointReel(), PointReel() # Points dans fenêtre image transfo = TransfoImages() # Structure de transformation fr = FenetreReel(PointReel(-11,-11),PointReel(11,11)) # Fenêtre réelle fi = FenetreImage() # Fenêtre image # Détermination de la dimension minimale de la fenêtre minDim = Hauteur if minDim > Largeur: minDim = Largeur # Réglage automatique de la fenêtre image fi.bg.col = MARGE + (Largeur - minDim) // 2 fi.bg.lig = minDim - MARGE - (Hauteur - minDim) // 2 fi.hd.col = Largeur - MARGE - (Largeur - minDim) // 2 fi.hd.lig = MARGE + (Hauteur - minDim) // 2 # Affichage seulement s'il n'a pas déjà été fait (pour éviter une charge CPU inutile) if dejaFait == False: Effacer() # Initialisation de la fenêtre en noir dejaFait = True; # Indique que les dessins sont faits transfo = CalculTransfosFenetres(fr, fi) # Calcul de la transfo Réel -> Image # Affichage du contour de la fenêtre image (les 4 côtés) en utilisant le dessin de segments image # Affichage des axes avec graduation entières if (not action == CUBE) and (not action == PARAM) and (not action == FONCTION): DessineContours(fi, Rouge) DessineAxes(1.0, transfo, Rouge) if action == CUBE: # 1ère étape : cube des couleurs AfficheTexte("Faces simples", (600, MARGE), Jaune) DessineFacesCubeCouleurs() # Affichage des faces du cube AfficheTexte("Cube des couleurs", (600, MARGE), Jaune) elif action == TRIS: # 2ème étape : segments image et triangles AfficheTexte("Triangles", (MARGE, MARGE), Jaune) pi1 = PointImage(fi.bg.col+10, fi.hd.lig+10) pi2 = PointImage(fi.bg.col+550, fi.hd.lig+300) pi3 = PointImage(fi.bg.col+375, fi.hd.lig+500) # Dessin d'un triangle image DessineSegmentImage(pi1, pi2, couleur) DessineSegmentImage(pi2, pi3, couleur) DessineSegmentImage(pi1, pi3, couleur) # Dessin d'un triangle réel pas complètement inclus dans la fenêtre pr1 = PointReel(-5.0, -5.0) pr2 = PointReel(15.0, 2.5) pr3 = PointReel(-7.5, 12.5) pi1 = TransformationRvI(pr1, transfo) pi2 = TransformationRvI(pr2, transfo) pi3 = TransformationRvI(pr3, transfo) DessineSegmentReel(pr1, pr2, Bleu, transfo) DessineSegmentReel(pr2, pr3, Bleu, transfo) DessineSegmentReel(pr1, pr3, Bleu, transfo) DessineSegmentReel(pr1, pr2, Rouge, transfo, True) DessineSegmentReel(pr2, pr3, Rouge, transfo, True) DessineSegmentReel(pr1, pr3, Rouge, transfo, True) ColoriePixel(pi1.col, pi1.lig, Jaune) ColoriePixel(pi2.col, pi2.lig, Jaune) ColoriePixel(pi3.col, pi3.lig, Jaune) elif action == FUSEAU1: # 3ème étape : fuseau issu du point haut-gauche AfficheTexte("Fuseau n°1", (MARGE, MARGE), Jaune) # À COMPLÉTER # Boucle de dessin de segments balayant la fenêtre # ... pass elif action == FUSEAU2: # 4ème étape : fuseau issu du point haut-droit AfficheTexte("Fuseau n°2", (MARGE, MARGE), Jaune) # À COMPLÉTER # Boucle de dessin de segments balayant la fenêtre # ... pass elif action == FUSEAU3: # 5ème étape : fuseau issu du point bas-droit AfficheTexte("Fuseau n°3", (MARGE, MARGE), Jaune) # À COMPLÉTER # Boucle de dessin de segments balayant la fenêtre # ... pass elif action == FUSEAU4: # 6ème étape : fuseau issu du point bas-gauche AfficheTexte("Fuseau n°4", (MARGE, MARGE), Jaune) # À COMPLÉTER # Boucle de dessin de segments balayant la fenêtre # ... pass ################################################################################ # CORPS DU PROGRAMME ################################################################################ # Appel de la fonction d'affichage effectuant les dessins Affichage(action) # Boucle de gestion des évènements while fini == False: # Gestion du clavier gestionTouches() # Gestion de la souris gestionSouris() # Mise à jour de la fenêtre MiseAJour() # Fin du programme sortie()