Source code for ClearMap.Visualization.Vispy.arbitrary_rotation_camera

# -*- coding: utf-8 -*-
# Copyright (c) Vispy Development Team. All Rights Reserved.
# Distributed under the (new) BSD License. See LICENSE.txt for more info.

# Modified by C. Kirst to allow arbitrary rotations

from __future__ import division

import numpy as np
from vispy.scene import TurntableCamera

from vispy.util import keys

MOUSE_LEFT = 1
MOUSE_RIGHT = 2


[docs] class ArbitraryRotationCamera(TurntableCamera): """ 3D camera class that orbits around a center point while maintaining a view on a center point. For this camera, the ``scale_factor`` indicates the zoom level, and the ``center`` indicates the position to put at the center of the view. Parameters ---------- fov : float Field of view. Zero (default) means orthographic projection. elevation : float Elevation angle in degrees. Positive angles place the camera above the center point, negative angles place the camera below the center point. azimuth : float Azimuth angle in degrees. Zero degrees places the camera on the positive x-axis, pointing in the negative x direction. roll : float Roll angle in degrees distance : float | None The distance of the camera from the rotation point (only makes sense if fov > 0). If None (default) the distance is determined from the scale_factor and fov. **kwargs : dict Keyword arguments to pass to `BaseCamera`. Notes ----- Interaction: * LMB: orbits the view around its center point. * RMB or scroll: change scale_factor (i.e. zoom level) * SHIFT + LMB: translate the center point * SHIFT + RMB: change FOV """ def __init__(self, fov=45.0, elevation=30.0, azimuth=30.0, roll=0.0, distance=None, translate_speed=1.0, **kwargs): # REFACTOR: theoretically useless super().__init__(fov=fov, elevation=elevation, azimuth=azimuth, roll=roll, distance=distance, translate_speed=translate_speed, **kwargs) @property def elevation(self): """ The angle of the camera in degrees above the horizontal (x, z) plane. """ return self._elevation @elevation.setter def elevation(self, elev): elev = float(elev) while elev < -180: elev += 360 while elev > 180: elev -= 360 self._elevation = elev self.view_changed()
[docs] def orbit(self, azim, elev): """ Orbits the camera around the center position. Parameters ---------- azim : float Angle in degrees to rotate horizontally around the center point. elev : float Angle in degrees to rotate vertically around the center point. """ self.azimuth += azim self.elevation = self.elevation + elev # np.clip(self.elevation + elev, -90, 90) self.view_changed()
def _update_roll(self, event): """Update roll parameter based on mouse movement""" _, _, d = self._get_mouse_points(event) self._event_value = self.roll if self._event_value is None else self._event_value self.roll = self._event_value + d[0] def _update_elevation(self, event): """Update elevation parameter based on mouse movement""" _, _, d = self._get_mouse_points(event) self._event_value = self.elevation if self._event_value is None else self._event_value self.elevation = self._event_value + d[0] def _update_azimuth(self, event): """Update azimuth parameter based on mouse movement""" _, _, d = self._get_mouse_points(event) self._event_value = self.azimuth if self._event_value is None else self._event_value self.azimuth = self._event_value + d[0] def _update_zoom(self, event): """Update zoom based on mouse movement""" _, _, d = self._get_mouse_points(event) if self._event_value is None: self._event_value = (self._scale_factor, self._distance) zoom_y = (1 + self.zoom_factor) ** d[1] self.scale_factor = self._event_value[0] * zoom_y # Modify distance if given if self._distance is not None: self._distance = self._event_value[1] * zoom_y self.view_changed() def _get_mouse_points(self, event): p1 = event.mouse_event.press_event.pos p2 = event.mouse_event.pos d = p2 - p1 return p1, p2, d def _update_translate(self, event): """Update translate parameter based on mouse movement""" p1, p2, _ = self._get_mouse_points(event) norm = np.mean(self._viewbox.size) if self._event_value is None or len(self._event_value) == 2: self._event_value = self.center dist = (p1 - p2) / norm * self._scale_factor # FIXME: why invert d dist[1] *= -1 # Black magic part 1: turn 2D into 3D translations dx, dy, dz = self._dist_to_trans(dist) # Black magic part 2: take up-vector and flipping into account ff = self._flip_factors up, forward, right = self._get_dim_vectors() dx, dy, dz = right * dx + forward * dy + up * dz dx, dy, dz = ff[0] * dx, ff[1] * dy, ff[2] * dz c = self._event_value self.center = c[0] + dx, c[1] + dy, c[2] + dz def _update_fov(self, event): """Update fov parameter based on mouse movement""" p1, p2, d = self._get_mouse_points(event) self._event_value = self._fov if self._event_value is None else self._event_value fov = self._event_value - d[1] / 5.0 self.fov = min(180.0, max(0.0, fov)) def _rotate_tr(self): """Rotate the transformation matrix based on camera parameters""" up, forward, right = self._get_dim_vectors() super()._rotate_tr() self.transform.rotate(self.roll, forward)
[docs] def viewbox_mouse_event(self, event): """ The viewbox received a mouse event; update transform accordingly. Parameters ---------- event : instance of Event The event. """ if event.handled or not self.interactive: return # PerspectiveCamera.viewbox_mouse_event(self, event) if event.type == 'mouse_wheel': s = 1.1 ** - event.delta[1] self._scale_factor *= s if self._distance is not None: self._distance *= s self.view_changed() elif event.type == 'mouse_release': self._event_value = None # Reset elif event.type == 'mouse_press': event.handled = True elif event.type == 'mouse_move': if event.press_event is None: return modifiers = event.mouse_event.modifiers if MOUSE_LEFT in event.buttons: if not modifiers: # Rotate self._update_rotation(event) elif keys.CONTROL in modifiers: # roll only self._update_roll(event) elif keys.META in modifiers: # elevation only self._update_elevation(event) elif keys.SHIFT in modifiers: # azimuth only self._update_azimuth(event) elif MOUSE_RIGHT in event.buttons: if not modifiers: # zoom self._update_zoom(event) elif keys.SHIFT in modifiers: # Translate self._update_translate(event) elif keys.META in modifiers: # Change fov self._update_fov(event)