File: magnify.py

package info (click to toggle)
python-vispy 0.15.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 8,868 kB
  • sloc: python: 59,799; javascript: 6,800; makefile: 69; sh: 6
file content (163 lines) | stat: -rw-r--r-- 5,387 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# -*- coding: utf-8 -*-
# Copyright (c) Vispy Development Team. All Rights Reserved.
# Distributed under the (new) BSD License. See LICENSE.txt for more info.
from __future__ import division

import numpy as np

from .panzoom import PanZoomCamera
from ...visuals.transforms.nonlinear import (MagnifyTransform, 
                                             Magnify1DTransform)
from ...app import Timer


class MagnifyCamera(PanZoomCamera):
    """Camera implementing a MagnifyTransform combined with PanZoomCamera.

    Parameters
    ----------
    size_factor : float
        The size factor to use.
    radius_ratio : float
        The radius ratio to use.
    **kwargs : dict
        Keyword arguments to pass to `PanZoomCamera` and create a transform.

    Notes
    -----
    This Camera uses the mouse cursor position to set the center position of
    the MagnifyTransform, and uses mouse wheel events to adjust the 
    magnification factor.

    At high magnification, very small mouse movements can result in large
    changes, so we use a timer to animate transitions in the transform 
    properties.

    The camera also adjusts the size of its "lens" area when the view is
    resized.
    """

    transform_class = MagnifyTransform

    def __init__(self, size_factor=0.25, radius_ratio=0.9, **kwargs):
        # what fraction of the view width to use for radius
        self.size_factor = size_factor

        # ratio of inner to outer lens radius
        self.radius_ratio = radius_ratio

        # Extract kwargs for panzoom
        camkwargs = {}
        for key in ('parent', 'name', 'rect', 'aspect'):
            if key in kwargs:
                camkwargs[key] = kwargs.pop(key)

        # Create the mag transform - kwrds go here
        self.mag = self.transform_class(**kwargs)

        # for handling smooth transitions
        self.mag_target = self.mag.mag
        self.mag._mag = self.mag_target
        self.mouse_pos = None
        self.timer = Timer(interval=0.016, connect=self.on_timer)

        super(MagnifyCamera, self).__init__(**camkwargs)

        # This tells the camera to insert the magnification transform at the
        # beginning of the transform it applies to the scene. This is the 
        # correct place for the mag transform because:
        # 1. We want it to apply to everything inside the scene, and not to
        #    the ViewBox itself or anything outside of the ViewBox.
        # 2. We do _not_ want the pan/zoom transforms applied first, because
        #    the scale factors implemented there should not change the shape
        #    of the lens.
        self.pre_transform = self.mag

    def _viewbox_set(self, viewbox):
        PanZoomCamera._viewbox_set(self, viewbox)

    def _viewbox_unset(self, viewbox):
        PanZoomCamera._viewbox_unset(self, viewbox)
        self.timer.stop()

    def viewbox_mouse_event(self, event):
        """The ViewBox mouse event handler

        Parameters
        ----------
        event : instance of Event
            The mouse event.
        """
        # When the attached ViewBox reseives a mouse event, it is sent to the
        # camera here.

        self.mouse_pos = event.pos[:2]
        if event.type == 'mouse_wheel':
            # wheel rolled; adjust the magnification factor and hide the 
            # event from the superclass
            m = self.mag_target 
            m *= 1.2 ** event.delta[1]
            m = m if m > 1 else 1
            self.mag_target = m
        else:
            # send everything _except_ wheel events to the superclass
            super(MagnifyCamera, self).viewbox_mouse_event(event)

        # start the timer to smoothly modify the transform properties. 
        if not self.timer.running:
            self.timer.start()

        self._update_transform()

    def on_timer(self, event=None):
        """Timer event handler

        Parameters
        ----------
        event : instance of Event
            The timer event.
        """
        # Smoothly update center and magnification properties of the transform
        k = np.clip(100. / self.mag.mag, 10, 100)
        s = 10**(-k * event.dt)

        c = np.array(self.mag.center)
        c1 = c * s + self.mouse_pos * (1-s)

        m = self.mag.mag * s + self.mag_target * (1-s)

        # If changes are very small, then it is safe to stop the timer.
        if (np.all(np.abs((c - c1) / c1) < 1e-5) and 
                (np.abs(np.log(m / self.mag.mag)) < 1e-3)):
            self.timer.stop()

        self.mag.center = c1
        self.mag.mag = m

        self._update_transform()

    def viewbox_resize_event(self, event):
        """The ViewBox resize event handler

        Parameters
        ----------
        event : instance of Event
            The viewbox resize event.
        """
        PanZoomCamera.viewbox_resize_event(self, event)
        self.view_changed()

    def view_changed(self):
        # make sure radii are updated when a view is attached.
        # when the view resizes, we change the lens radii to match.
        if self._viewbox is not None:
            vbs = self._viewbox.size
            r = min(vbs) * self.size_factor
            self.mag.radii = r * self.radius_ratio, r

        PanZoomCamera.view_changed(self)


class Magnify1DCamera(MagnifyCamera):
    transform_class = Magnify1DTransform
    __doc__ = MagnifyCamera.__doc__