"""Atividade — composição navegável em forma de pedra lapidada. Aula 06 — Transformações Geométricas 3D em OpenGL. Execute com: uv run aula-06-visualizacao-3d/pedra_lapidada_base.py O arquivo abre mostrando apenas o cubo central. Complete os dois TODOs de ``desenhar_pedra_lapidada`` para acoplar uma pirâmide a cada face lateral. Não duplique nem desloque os vértices da pirâmide: use a pilha de matrizes. Teclas: setas giram o conjunto W / S movem o conjunto em profundidade A / D movem o conjunto lateralmente 0 restaura posição e rotação Esc fecha """ from __future__ import annotations import sys from PySide6.QtCore import Qt from PySide6.QtGui import QSurfaceFormat from PySide6.QtOpenGLWidgets import QOpenGLWidget from PySide6.QtWidgets import QApplication from OpenGL.GL import * from OpenGL.GLU import gluLookAt, gluPerspective PASSO_MOVIMENTO = 0.15 PASSO_ROTACAO = 4.0 OLHO = (4.2, 3.2, 7.5) ALVO = (0.0, 0.0, 0.0) UP = (0.0, 1.0, 0.0) COR_LINHA = (0.94, 0.65, 0.29) COR_FUNDO = (0.10, 0.11, 0.13, 1.0) # Cubo de lado 2, centrado na origem. VERTICES_CUBO = [ (-1.0, -1.0, -1.0), (1.0, -1.0, -1.0), (1.0, 1.0, -1.0), (-1.0, 1.0, -1.0), (-1.0, -1.0, 1.0), (1.0, -1.0, 1.0), (1.0, 1.0, 1.0), (-1.0, 1.0, 1.0), ] ARESTAS_CUBO = [ (0, 1), (1, 2), (2, 3), (3, 0), (4, 5), (5, 6), (6, 7), (7, 4), (0, 4), (1, 5), (2, 6), (3, 7), ] # Pirâmide local orientada para +x. Sua base está no plano x=0 e tem o # mesmo tamanho da face lateral do cubo; o ápice fica em x=1.5. VERTICES_PIRAMIDE = [ (1.5, 0.0, 0.0), (0.0, -1.0, -1.0), (0.0, 1.0, -1.0), (0.0, 1.0, 1.0), (0.0, -1.0, 1.0), ] ARESTAS_PIRAMIDE = [ (0, 1), (0, 2), (0, 3), (0, 4), (1, 2), (2, 3), (3, 4), (4, 1), ] def desenhar_arestas(vertices, arestas) -> None: """Desenha uma tabela de arestas que referencia uma lista de vértices.""" glBegin(GL_LINES) for inicio, fim in arestas: glVertex3f(*vertices[inicio]) glVertex3f(*vertices[fim]) glEnd() def desenhar_pedra_lapidada() -> None: """Compõe o cubo central e duas instâncias da pirâmide local.""" desenhar_arestas(VERTICES_CUBO, ARESTAS_CUBO) # TODO 1 — ponta direita: # salve a matriz; translade a pirâmide para que sua base x=0 # coincida com a face x=+1 do cubo; desenhe; restaure a matriz. # TODO 2 — ponta esquerda: # salve a matriz; translade até a face x=-1; gire 180° em torno de Y # para que o ápice aponte para -x; desenhe; restaure a matriz. pass class PedraLapidada(QOpenGLWidget): def __init__(self, parent=None): super().__init__(parent) self.pos = [0.0, 0.0, 0.0] self.rot_x = -12.0 self.rot_y = 28.0 self.setFocusPolicy(Qt.FocusPolicy.StrongFocus) def initializeGL(self) -> None: glClearColor(*COR_FUNDO) glEnable(GL_DEPTH_TEST) def resizeGL(self, w: int, h: int) -> None: dpr = self.devicePixelRatioF() w, h = max(1, round(w * dpr)), max(1, round(h * dpr)) glViewport(0, 0, w, h) glMatrixMode(GL_PROJECTION) glLoadIdentity() gluPerspective(50.0, w / float(h), 0.1, 100.0) glMatrixMode(GL_MODELVIEW) def paintGL(self) -> None: glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) glLoadIdentity() gluLookAt(*OLHO, *ALVO, *UP) glColor3f(*COR_LINHA) glLineWidth(2.0) glPushMatrix() glTranslatef(*self.pos) glRotatef(self.rot_y, 0.0, 1.0, 0.0) glRotatef(self.rot_x, 1.0, 0.0, 0.0) desenhar_pedra_lapidada() glPopMatrix() self.setWindowTitle( f"Pedra lapidada — pos=({self.pos[0]:.1f}, {self.pos[1]:.1f}, " f"{self.pos[2]:.1f}) rot_x={self.rot_x:.0f}° rot_y={self.rot_y:.0f}°" ) def keyPressEvent(self, event) -> None: tecla = event.key() if tecla == Qt.Key.Key_Escape: self.close() return if tecla == Qt.Key.Key_0: self.pos = [0.0, 0.0, 0.0] self.rot_x, self.rot_y = -12.0, 28.0 elif tecla == Qt.Key.Key_Left: self.rot_y -= PASSO_ROTACAO elif tecla == Qt.Key.Key_Right: self.rot_y += PASSO_ROTACAO elif tecla == Qt.Key.Key_Up: self.rot_x -= PASSO_ROTACAO elif tecla == Qt.Key.Key_Down: self.rot_x += PASSO_ROTACAO elif tecla == Qt.Key.Key_W: self.pos[2] -= PASSO_MOVIMENTO elif tecla == Qt.Key.Key_S: self.pos[2] += PASSO_MOVIMENTO elif tecla == Qt.Key.Key_A: self.pos[0] -= PASSO_MOVIMENTO elif tecla == Qt.Key.Key_D: self.pos[0] += PASSO_MOVIMENTO else: return self.update() def main() -> int: formato = QSurfaceFormat() formato.setVersion(2, 1) formato.setProfile(QSurfaceFormat.OpenGLContextProfile.NoProfile) formato.setDepthBufferSize(24) QSurfaceFormat.setDefaultFormat(formato) app = QApplication(sys.argv) janela = PedraLapidada() janela.resize(900, 600) janela.show() return app.exec() if __name__ == "__main__": raise SystemExit(main())