1

I am trying to understand why the isometric view is not correct. The most accurate representation is at 55 degrees with the vertical axis, and 45 with the horizontal. In the mcve there are two points who should be aligned at 45 degrees on each axis. I have set up a camera movement based in the sphere algebra definition. The edges of the planes should coincide, but they do not.

MCVE:

from OpenGL.GL import glClear, GL_COLOR_BUFFER_BIT, glEnable, GL_DEPTH_TEST, glMatrixMode, GL_PROJECTION, \
    glLoadIdentity, glOrtho, glClearColor, GL_DEPTH_BUFFER_BIT, GL_MODELVIEW, glLineWidth, glBegin, glColor, glVertex, \
    glEnd, glPointSize, GL_POINT_SMOOTH, GL_POINTS, GL_BLEND, glBlendFunc, GL_SRC_ALPHA, \
    GL_QUADS, glDisable, GL_LINES, GL_LINE_LOOP, glDepthMask, GL_FALSE, GL_TRUE, GL_ONE_MINUS_SRC_ALPHA
from OpenGL.GLU import gluLookAt
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import QOpenGLWidget
from math import sin, cos, radians


class Renderizador(QOpenGLWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.dx = 0
        self.dy = 0
        self.dz = 0
        self.theta = 405
        self.phi = 45
        self.x = sin(radians(self.theta)) * cos(radians(self.phi)) + self.dx
        self.z = sin(radians(self.theta)) * sin(radians(self.phi)) + self.dz
        self.y = cos(radians(self.theta)) + self.dy
        self.vertices_vertical = ((100, 100, 0), (-100, 100, 0), (-100, 0, 0), (100, 0, 0))
        self.vertices_vertical_debajo = ((100, 0, 0), (-100, 0, 0), (-100, -100, 0), (100, -100, 0))
        self.vertices_horizontal = ((100, 0, 0), (100, 0, 100), (-100, 0, 100), (-100, 0, 0))
        self.vertices_horizontal_detras = ((100, 0, 0), (100, 0, -100), (-100, 0, -100), (-100, 0, 0))
        self.vertices_borde_v = ((100, 100, 0), (100, -100, 0), (-100, -100, 0), (-100, 100, 0))
        self.vertices_borde_h = ((100, 0, 100), (-100, 0, 100), (-100, 0, -100), (100, 0, -100))
        self.puntos = [("a", 1, 1, 1), ("b", 10, 10, 10)]

    def recalcular(self):
        self.x = sin(radians(self.theta)) * cos(radians(self.phi)) + self.dx
        self.z = sin(radians(self.theta)) * sin(radians(self.phi)) + self.dz
        self.y = cos(radians(self.theta)) + self.dy
        gluLookAt(self.x, self.y, self.z, self.dx, self.dy, self.dz, 0, 1, 0)
        self.update()

    def dibujar_planos(self):
        glEnable(GL_BLEND)
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
        glDepthMask(GL_FALSE)
        glBegin(GL_QUADS)
        glColor(1, 0, 0, 0.5)
        for vertex in range(4):
            glVertex(self.vertices_horizontal_detras[vertex])
        glColor(0, 1, 0, 0.5)
        for vertex in range(4):
            glVertex(self.vertices_vertical_debajo[vertex])
        glColor(0, 1, 0, 0.5)
        for vertex in range(4):
            glVertex(self.vertices_vertical[vertex])
        glColor(1, 0, 0, 0.5)
        for vertex in range(4):
            glVertex(self.vertices_horizontal[vertex])
        glEnd()
        glDepthMask(GL_TRUE)
        glDisable(GL_BLEND)
        glLineWidth(1)
        glColor(0.2, 1, 0.2, 0.5)
        glBegin(GL_LINE_LOOP)
        for vertex in range(4):
            glVertex(self.vertices_borde_v[vertex])
        glColor(1, 0.2, 0.2, 0.5)
        glEnd()
        glBegin(GL_LINE_LOOP)
        for vertex in range(4):
            glVertex(self.vertices_borde_h[vertex])
        glEnd()

    @staticmethod
    def dibujar_ejes():
        glLineWidth(3)
        glBegin(GL_LINES)
        # X ROJO
        glColor(1, 0, 0)
        glVertex(0, 0, 0)
        glVertex(10, 0, 0)
        # Y VERDE
        glColor(0, 1, 0)
        glVertex(0, 0, 0)
        glVertex(0, 10, 0)
        # Z AZUL
        glColor(0, 0, 1)
        glVertex(0, 0, 0)
        glVertex(0, 0, 10)
        glEnd()

    def dibujar_punto(self):

        glColor(0, 0, 0, 0)
        glPointSize(4)
        glEnable(GL_POINT_SMOOTH)
        glBegin(GL_POINTS)
        for i in range(len(self.puntos)):
            glVertex(self.puntos[i][1], self.puntos[i][3], self.puntos[i][2])
        glEnd()

        # self.x = sin(radians(self.theta)) * cos(radians(self.phi)) + self.dx
        # self.z = sin(radians(self.theta)) * sin(radians(self.phi)) + self.dz
        # self.y = cos(radians(self.theta)) + self.dy
        # gluLookAt(self.x, self.y, self.z, self.dx, self.dy, self.dz, 0, 1, 0)
        self.update()

    def initializeGL(self):
        glEnable(GL_DEPTH_TEST)
        glMatrixMode(GL_PROJECTION)
        glLoadIdentity()
        glOrtho(100, -100, -100, 100, -500, 150)
        glMatrixMode(GL_MODELVIEW)

    def paintGL(self):
        glClearColor(1, 1, 1, 0)
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        glLoadIdentity()
        up = 1
        if self.theta == 360:
            up = -1
        gluLookAt(self.x, self.y, self.z, self.dx, self.dy, self.dz, 0, up, 0)
        self.dibujar_ejes()
        self.dibujar_planos()
        self.dibujar_punto()

    def keyPressEvent(self, event):
        if event.key() == QtCore.Qt.Key_W:
            self.theta -= 5
        elif event.key() == QtCore.Qt.Key_A:
            self.phi -= 5
        elif event.key() == QtCore.Qt.Key_S:
            self.theta += 5
        elif event.key() == QtCore.Qt.Key_D:
            self.phi += 5
        elif event.key() == QtCore.Qt.Key_Q:
            self.dz += 1
        elif event.key() == QtCore.Qt.Key_E:
            self.dz -= 1
        elif event.key() == QtCore.Qt.Key_Left:
            self.dx -= 1
        elif event.key() == QtCore.Qt.Key_Up:
            self.dy += 1
        elif event.key() == QtCore.Qt.Key_Right:
            self.dx += 1
        elif event.key() == QtCore.Qt.Key_Down:
            self.dy -= 1

        if self.theta < 360:
            self.theta = 360
        if self.theta > 540:
            self.theta = 540
        if self.phi >= 360:
            self.phi -= 360
        if self.phi < 0:
            self.phi += 360

        self.x = sin(radians(self.theta)) * cos(radians(self.phi)) + self.dx
        self.z = sin(radians(self.theta)) * sin(radians(self.phi)) + self.dz
        self.y = cos(radians(self.theta)) + self.dy

        ui.actualizar()
        self.update()
        super().keyPressEvent(event)


class UiVentana(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(UiVentana, self).__init__(parent)
        ventana.resize(1500, 1015)

        self.widget_central = QtWidgets.QWidget(ventana)
        self.Renderizador = Renderizador(self.widget_central)
        self.Renderizador.setGeometry(QtCore.QRect(0, 0, 1000, 1000))
        self.Renderizador.setFocusPolicy(QtCore.Qt.StrongFocus)

        self.label_5 = QtWidgets.QLabel(self.widget_central)
        self.label_5.setGeometry(QtCore.QRect(1110, 49, 160, 20))
        self.label_6 = QtWidgets.QLabel(self.widget_central)
        self.label_6.setGeometry(QtCore.QRect(1010, 70, 111, 16))
        self.label_7 = QtWidgets.QLabel(self.widget_central)
        self.label_7.setGeometry(QtCore.QRect(1130, 70, 130, 16))

        self.label_5.setText("X: Y: Z:")
        self.label_6.setText("Ángulo vertical:")
        self.label_7.setText("Ángulo horizontal:")

        ventana.setCentralWidget(self.widget_central)
        ventana.show()

    def actualizar(self):
        x = round(100 * (sin(radians(self.Renderizador.theta)) * cos(radians(self.Renderizador.phi)))
                  + self.Renderizador.dx, 2)
        z = round(100 * (sin(radians(self.Renderizador.theta)) * sin(radians(self.Renderizador.phi)))
                  + self.Renderizador.dz, 2)
        y = round(100 * (cos(radians(self.Renderizador.theta))) + self.Renderizador.dy, 2)
        theta = self.Renderizador.theta - 360
        phi = self.Renderizador.phi
        if x == -0:
            x = 0
        if y == -0:
            y = 0
        if z == -0:
            z = 0
        self.label_5.setText("X: {} Y: {} Z: {}".format(x, z, y))
        self.label_6.setText("Ángulo vertical: " + str(theta))
        self.label_7.setText("Ángulo horizontal: " + str(phi))


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication([])
    ventana = QtWidgets.QMainWindow()
    ui = UiVentana()
    sys.exit(app.exec_())
Rabbid76
  • 202,892
  • 27
  • 131
  • 174
Jaime02
  • 299
  • 7
  • 21

1 Answers1

1

For a perfect isometric view, the point of view has to be on a diagonal of a cube. This means if the target of the view is (0, 0, 0) then the absolute value of the x, y and z coordinate of the eye position has to be equal.

When you look at the formula which calculates x, y, and z:

self.x = sin(radians(self.theta)) * cos(radians(self.phi)) + self.dx
self.z = sin(radians(self.theta)) * sin(radians(self.phi)) + self.dz
self.y = cos(radians(self.theta)) + self.dy

This means you've to find a phi and theta that result in equal x, y and z.

For phi this is easy. Phi is only use do calculate x and z. x depends on cos(phi) and z depends on sin(phi) since sin(45) == cos(45), phi has to be 45°.

sin(45) is equal 1/sqrt(2).

So it has to be found a theta, where x, y and z are equal when

x = sin(theta) * 1/sqrt(2)
z = sin(theta) * 1/sqrt(2)
y = cos(theta)  

This means

sin(theta) * 1/sqrt(2) = cos(theta) 

From Pythagoras we know that:

cos²(theta) + sin²(theta) = 1

So

sin(theta) = cos(theta) * sqrt(2) and sin²(theta) = 1 - cos²(theta)

1 - cos²(theta) = cos²(theta) * 2

1 = cos(theta) * sqrt(3)

theta = acos(1/sqrt(3))

At the end phi and theta have to be:

self.phi   = 45
self.theta = 54.735610317245345684622999669982
Rabbid76
  • 202,892
  • 27
  • 131
  • 174