Source code for madgui.survey.camera

"""
Utilities for dealing with coordinate system / camera transformations.
"""

from math import pi
import numpy as np

from PyQt5.QtCore import QObject, pyqtSignal

from .transform import (
    gl_array, translate, rotate, inverted, perspective_projection)


ORIGIN = gl_array([0, 0, 0, 1])


[docs]class Camera(QObject): updated = pyqtSignal() distance = 1 # camera angle theta = 0 phi = 0 psi = 0 # perspective fov = 70.0 near = 0.01 far = 1000 def __init__(self): super().__init__() self.position = gl_array([0, 0, -1]) self.center = gl_array([0, 0, 0]) self.theta = self.phi = self.psi = 0
[docs] def zoom(self, scale): """Scale the figure uniformly along both axes.""" self.fov = np.clip(self.fov - scale/5, 1.0, 180.0) self.updated.emit()
[docs] def look_from(self, theta, phi, psi=0): """Set camera position by rotating camera around its view target. The angles are the coordinates of the camera relative to the target, at a distance ``self.distance``.""" phi = np.clip(phi, -pi/2, pi/2) rot = rotate(theta, phi, psi) tra0 = translate(*self.center) tra1 = translate(0, 0, self.distance) goto_camera = tra0 @ rot.T @ tra1 self.position = (goto_camera @ ORIGIN)[:3] self.view_matrix = inverted(goto_camera) self.theta, self.phi, self.psi = theta, phi, psi self.updated.emit()
[docs] def look_toward(self, theta, phi, psi=0): """Rotate camera at fixed position. The center of view is recalculated to be at distance ``self.distance`` from the camera position in the direction given by the angles.""" phi = np.clip(phi, -pi/2, pi/2) rot = rotate(theta, phi, psi) tra0 = translate(*self.position) tra1 = translate(0, 0, -self.distance) self.center = (tra0 @ rot.T @ tra1 @ ORIGIN)[:3] self.view_matrix = tra1 @ rot @ translate(*-self.center) self.theta, self.phi, self.psi = theta, phi, psi self.updated.emit()
[docs] def translate(self, dx, dy, dz): """Move camera position and its view center along the local coordinate system. Keeps angles fixed.""" rot = rotate(self.theta, self.phi, self.psi) tra = translate(dx, dy, dz) move = rot.T @ tra @ rot self.center = (move @ gl_array([*self.center, 1]))[:3] self.position = (move @ gl_array([*self.position, 1]))[:3] self.look_toward(self.theta, self.phi, self.psi)
[docs] def projection(self, width, height): """Return a perspective projection matrix for a viewport with the given size.""" return perspective_projection( self.fov, width/height, self.near, self.far)