vispy 0.9.5__cp38-cp38-win_amd64.whl → 0.14.0__cp38-cp38-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of vispy might be problematic. Click here for more details.
- vispy/app/backends/_glfw.py +2 -2
- vispy/app/backends/_pyglet.py +8 -2
- vispy/app/backends/_qt.py +88 -63
- vispy/app/backends/_wx.py +6 -1
- vispy/app/canvas.py +4 -2
- vispy/app/tests/test_canvas.py +52 -1
- vispy/app/tests/test_context.py +5 -3
- vispy/color/color_array.py +8 -1
- vispy/color/colormap.py +5 -25
- vispy/geometry/meshdata.py +76 -38
- vispy/geometry/rect.py +6 -0
- vispy/geometry/tests/test_meshdata.py +72 -0
- vispy/gloo/buffer.py +12 -0
- vispy/gloo/gl/_constants.py +9 -5
- vispy/gloo/gl/_es2.py +8 -4
- vispy/gloo/gl/_gl2.py +2 -3
- vispy/gloo/gl/_proxy.py +1 -1
- vispy/gloo/gl/_pyopengl2.py +12 -7
- vispy/gloo/gl/tests/test_names.py +3 -0
- vispy/gloo/glir.py +26 -13
- vispy/gloo/program.py +39 -22
- vispy/gloo/tests/test_program.py +9 -2
- vispy/gloo/tests/test_texture.py +19 -2
- vispy/gloo/texture.py +46 -16
- vispy/gloo/wrappers.py +4 -2
- vispy/glsl/build_spatial_filters.py +241 -293
- vispy/glsl/misc/spatial-filters.frag +1299 -254
- vispy/io/_data/spatial-filters.npy +0 -0
- vispy/io/datasets.py +2 -2
- vispy/io/image.py +1 -1
- vispy/io/stl.py +3 -3
- vispy/scene/cameras/base_camera.py +6 -2
- vispy/scene/cameras/panzoom.py +10 -14
- vispy/scene/cameras/perspective.py +6 -0
- vispy/scene/cameras/tests/test_cameras.py +27 -0
- vispy/scene/cameras/tests/test_perspective.py +37 -0
- vispy/scene/cameras/turntable.py +39 -23
- vispy/scene/canvas.py +9 -5
- vispy/scene/events.py +9 -0
- vispy/scene/node.py +19 -2
- vispy/scene/tests/test_canvas.py +30 -1
- vispy/scene/tests/test_visuals.py +113 -0
- vispy/scene/visuals.py +6 -1
- vispy/scene/widgets/viewbox.py +3 -2
- vispy/testing/_runners.py +6 -12
- vispy/testing/_testing.py +3 -4
- vispy/util/check_environment.py +4 -4
- vispy/util/gallery_scraper.py +50 -32
- vispy/util/tests/test_gallery_scraper.py +2 -0
- vispy/util/transforms.py +1 -1
- vispy/util/wrappers.py +1 -1
- vispy/version.py +2 -3
- vispy/visuals/__init__.py +2 -0
- vispy/visuals/_scalable_textures.py +20 -17
- vispy/visuals/collections/array_list.py +3 -3
- vispy/visuals/collections/base_collection.py +1 -1
- vispy/visuals/ellipse.py +1 -1
- vispy/visuals/filters/__init__.py +3 -2
- vispy/visuals/filters/base_filter.py +120 -0
- vispy/visuals/filters/clipping_planes.py +24 -12
- vispy/visuals/filters/markers.py +28 -0
- vispy/visuals/filters/mesh.py +61 -6
- vispy/visuals/filters/tests/test_primitive_picking_filters.py +70 -0
- vispy/visuals/graphs/graph.py +1 -1
- vispy/visuals/image.py +114 -26
- vispy/visuals/image_complex.py +130 -0
- vispy/visuals/instanced_mesh.py +152 -0
- vispy/visuals/isocurve.py +1 -1
- vispy/visuals/line/dash_atlas.py +46 -41
- vispy/visuals/line/line.py +2 -5
- vispy/visuals/markers.py +310 -384
- vispy/visuals/mesh.py +2 -2
- vispy/visuals/shaders/function.py +3 -0
- vispy/visuals/shaders/tests/test_function.py +6 -0
- vispy/visuals/tests/test_axis.py +2 -2
- vispy/visuals/tests/test_image.py +92 -2
- vispy/visuals/tests/test_image_complex.py +36 -0
- vispy/visuals/tests/test_instanced_mesh.py +50 -0
- vispy/visuals/tests/test_markers.py +6 -0
- vispy/visuals/tests/test_mesh.py +17 -0
- vispy/visuals/tests/test_text.py +11 -0
- vispy/visuals/tests/test_volume.py +218 -12
- vispy/visuals/text/_sdf_cpu.cp38-win_amd64.pyd +0 -0
- vispy/visuals/text/_sdf_cpu.pyx +21 -23
- vispy/visuals/text/text.py +9 -3
- vispy/visuals/tube.py +2 -2
- vispy/visuals/visual.py +144 -3
- vispy/visuals/volume.py +300 -131
- {vispy-0.9.5.dist-info → vispy-0.14.0.dist-info}/LICENSE.txt +1 -1
- {vispy-0.9.5.dist-info → vispy-0.14.0.dist-info}/METADATA +218 -198
- {vispy-0.9.5.dist-info → vispy-0.14.0.dist-info}/RECORD +93 -96
- {vispy-0.9.5.dist-info → vispy-0.14.0.dist-info}/WHEEL +1 -1
- vispy/glsl/antialias/__init__.py +0 -0
- vispy/glsl/arrowheads/__init__.py +0 -0
- vispy/glsl/arrows/__init__.py +0 -0
- vispy/glsl/collections/__init__.py +0 -0
- vispy/glsl/colormaps/__init__.py +0 -0
- vispy/glsl/lines/__init__.py +0 -0
- vispy/glsl/markers/__init__.py +0 -0
- vispy/glsl/math/__init__.py +0 -0
- vispy/glsl/misc/__init__.py +0 -0
- vispy/glsl/transforms/__init__.py +0 -0
- {vispy-0.9.5.dist-info → vispy-0.14.0.dist-info}/top_level.txt +0 -0
|
Binary file
|
vispy/io/datasets.py
CHANGED
|
@@ -81,8 +81,8 @@ def load_spatial_filters(packed=True):
|
|
|
81
81
|
Respective interpolation names, plus "Nearest" which does
|
|
82
82
|
not require a filter but can still be used
|
|
83
83
|
"""
|
|
84
|
-
names = ("
|
|
85
|
-
"Kaiser", "Quadric", "
|
|
84
|
+
names = ("Linear", "Hanning", "Hamming", "Hermite",
|
|
85
|
+
"Kaiser", "Quadric", "Cubic", "CatRom",
|
|
86
86
|
"Mitchell", "Spline16", "Spline36", "Gaussian",
|
|
87
87
|
"Bessel", "Sinc", "Lanczos", "Blackman", "Nearest")
|
|
88
88
|
|
vispy/io/image.py
CHANGED
|
@@ -35,7 +35,7 @@ def _make_png(data, level=6):
|
|
|
35
35
|
PNG formatted array
|
|
36
36
|
"""
|
|
37
37
|
# Eventually we might want to use ext/png.py for this, but this
|
|
38
|
-
# routine *should* be faster b/c it's
|
|
38
|
+
# routine *should* be faster b/c it's specialized for our use case
|
|
39
39
|
|
|
40
40
|
def mkchunk(data, name):
|
|
41
41
|
if isinstance(data, np.ndarray):
|
vispy/io/stl.py
CHANGED
|
@@ -33,7 +33,7 @@ def load_stl(file_obj, file_type=None):
|
|
|
33
33
|
file_type: not used
|
|
34
34
|
|
|
35
35
|
Returns
|
|
36
|
-
|
|
36
|
+
-------
|
|
37
37
|
loaded: kwargs for a Trimesh constructor with keys:
|
|
38
38
|
vertices: (n,3) float, vertices
|
|
39
39
|
faces: (m,3) int, indexes of vertices
|
|
@@ -65,7 +65,7 @@ def load_stl_binary(file_obj):
|
|
|
65
65
|
file_obj: open file- like object
|
|
66
66
|
|
|
67
67
|
Returns
|
|
68
|
-
|
|
68
|
+
-------
|
|
69
69
|
loaded: kwargs for a Trimesh constructor with keys:
|
|
70
70
|
vertices: (n,3) float, vertices
|
|
71
71
|
faces: (m,3) int, indexes of vertices
|
|
@@ -124,7 +124,7 @@ def load_stl_ascii(file_obj):
|
|
|
124
124
|
file_obj: open file- like object
|
|
125
125
|
|
|
126
126
|
Returns
|
|
127
|
-
|
|
127
|
+
-------
|
|
128
128
|
loaded: kwargs for a Trimesh constructor with keys:
|
|
129
129
|
vertices: (n,3) float, vertices
|
|
130
130
|
faces: (m,3) int, indexes of vertices
|
|
@@ -133,6 +133,8 @@ class BaseCamera(Node):
|
|
|
133
133
|
viewbox.events.mouse_release.connect(self.viewbox_mouse_event)
|
|
134
134
|
viewbox.events.mouse_move.connect(self.viewbox_mouse_event)
|
|
135
135
|
viewbox.events.mouse_wheel.connect(self.viewbox_mouse_event)
|
|
136
|
+
viewbox.events.gesture_zoom.connect(self.viewbox_mouse_event)
|
|
137
|
+
viewbox.events.gesture_rotate.connect(self.viewbox_mouse_event)
|
|
136
138
|
viewbox.events.resize.connect(self.viewbox_resize_event)
|
|
137
139
|
# todo: also add key events! (and also on viewbox (they're missing)
|
|
138
140
|
|
|
@@ -144,6 +146,8 @@ class BaseCamera(Node):
|
|
|
144
146
|
viewbox.events.mouse_release.disconnect(self.viewbox_mouse_event)
|
|
145
147
|
viewbox.events.mouse_move.disconnect(self.viewbox_mouse_event)
|
|
146
148
|
viewbox.events.mouse_wheel.disconnect(self.viewbox_mouse_event)
|
|
149
|
+
viewbox.events.gesture_zoom.disconnect(self.viewbox_mouse_event)
|
|
150
|
+
viewbox.events.gesture_rotate.disconnect(self.viewbox_mouse_event)
|
|
147
151
|
viewbox.events.resize.disconnect(self.viewbox_resize_event)
|
|
148
152
|
|
|
149
153
|
@property
|
|
@@ -224,8 +228,8 @@ class BaseCamera(Node):
|
|
|
224
228
|
@fov.setter
|
|
225
229
|
def fov(self, fov):
|
|
226
230
|
fov = float(fov)
|
|
227
|
-
if fov < 0 or fov
|
|
228
|
-
raise ValueError("fov must be
|
|
231
|
+
if fov < 0 or fov > 180:
|
|
232
|
+
raise ValueError("fov must be 0 <= fov <= 180.")
|
|
229
233
|
self._fov = fov
|
|
230
234
|
self.view_changed()
|
|
231
235
|
|
vispy/scene/cameras/panzoom.py
CHANGED
|
@@ -132,8 +132,11 @@ class PanZoomCamera(BaseCamera):
|
|
|
132
132
|
|
|
133
133
|
@property
|
|
134
134
|
def rect(self):
|
|
135
|
-
"""The rectangular border of the ViewBox visible area
|
|
136
|
-
|
|
135
|
+
"""The rectangular border of the ViewBox visible area.
|
|
136
|
+
|
|
137
|
+
This is expressed in the coordinate system of the scene.
|
|
138
|
+
See :class:`~vispy.geometry.rect.Rect` for different ways this can
|
|
139
|
+
be specified.
|
|
137
140
|
|
|
138
141
|
Note that the rectangle can have negative width or height, in
|
|
139
142
|
which case the corresponding dimension is flipped (this flipping
|
|
@@ -162,17 +165,7 @@ class PanZoomCamera(BaseCamera):
|
|
|
162
165
|
if not (isinstance(center, (tuple, list)) and len(center) in (2, 3)):
|
|
163
166
|
raise ValueError('center must be a 2 or 3 element tuple')
|
|
164
167
|
rect = Rect(self.rect) or Rect(*DEFAULT_RECT_TUPLE)
|
|
165
|
-
|
|
166
|
-
x2 = rect.center[0]
|
|
167
|
-
y2 = rect.center[1]
|
|
168
|
-
# Apply new ranges
|
|
169
|
-
x1 = center[0]
|
|
170
|
-
y1 = center[1]
|
|
171
|
-
rect.left = x1 - x2
|
|
172
|
-
rect.right = x1 + x2
|
|
173
|
-
rect.bottom = y1 - y2
|
|
174
|
-
rect.top = y1 + y2
|
|
175
|
-
#
|
|
168
|
+
rect.center = center[:2]
|
|
176
169
|
self.rect = rect
|
|
177
170
|
|
|
178
171
|
def _set_range(self, init):
|
|
@@ -214,7 +207,10 @@ class PanZoomCamera(BaseCamera):
|
|
|
214
207
|
center = self._scene_transform.imap(event.pos)
|
|
215
208
|
self.zoom((1 + self.zoom_factor)**(-event.delta[1] * 30), center)
|
|
216
209
|
event.handled = True
|
|
217
|
-
|
|
210
|
+
elif event.type == 'gesture_zoom':
|
|
211
|
+
center = self._scene_transform.imap(event.pos)
|
|
212
|
+
self.zoom(1 - event.scale, center)
|
|
213
|
+
event.handled = True
|
|
218
214
|
elif event.type == 'mouse_move':
|
|
219
215
|
if event.press_event is None:
|
|
220
216
|
return
|
|
@@ -62,6 +62,12 @@ class PerspectiveCamera(BaseCamera):
|
|
|
62
62
|
if self._distance is not None:
|
|
63
63
|
self._distance *= s
|
|
64
64
|
self.view_changed()
|
|
65
|
+
elif event.type == 'gesture_zoom':
|
|
66
|
+
s = 1 - event.scale
|
|
67
|
+
self._scale_factor *= s
|
|
68
|
+
if self._distance is not None:
|
|
69
|
+
self._distance *= s
|
|
70
|
+
self.view_changed()
|
|
65
71
|
|
|
66
72
|
@property
|
|
67
73
|
def scale_factor(self):
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# -----------------------------------------------------------------------------
|
|
3
|
+
# Copyright (c) Vispy Development Team. All Rights Reserved.
|
|
4
|
+
# Distributed under the (new) BSD License. See LICENSE.txt for more info.
|
|
5
|
+
# -----------------------------------------------------------------------------
|
|
6
|
+
import numpy as np
|
|
7
|
+
import pytest
|
|
8
|
+
from vispy.scene.cameras import TurntableCamera
|
|
9
|
+
from vispy.testing import run_tests_if_main
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@pytest.mark.parametrize(
|
|
13
|
+
"elevation, azimuth, roll, expected",
|
|
14
|
+
[
|
|
15
|
+
[0, 0, 0, np.eye(4)],
|
|
16
|
+
[90, 0, 0, [[1, 0, 0, 0], [0, 0, -1, 0], [0, 1, 0, 0], [0, 0, 0, 1]]],
|
|
17
|
+
[0, 90, 0, [[0, 1, 0, 0], [-1, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]],
|
|
18
|
+
[0, 0, 90, [[0, 0, -1, 0], [0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 0, 1]]],
|
|
19
|
+
],
|
|
20
|
+
)
|
|
21
|
+
def test_turntable_camera_transform(elevation, azimuth, roll, expected):
|
|
22
|
+
camera = TurntableCamera(elevation=elevation, azimuth=azimuth, roll=roll)
|
|
23
|
+
matrix = camera._get_rotation_tr()
|
|
24
|
+
np.testing.assert_allclose(matrix, expected, atol=1e-5)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
run_tests_if_main()
|
|
@@ -82,4 +82,41 @@ def test_panzoom_center():
|
|
|
82
82
|
assert v.camera.center == (-12.8, -12.8, 0)
|
|
83
83
|
|
|
84
84
|
|
|
85
|
+
@requires_application()
|
|
86
|
+
def test_panzoom_gesture_zoom():
|
|
87
|
+
with TestingCanvas(size=(120, 200)) as canvas:
|
|
88
|
+
view = canvas.central_widget.add_view()
|
|
89
|
+
imdata = io.load_crate().astype('float32') / 255
|
|
90
|
+
scene.visuals.Image(imdata, parent=view.scene)
|
|
91
|
+
view.camera = scene.PanZoomCamera(aspect=1)
|
|
92
|
+
|
|
93
|
+
assert view.camera.rect.size == (1, 1)
|
|
94
|
+
|
|
95
|
+
canvas.events.touch(
|
|
96
|
+
type="gesture_zoom",
|
|
97
|
+
pos=(60, 100),
|
|
98
|
+
scale=-1.0,
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
assert view.camera.rect.size == (2, 2)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
@requires_application()
|
|
105
|
+
def test_turntable_gesture_zoom():
|
|
106
|
+
with TestingCanvas(size=(120, 200)) as canvas:
|
|
107
|
+
view = canvas.central_widget.add_view()
|
|
108
|
+
imdata = io.load_crate().astype('float32') / 255
|
|
109
|
+
scene.visuals.Image(imdata, parent=view.scene)
|
|
110
|
+
view.camera = scene.TurntableCamera()
|
|
111
|
+
|
|
112
|
+
initial_scale_factor = view.camera.scale_factor
|
|
113
|
+
canvas.events.touch(
|
|
114
|
+
type="gesture_zoom",
|
|
115
|
+
pos=(60, 100),
|
|
116
|
+
scale=-1.0,
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
assert view.camera.scale_factor == 2 * initial_scale_factor
|
|
120
|
+
|
|
121
|
+
|
|
85
122
|
run_tests_if_main()
|
vispy/scene/cameras/turntable.py
CHANGED
|
@@ -18,19 +18,26 @@ class TurntableCamera(Base3DRotationCamera):
|
|
|
18
18
|
the ``center`` indicates the position to put at the center of the
|
|
19
19
|
view.
|
|
20
20
|
|
|
21
|
+
When ``elevation`` and ``azimuth`` are set to 0, the camera
|
|
22
|
+
points along the +y axis.
|
|
23
|
+
|
|
21
24
|
Parameters
|
|
22
25
|
----------
|
|
23
26
|
fov : float
|
|
24
|
-
Field of view.
|
|
27
|
+
Field of view. 0.0 means orthographic projection,
|
|
28
|
+
default is 45.0 (some perspective)
|
|
25
29
|
elevation : float
|
|
26
|
-
Elevation angle in degrees.
|
|
27
|
-
|
|
28
|
-
the
|
|
30
|
+
Elevation angle in degrees. The elevation angle represents a
|
|
31
|
+
rotation of the camera around the current scene x-axis. The
|
|
32
|
+
camera points along the x-y plane when the angle is 0.
|
|
29
33
|
azimuth : float
|
|
30
|
-
Azimuth angle in degrees.
|
|
31
|
-
|
|
34
|
+
Azimuth angle in degrees. The azimuth angle represents a
|
|
35
|
+
rotation of the camera around the scene z-axis according to the
|
|
36
|
+
right-hand screw rule. The camera points along the y-z plane when
|
|
37
|
+
the angle is 0.
|
|
32
38
|
roll : float
|
|
33
|
-
Roll angle in degrees
|
|
39
|
+
Roll angle in degrees. The roll angle represents a rotation of
|
|
40
|
+
the camera around the current scene y-axis.
|
|
34
41
|
distance : float | None
|
|
35
42
|
The distance of the camera from the rotation point (only makes sense
|
|
36
43
|
if fov > 0). If None (default) the distance is determined from the
|
|
@@ -51,24 +58,32 @@ class TurntableCamera(Base3DRotationCamera):
|
|
|
51
58
|
|
|
52
59
|
"""
|
|
53
60
|
|
|
54
|
-
_state_props = Base3DRotationCamera._state_props + (
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
61
|
+
_state_props = Base3DRotationCamera._state_props + ("elevation", "azimuth", "roll")
|
|
62
|
+
|
|
63
|
+
def __init__(
|
|
64
|
+
self,
|
|
65
|
+
fov=45.0,
|
|
66
|
+
elevation=30.0,
|
|
67
|
+
azimuth=30.0,
|
|
68
|
+
roll=0.0,
|
|
69
|
+
distance=None,
|
|
70
|
+
translate_speed=1.0,
|
|
71
|
+
**kwargs
|
|
72
|
+
):
|
|
59
73
|
super(TurntableCamera, self).__init__(fov=fov, **kwargs)
|
|
60
74
|
|
|
61
75
|
# Set camera attributes
|
|
62
76
|
self.azimuth = azimuth
|
|
63
77
|
self.elevation = elevation
|
|
64
|
-
self.roll = roll
|
|
78
|
+
self.roll = roll
|
|
65
79
|
self.distance = distance # None means auto-distance
|
|
66
80
|
self.translate_speed = translate_speed
|
|
67
81
|
|
|
68
82
|
@property
|
|
69
83
|
def elevation(self):
|
|
70
|
-
"""
|
|
71
|
-
|
|
84
|
+
"""Get the camera elevation angle in degrees.
|
|
85
|
+
|
|
86
|
+
The camera points along the x-y plane when the angle is 0.
|
|
72
87
|
"""
|
|
73
88
|
return self._elevation
|
|
74
89
|
|
|
@@ -80,8 +95,9 @@ class TurntableCamera(Base3DRotationCamera):
|
|
|
80
95
|
|
|
81
96
|
@property
|
|
82
97
|
def azimuth(self):
|
|
83
|
-
"""
|
|
84
|
-
|
|
98
|
+
"""Get the camera azimuth angle in degrees.
|
|
99
|
+
|
|
100
|
+
The camera points along the y-z plane when the angle is 0.
|
|
85
101
|
"""
|
|
86
102
|
return self._azimuth
|
|
87
103
|
|
|
@@ -97,9 +113,7 @@ class TurntableCamera(Base3DRotationCamera):
|
|
|
97
113
|
|
|
98
114
|
@property
|
|
99
115
|
def roll(self):
|
|
100
|
-
"""
|
|
101
|
-
0 places puts the camera upright.
|
|
102
|
-
"""
|
|
116
|
+
"""Get the camera roll angle in degrees."""
|
|
103
117
|
return self._roll
|
|
104
118
|
|
|
105
119
|
@roll.setter
|
|
@@ -138,10 +152,12 @@ class TurntableCamera(Base3DRotationCamera):
|
|
|
138
152
|
def _get_rotation_tr(self):
|
|
139
153
|
"""Return a rotation matrix based on camera parameters"""
|
|
140
154
|
up, forward, right = self._get_dim_vectors()
|
|
141
|
-
|
|
142
|
-
transforms.rotate(self.elevation, -right)
|
|
143
|
-
transforms.rotate(self.azimuth, up)
|
|
155
|
+
matrix = (
|
|
156
|
+
transforms.rotate(self.elevation, -right)
|
|
157
|
+
.dot(transforms.rotate(self.azimuth, up))
|
|
158
|
+
.dot(transforms.rotate(self.roll, forward))
|
|
144
159
|
)
|
|
160
|
+
return matrix
|
|
145
161
|
|
|
146
162
|
def _dist_to_trans(self, dist):
|
|
147
163
|
"""Convert mouse x, y movement into x, y, z translations"""
|
vispy/scene/canvas.py
CHANGED
|
@@ -140,6 +140,7 @@ class SceneCanvas(app.Canvas, Frozen):
|
|
|
140
140
|
self.events.mouse_move.connect(self._process_mouse_event)
|
|
141
141
|
self.events.mouse_release.connect(self._process_mouse_event)
|
|
142
142
|
self.events.mouse_wheel.connect(self._process_mouse_event)
|
|
143
|
+
self.events.touch.connect(self._process_mouse_event)
|
|
143
144
|
|
|
144
145
|
self.scene = SubScene()
|
|
145
146
|
self.freeze()
|
|
@@ -344,7 +345,12 @@ class SceneCanvas(app.Canvas, Frozen):
|
|
|
344
345
|
|
|
345
346
|
def _process_mouse_event(self, event):
|
|
346
347
|
prof = Profiler() # noqa
|
|
347
|
-
deliver_types = [
|
|
348
|
+
deliver_types = [
|
|
349
|
+
'mouse_press',
|
|
350
|
+
'mouse_wheel',
|
|
351
|
+
'gesture_zoom',
|
|
352
|
+
'gesture_rotate',
|
|
353
|
+
]
|
|
348
354
|
if self._send_hover_events:
|
|
349
355
|
deliver_types += ['mouse_move']
|
|
350
356
|
|
|
@@ -487,11 +493,8 @@ class SceneCanvas(app.Canvas, Frozen):
|
|
|
487
493
|
than triggering transform updates across the scene with every
|
|
488
494
|
click.
|
|
489
495
|
"""
|
|
490
|
-
|
|
491
|
-
self._scene.picking = True
|
|
496
|
+
with self._scene.set_picking():
|
|
492
497
|
img = self.render(bgcolor=(0, 0, 0, 0), crop=crop)
|
|
493
|
-
finally:
|
|
494
|
-
self._scene.picking = False
|
|
495
498
|
img = img.astype('int32') * [2**0, 2**8, 2**16, 2**24]
|
|
496
499
|
id_ = img.sum(axis=2).astype('int32')
|
|
497
500
|
return id_
|
|
@@ -524,6 +527,7 @@ class SceneCanvas(app.Canvas, Frozen):
|
|
|
524
527
|
self.events.mouse_move.disconnect(self._process_mouse_event)
|
|
525
528
|
self.events.mouse_release.disconnect(self._process_mouse_event)
|
|
526
529
|
self.events.mouse_wheel.disconnect(self._process_mouse_event)
|
|
530
|
+
self.events.touch.disconnect(self._process_mouse_event)
|
|
527
531
|
|
|
528
532
|
# -------------------------------------------------- transform handling ---
|
|
529
533
|
def push_viewport(self, viewport):
|
vispy/scene/events.py
CHANGED
|
@@ -71,6 +71,15 @@ class SceneMouseEvent(Event):
|
|
|
71
71
|
"""The increment by which the mouse wheel has moved."""
|
|
72
72
|
return self.mouse_event.delta
|
|
73
73
|
|
|
74
|
+
@property
|
|
75
|
+
def scale(self):
|
|
76
|
+
"""The scale of a gesture_zoom event"""
|
|
77
|
+
try:
|
|
78
|
+
return self.mouse_event.scale
|
|
79
|
+
except AttributeError:
|
|
80
|
+
errmsg = f"SceneMouseEvent type '{self.type}' has no scale"
|
|
81
|
+
raise TypeError(errmsg)
|
|
82
|
+
|
|
74
83
|
def copy(self):
|
|
75
84
|
ev = self.__class__(self.mouse_event, self.visual)
|
|
76
85
|
return ev
|
vispy/scene/node.py
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
from __future__ import division
|
|
6
6
|
|
|
7
7
|
import weakref
|
|
8
|
+
from contextlib import contextmanager
|
|
8
9
|
|
|
9
10
|
from ..util.event import Event, EmitterGroup
|
|
10
11
|
from ..visuals.transforms import (NullTransform, BaseTransform,
|
|
@@ -63,7 +64,8 @@ class Node(object):
|
|
|
63
64
|
# Add some events to the emitter groups:
|
|
64
65
|
events = ['canvas_change', 'parent_change', 'children_change',
|
|
65
66
|
'transform_change', 'mouse_press', 'mouse_move',
|
|
66
|
-
'mouse_release', 'mouse_wheel', 'key_press', 'key_release'
|
|
67
|
+
'mouse_release', 'mouse_wheel', 'key_press', 'key_release',
|
|
68
|
+
'gesture_zoom', 'gesture_rotate']
|
|
67
69
|
# Create event emitter if needed (in subclasses that inherit from
|
|
68
70
|
# Visual, we already have an emitter to share)
|
|
69
71
|
if not hasattr(self, 'events'):
|
|
@@ -423,7 +425,7 @@ class Node(object):
|
|
|
423
425
|
If true, add information about node transform types.
|
|
424
426
|
|
|
425
427
|
Returns
|
|
426
|
-
|
|
428
|
+
-------
|
|
427
429
|
tree : str
|
|
428
430
|
The tree diagram.
|
|
429
431
|
"""
|
|
@@ -625,3 +627,18 @@ class Node(object):
|
|
|
625
627
|
for c in self.children:
|
|
626
628
|
c.picking = p
|
|
627
629
|
self._picking = p
|
|
630
|
+
|
|
631
|
+
@contextmanager
|
|
632
|
+
def set_picking(self, *, picking=True):
|
|
633
|
+
"""Context manager to temporarily set picking for this node and its children.
|
|
634
|
+
|
|
635
|
+
Note that this function will not alter the picking mode unless/until
|
|
636
|
+
the context manager is entered (using the `with` statement). Use
|
|
637
|
+
:py:attr:`~.picking` for setting the picking mode directly.
|
|
638
|
+
"""
|
|
639
|
+
old_picking = self.picking
|
|
640
|
+
try:
|
|
641
|
+
self.picking = picking
|
|
642
|
+
yield self.picking
|
|
643
|
+
finally:
|
|
644
|
+
self.picking = old_picking
|
vispy/scene/tests/test_canvas.py
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# Copyright (c) Vispy Development Team. All Rights Reserved.
|
|
3
3
|
# Distributed under the (new) BSD License. See LICENSE.txt for more info.
|
|
4
4
|
|
|
5
|
-
from vispy import scene
|
|
5
|
+
from vispy import gloo, scene
|
|
6
6
|
from vispy.testing import requires_application, TestingCanvas
|
|
7
7
|
from vispy.visuals.transforms import STTransform
|
|
8
8
|
|
|
@@ -88,3 +88,32 @@ def test_picking_basic():
|
|
|
88
88
|
assert len(picked_visuals) == 2
|
|
89
89
|
assert any(isinstance(vis, scene.ViewBox) for vis in picked_visuals)
|
|
90
90
|
assert any(isinstance(vis, scene.Line) for vis in picked_visuals)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@requires_application()
|
|
94
|
+
@pytest.mark.parametrize(
|
|
95
|
+
'preset',
|
|
96
|
+
[
|
|
97
|
+
'opaque', 'additive', 'translucent',
|
|
98
|
+
])
|
|
99
|
+
def test_blend_presets(preset):
|
|
100
|
+
"""Test blending presets render a canvas to an array.
|
|
101
|
+
|
|
102
|
+
Different blending presets are used to test that they properly set
|
|
103
|
+
blend equations.
|
|
104
|
+
|
|
105
|
+
"""
|
|
106
|
+
with TestingCanvas(size=(125, 125), show=True, title='run') as c:
|
|
107
|
+
view = c.central_widget.add_view()
|
|
108
|
+
im1 = np.zeros((100, 100, 4)).astype(np.float32)
|
|
109
|
+
im1[:, :, 1] = 1
|
|
110
|
+
im1[:, :, 3] = .4
|
|
111
|
+
# Create the image
|
|
112
|
+
image1 = scene.visuals.Image(im1, parent=view.scene)
|
|
113
|
+
image1.transform = STTransform(translate=(20, 20, -1))
|
|
114
|
+
|
|
115
|
+
gloo.set_state(blend_equation='min')
|
|
116
|
+
image1.set_gl_state(preset)
|
|
117
|
+
|
|
118
|
+
rgba_result = c.render()
|
|
119
|
+
assert not np.allclose(rgba_result[..., :3], 0)
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
1
3
|
from vispy.scene import visuals, Node
|
|
2
4
|
from vispy.scene.visuals import VisualNode
|
|
3
5
|
import vispy.visuals
|
|
@@ -26,3 +28,114 @@ def test_visual_node_generation():
|
|
|
26
28
|
vis_node = getattr(visuals, name[:-6])
|
|
27
29
|
assert issubclass(vis_node, Node)
|
|
28
30
|
assert issubclass(vis_node, obj)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_push_gl_state():
|
|
34
|
+
node = vispy.visuals.MeshVisual()
|
|
35
|
+
og_gl_state = node._vshare.gl_state.copy()
|
|
36
|
+
node.push_gl_state(blend=False, depth_test=False)
|
|
37
|
+
assert node._vshare.gl_state != og_gl_state
|
|
38
|
+
# preset is always set, unset kwargs should be absent
|
|
39
|
+
assert node._vshare.gl_state == {
|
|
40
|
+
"preset": None,
|
|
41
|
+
"blend": False,
|
|
42
|
+
"depth_test": False,
|
|
43
|
+
}
|
|
44
|
+
node.pop_gl_state()
|
|
45
|
+
assert node._vshare.gl_state == og_gl_state
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def test_push_gl_state_update():
|
|
49
|
+
node = vispy.visuals.MeshVisual()
|
|
50
|
+
og_gl_state = node._vshare.gl_state.copy()
|
|
51
|
+
assert "blend" not in og_gl_state
|
|
52
|
+
assert node._vshare.gl_state["depth_test"]
|
|
53
|
+
|
|
54
|
+
node.push_gl_state_update(blend=False, depth_test=False)
|
|
55
|
+
assert node._vshare.gl_state != og_gl_state
|
|
56
|
+
assert not node._vshare.gl_state["blend"]
|
|
57
|
+
assert not node._vshare.gl_state["depth_test"]
|
|
58
|
+
|
|
59
|
+
node.pop_gl_state()
|
|
60
|
+
assert node._vshare.gl_state == og_gl_state
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def test_pop_empty_gl_state():
|
|
64
|
+
node = vispy.visuals.MeshVisual()
|
|
65
|
+
assert node._prev_gl_state == []
|
|
66
|
+
og_gl_state = node._vshare.gl_state.copy()
|
|
67
|
+
node.pop_gl_state()
|
|
68
|
+
assert node._vshare.gl_state == og_gl_state
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def test_update_gl_state():
|
|
72
|
+
node = vispy.visuals.MeshVisual()
|
|
73
|
+
|
|
74
|
+
og_gl_state = node._vshare.gl_state.copy()
|
|
75
|
+
assert og_gl_state
|
|
76
|
+
og_gl_state["blend"] = False
|
|
77
|
+
|
|
78
|
+
node.update_gl_state(blend=True)
|
|
79
|
+
|
|
80
|
+
# check that the state was updated
|
|
81
|
+
assert node._vshare.gl_state.pop("blend") != og_gl_state.pop("blend")
|
|
82
|
+
# the rest of the state should be unchanged
|
|
83
|
+
assert node._vshare.gl_state == og_gl_state
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def test_update_gl_state_context_manager():
|
|
87
|
+
node = vispy.visuals.MeshVisual()
|
|
88
|
+
|
|
89
|
+
node.set_gl_state(blend=False)
|
|
90
|
+
og_gl_state = node._vshare.gl_state.copy()
|
|
91
|
+
|
|
92
|
+
with node.update_gl_state(blend=True):
|
|
93
|
+
# check that the state was updated
|
|
94
|
+
assert node._vshare.gl_state == {**og_gl_state, "blend": True}
|
|
95
|
+
|
|
96
|
+
# the update should be reverted once out of the context
|
|
97
|
+
assert node._vshare.gl_state == og_gl_state
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def test_set_gl_state():
|
|
101
|
+
node = vispy.visuals.MeshVisual()
|
|
102
|
+
|
|
103
|
+
node.set_gl_state(blend=False, depth_test=False)
|
|
104
|
+
# preset is always set, unset kwargs should be absent
|
|
105
|
+
assert node._vshare.gl_state == {
|
|
106
|
+
"preset": None,
|
|
107
|
+
"blend": False,
|
|
108
|
+
"depth_test": False,
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
node.set_gl_state(blend=False)
|
|
112
|
+
# preset is always set, unset kwargs should be absent
|
|
113
|
+
assert node._vshare.gl_state == {"preset": None, "blend": False}
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def test_set_gl_state_context_manager():
|
|
117
|
+
node = vispy.visuals.MeshVisual()
|
|
118
|
+
|
|
119
|
+
node.set_gl_state(blend=False)
|
|
120
|
+
og_gl_state = node._vshare.gl_state.copy()
|
|
121
|
+
|
|
122
|
+
with node.set_gl_state(blend=True):
|
|
123
|
+
# preset is always set, unset kwargs should be absent
|
|
124
|
+
assert node._vshare.gl_state == {"preset": None, "blend": True}
|
|
125
|
+
|
|
126
|
+
# the update should be reverted once out of the context
|
|
127
|
+
assert node._vshare.gl_state == og_gl_state
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
@pytest.mark.parametrize("enable_picking", [True, False])
|
|
131
|
+
def test_picking_context(enable_picking):
|
|
132
|
+
mesh = visuals.Mesh()
|
|
133
|
+
mesh.picking = not enable_picking
|
|
134
|
+
|
|
135
|
+
assert mesh.picking != enable_picking
|
|
136
|
+
|
|
137
|
+
with mesh.set_picking(picking=enable_picking) as p:
|
|
138
|
+
assert p == enable_picking
|
|
139
|
+
assert mesh.picking == enable_picking
|
|
140
|
+
|
|
141
|
+
assert mesh.picking != enable_picking
|
vispy/scene/visuals.py
CHANGED
|
@@ -71,7 +71,10 @@ class VisualNode(Node):
|
|
|
71
71
|
return
|
|
72
72
|
self._picking = p
|
|
73
73
|
self._picking_filter.enabled = p
|
|
74
|
-
|
|
74
|
+
if p:
|
|
75
|
+
self.push_gl_state_update(blend=False)
|
|
76
|
+
else:
|
|
77
|
+
self.pop_gl_state()
|
|
75
78
|
|
|
76
79
|
def _update_trsys(self, event):
|
|
77
80
|
"""Transform object(s) have changed for this Node; assign these to the
|
|
@@ -242,7 +245,9 @@ GridLines = create_visual_node(visuals.GridLinesVisual)
|
|
|
242
245
|
GridMesh = create_visual_node(visuals.GridMeshVisual)
|
|
243
246
|
Histogram = create_visual_node(visuals.HistogramVisual)
|
|
244
247
|
Image = create_visual_node(visuals.ImageVisual)
|
|
248
|
+
ComplexImage = create_visual_node(visuals.ComplexImageVisual)
|
|
245
249
|
InfiniteLine = create_visual_node(visuals.InfiniteLineVisual)
|
|
250
|
+
InstancedMesh = create_visual_node(visuals.InstancedMeshVisual)
|
|
246
251
|
Isocurve = create_visual_node(visuals.IsocurveVisual)
|
|
247
252
|
Isoline = create_visual_node(visuals.IsolineVisual)
|
|
248
253
|
Isosurface = create_visual_node(visuals.IsosurfaceVisual)
|
vispy/scene/widgets/viewbox.py
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
# Distributed under the (new) BSD License. See LICENSE.txt for more info.
|
|
4
4
|
|
|
5
5
|
from __future__ import division
|
|
6
|
+
from typing import Union
|
|
6
7
|
|
|
7
8
|
import numpy as np
|
|
8
9
|
|
|
@@ -67,7 +68,7 @@ class ViewBox(Widget):
|
|
|
67
68
|
raise TypeError('Argument "camera" must be None, str, or Camera.')
|
|
68
69
|
|
|
69
70
|
@property
|
|
70
|
-
def camera(self):
|
|
71
|
+
def camera(self) -> BaseCamera:
|
|
71
72
|
"""Get/set the Camera in use by this ViewBox
|
|
72
73
|
|
|
73
74
|
If a string is given (e.g. 'panzoom', 'turntable', 'fly'). A
|
|
@@ -84,7 +85,7 @@ class ViewBox(Widget):
|
|
|
84
85
|
return self._camera
|
|
85
86
|
|
|
86
87
|
@camera.setter
|
|
87
|
-
def camera(self, cam):
|
|
88
|
+
def camera(self, cam: Union[str, BaseCamera]):
|
|
88
89
|
if isinstance(cam, str):
|
|
89
90
|
# Try to select an existing camera
|
|
90
91
|
for child in self.scene.children:
|
vispy/testing/_runners.py
CHANGED
|
@@ -219,24 +219,18 @@ try:
|
|
|
219
219
|
faulthandler.enable()
|
|
220
220
|
except Exception:
|
|
221
221
|
pass
|
|
222
|
+
from vispy.util.gallery_scraper import get_canvaslike_from_globals, FrameGrabber
|
|
222
223
|
os.environ['VISPY_IGNORE_OLD_VERSION'] = 'true'
|
|
224
|
+
os.environ['_VISPY_RUNNING_GALLERY_EXAMPLES'] = 'true'
|
|
223
225
|
import {0}
|
|
224
226
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
elif hasattr({0}, 'Canvas'):
|
|
228
|
-
canvas = {0}.Canvas()
|
|
229
|
-
elif hasattr({0}, 'fig'):
|
|
230
|
-
canvas = {0}.fig
|
|
231
|
-
else:
|
|
227
|
+
canvas_or_widget = get_canvaslike_from_globals({0}.__dict__)
|
|
228
|
+
if canvas_or_widget is None:
|
|
232
229
|
raise RuntimeError('Bad example formatting: fix or add `# vispy: testskip`'
|
|
233
230
|
' to the top of the file.')
|
|
234
231
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
c.update()
|
|
238
|
-
c.app.process_events()
|
|
239
|
-
time.sleep(1./60.)
|
|
232
|
+
frame_grabber = FrameGrabber(canvas_or_widget, [1, 2, 3, 4, 5])
|
|
233
|
+
frame_grabber.collect_frames()
|
|
240
234
|
"""
|
|
241
235
|
|
|
242
236
|
bad_examples = []
|