vispy 0.13.0__cp310-cp310-macosx_10_9_x86_64.whl → 0.14.0__cp310-cp310-macosx_10_9_x86_64.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.

@@ -250,7 +250,7 @@ class CanvasBackend(BaseCanvasBackend):
250
250
  raise ValueError('fullscreen must be <= %s'
251
251
  % len(monitor))
252
252
  monitor = monitor[p.fullscreen]
253
- use_size = glfw.get_video_mode(monitor)[:2]
253
+ use_size = glfw.get_video_mode(monitor)[0][:2]
254
254
  if use_size != tuple(p.size):
255
255
  logger.debug('Requested size %s, will be ignored to '
256
256
  'use fullscreen mode %s' % (p.size, use_size))
vispy/color/colormap.py CHANGED
@@ -1092,17 +1092,13 @@ _colormaps = dict(
1092
1092
  )
1093
1093
 
1094
1094
 
1095
- def get_colormap(name, *args, **kwargs):
1096
- """Obtain a colormap.
1095
+ def get_colormap(name):
1096
+ """Obtain a colormap by name.
1097
1097
 
1098
1098
  Parameters
1099
1099
  ----------
1100
1100
  name : str | Colormap
1101
1101
  Colormap name. Can also be a Colormap for pass-through.
1102
- *args:
1103
- Deprecated.
1104
- **kwargs
1105
- Deprecated.
1106
1102
 
1107
1103
  Examples
1108
1104
  --------
@@ -1111,18 +1107,10 @@ def get_colormap(name, *args, **kwargs):
1111
1107
 
1112
1108
  .. versionchanged: 0.7
1113
1109
 
1114
- Additional args/kwargs are no longer accepted. Colormap classes are
1115
- no longer created on the fly. To create a ``cubehelix``
1116
- (``CubeHelixColormap``), ``single_hue`` (``SingleHue``), ``hsl``
1117
- (``HSL``), ``husl`` (``HSLuv``), ``diverging`` (``Diverging``), or
1118
- ``RdYeBuCy`` (``RedYellowBlueCyan``) colormap you must import and
1119
- instantiate it directly from the ``vispy.color.colormap`` module.
1110
+ Additional args/kwargs are no longer accepted. Colormap instances are
1111
+ no longer created on the fly.
1120
1112
 
1121
1113
  """
1122
- if args or kwargs:
1123
- warnings.warn("Creating a Colormap instance with 'get_colormap' is "
1124
- "no longer supported. No additional arguments or "
1125
- "keyword arguments should be passed.", DeprecationWarning)
1126
1114
  if isinstance(name, BaseColormap):
1127
1115
  return name
1128
1116
 
@@ -1130,14 +1118,6 @@ def get_colormap(name, *args, **kwargs):
1130
1118
  raise TypeError('colormap must be a Colormap or string name')
1131
1119
  if name in _colormaps: # vispy cmap
1132
1120
  cmap = _colormaps[name]
1133
- if name in ("cubehelix", "single_hue", "hsl", "husl", "diverging", "RdYeBuCy"):
1134
- warnings.warn(
1135
- f"Colormap '{name}' has been deprecated since vispy 0.7. "
1136
- f"Please import and create 'vispy.color.colormap.{cmap.__class__.__name__}' "
1137
- "directly instead.",
1138
- DeprecationWarning,
1139
- stacklevel=2,
1140
- )
1141
1121
 
1142
1122
  elif has_matplotlib(): # matplotlib cmap
1143
1123
  try:
vispy/gloo/texture.py CHANGED
@@ -33,7 +33,9 @@ def convert_dtype_and_clip(data, dtype, copy=False):
33
33
  else:
34
34
  # to reduce copying, we clip into a pre-generated array of the right dtype
35
35
  new_data = np.empty_like(data, dtype=dtype)
36
- np.clip(data, new_min, new_max, out=new_data)
36
+ # allow "unsafe" casting here as we're explicitly clipping to the
37
+ # range of the new dtype - this was a default before numpy 1.25
38
+ np.clip(data, new_min, new_max, out=new_data, casting="unsafe")
37
39
  return new_data
38
40
 
39
41
 
vispy/scene/canvas.py CHANGED
@@ -493,11 +493,8 @@ class SceneCanvas(app.Canvas, Frozen):
493
493
  than triggering transform updates across the scene with every
494
494
  click.
495
495
  """
496
- try:
497
- self._scene.picking = True
496
+ with self._scene.set_picking():
498
497
  img = self.render(bgcolor=(0, 0, 0, 0), crop=crop)
499
- finally:
500
- self._scene.picking = False
501
498
  img = img.astype('int32') * [2**0, 2**8, 2**16, 2**24]
502
499
  id_ = img.sum(axis=2).astype('int32')
503
500
  return id_
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,
@@ -626,3 +627,18 @@ class Node(object):
626
627
  for c in self.children:
627
628
  c.picking = p
628
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
@@ -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
- self.update_gl_state(blend=not p)
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
vispy/version.py CHANGED
@@ -1,4 +1,4 @@
1
1
  # file generated by setuptools_scm
2
2
  # don't change, don't track in version control
3
- __version__ = version = '0.13.0'
4
- __version_tuple__ = version_tuple = (0, 13, 0)
3
+ __version__ = version = '0.14.0'
4
+ __version_tuple__ = version_tuple = (0, 14, 0)
@@ -2,8 +2,9 @@
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 .base_filter import Filter # noqa
5
+ from .base_filter import Filter, PrimitivePickingFilter # noqa
6
6
  from .clipper import Clipper # noqa
7
7
  from .color import Alpha, ColorFilter, IsolineFilter, ZColormapFilter # noqa
8
8
  from .picking import PickingFilter # noqa
9
- from .mesh import TextureFilter, ShadingFilter, InstancedShadingFilter, WireframeFilter # noqa
9
+ from .markers import MarkerPickingFilter # noqa
10
+ from .mesh import TextureFilter, ShadingFilter, InstancedShadingFilter, WireframeFilter, FacePickingFilter # noqa
@@ -2,6 +2,11 @@
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 abc import ABCMeta, abstractmethod
6
+
7
+ import numpy as np
8
+
9
+ from vispy.gloo import VertexBuffer
5
10
  from ..shaders import Function
6
11
 
7
12
 
@@ -120,3 +125,118 @@ class Filter(BaseFilter):
120
125
 
121
126
  self._attached = False
122
127
  self._visual = None
128
+
129
+
130
+ class PrimitivePickingFilter(Filter, metaclass=ABCMeta):
131
+ """Abstract base class for Visual-specific filters to implement a
132
+ primitive-picking mode.
133
+
134
+ Subclasses must (and usually only need to) implement
135
+ :py:meth:`_get_picking_ids`.
136
+ """
137
+
138
+ def __init__(self, fpos=9, *, discard_transparent=False):
139
+ # fpos is set to 9 by default to put it near the end, but before the
140
+ # default PickingFilter
141
+ vfunc = Function("""\
142
+ varying vec4 v_marker_picking_color;
143
+ void prepare_marker_picking() {
144
+ v_marker_picking_color = $ids;
145
+ }
146
+ """)
147
+ ffunc = Function("""\
148
+ varying vec4 v_marker_picking_color;
149
+ void marker_picking_filter() {
150
+ if ( $enabled != 1 ) {
151
+ return;
152
+ }
153
+ if ( $discard_transparent == 1 && gl_FragColor.a == 0.0 ) {
154
+ discard;
155
+ }
156
+ gl_FragColor = v_marker_picking_color;
157
+ }
158
+ """)
159
+
160
+ self._id_colors = VertexBuffer(np.zeros((0, 4), dtype=np.float32))
161
+ vfunc['ids'] = self._id_colors
162
+ self._n_primitives = 0
163
+ super().__init__(vcode=vfunc, fcode=ffunc, fpos=fpos)
164
+ self.enabled = False
165
+ self.discard_transparent = discard_transparent
166
+
167
+ @abstractmethod
168
+ def _get_picking_ids(self):
169
+ """Return a 1D array of picking IDs for the vertices in the visual.
170
+
171
+ Generally, this method should be implemented to:
172
+ 1. Calculate the number of primitives in the visual (may be
173
+ persisted in `self._n_primitives`).
174
+ 2. Calculate a range of picking ids for each primitive in the
175
+ visual. IDs should start from 1, reserving 0 for the background. If
176
+ primitives comprise multiple vertices (triangles), ids may need to
177
+ be repeated.
178
+
179
+ The return value should be an array of uint32 with shape
180
+ (num_vertices,).
181
+
182
+ If no change to the picking IDs is needed (for example, the number of
183
+ primitives has not changed), this method should return `None`.
184
+ """
185
+ raise NotImplementedError(self)
186
+
187
+ def _update_id_colors(self):
188
+ """Calculate the colors encoding the picking IDs for the visual.
189
+
190
+ For performance, this method will not update the id colors VertexBuffer
191
+ if :py:meth:`_get_picking_ids` returns `None`.
192
+ """
193
+ # this should remain untouched
194
+ ids = self._get_picking_ids()
195
+ if ids is not None:
196
+ id_colors = self._pack_ids_into_rgba(ids)
197
+ self._id_colors.set_data(id_colors)
198
+
199
+ @staticmethod
200
+ def _pack_ids_into_rgba(ids):
201
+ """Pack an array of uint32 primitive ids into float32 RGBA colors."""
202
+ if ids.dtype != np.uint32:
203
+ raise ValueError(f"ids must be uint32, got {ids.dtype}")
204
+
205
+ return np.divide(
206
+ ids.view(np.uint8).reshape(-1, 4),
207
+ 255,
208
+ dtype=np.float32
209
+ )
210
+
211
+ def _on_data_updated(self, event=None):
212
+ if not self.attached:
213
+ return
214
+ self._update_id_colors()
215
+
216
+ @property
217
+ def enabled(self):
218
+ return self._enabled
219
+
220
+ @enabled.setter
221
+ def enabled(self, e):
222
+ self._enabled = bool(e)
223
+ self.fshader['enabled'] = int(self._enabled)
224
+ self._on_data_updated()
225
+
226
+ @property
227
+ def discard_transparent(self):
228
+ return self._discard_transparent
229
+
230
+ @discard_transparent.setter
231
+ def discard_transparent(self, d):
232
+ self._discard_transparent = bool(d)
233
+ self.fshader['discard_transparent'] = int(self._discard_transparent)
234
+
235
+ def _attach(self, visual):
236
+ super()._attach(visual)
237
+ visual.events.data_updated.connect(self._on_data_updated)
238
+ self._on_data_updated()
239
+
240
+ def _detach(self, visual):
241
+ visual.events.data_updated.disconnect(self._on_data_updated)
242
+ super()._detach(visual)
@@ -0,0 +1,28 @@
1
+ import numpy as np
2
+
3
+ from vispy.visuals.filters import PrimitivePickingFilter
4
+
5
+
6
+ class MarkerPickingFilter(PrimitivePickingFilter):
7
+ """Filter used to color markers by a picking ID.
8
+
9
+ Note that the ID color uses the alpha channel, so this may not be used
10
+ with blending enabled.
11
+
12
+ Examples
13
+ --------
14
+ :ref:`sphx_glr_gallery_scene_marker_picking.py`
15
+ """
16
+
17
+ def _get_picking_ids(self):
18
+ if self._visual._data is None:
19
+ n_markers = 0
20
+ else:
21
+ n_markers = len(self._visual._data['a_position'])
22
+
23
+ # we only care about the number of markers changing
24
+ if self._n_primitives == n_markers:
25
+ return
26
+ self._n_primitives = n_markers
27
+
28
+ return np.arange(1, n_markers + 1, dtype=np.uint32)
@@ -7,7 +7,7 @@ import numpy as np
7
7
 
8
8
  from vispy.gloo import Texture2D, VertexBuffer
9
9
  from vispy.visuals.shaders import Function, Varying
10
- from vispy.visuals.filters import Filter
10
+ from vispy.visuals.filters import Filter, PrimitivePickingFilter
11
11
  from ...color import Color
12
12
 
13
13
 
@@ -177,6 +177,7 @@ void shade() {
177
177
  vec3 u = dFdx(v_pos_scene.xyz);
178
178
  vec3 v = dFdy(v_pos_scene.xyz);
179
179
  normal = cross(u, v);
180
+ } else {
180
181
  // Note(asnt): The normal calculated above always points in the
181
182
  // direction of the camera. Reintroduce the original orientation of the
182
183
  // face.
@@ -756,13 +757,40 @@ class WireframeFilter(Filter):
756
757
  bc = np.tile(bc[None, ...], (n_faces, 1, 1))
757
758
  self._bc.set_data(bc, convert=True)
758
759
 
759
- def on_mesh_data_updated(self, event):
760
+ def on_data_updated(self, event):
760
761
  self._update_data()
761
762
 
762
763
  def _attach(self, visual):
763
764
  super()._attach(visual)
764
- visual.events.data_updated.connect(self.on_mesh_data_updated)
765
+ visual.events.data_updated.connect(self.on_data_updated)
765
766
 
766
767
  def _detach(self, visual):
767
- visual.events.data_updated.disconnect(self.on_mesh_data_updated)
768
+ visual.events.data_updated.disconnect(self.on_data_updated)
768
769
  super()._detach(visual)
770
+
771
+
772
+ class FacePickingFilter(PrimitivePickingFilter):
773
+ """Filter used to color mesh faces by a picking ID.
774
+
775
+ Note that the ID color uses the alpha channel, so this may not be used
776
+ with blending enabled.
777
+
778
+ Examples
779
+ --------
780
+ :ref:`sphx_glr_gallery_scene_face_picking.py`
781
+ """
782
+
783
+ def _get_picking_ids(self):
784
+ if self._visual.mesh_data.is_empty():
785
+ n_faces = 0
786
+ else:
787
+ n_faces = len(self._visual.mesh_data.get_faces())
788
+
789
+ # we only care about the number of faces changing
790
+ if self._n_primitives == n_faces:
791
+ return None
792
+ self._n_primitives = n_faces
793
+
794
+ ids = np.arange(1, n_faces + 1, dtype=np.uint32)
795
+ ids = np.repeat(ids, 3, axis=0) # repeat id for each vertex
796
+ return ids
@@ -0,0 +1,70 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright (c) Vispy Development Team. All Rights Reserved.
3
+ # Distributed under the (new) BSD License. See LICENSE.txt for more info.
4
+ import numpy as np
5
+
6
+ from vispy.geometry import create_plane
7
+ from vispy.scene.visuals import Markers, Mesh
8
+ from vispy.testing import requires_application, TestingCanvas
9
+ from vispy.visuals.filters import FacePickingFilter, MarkerPickingFilter
10
+
11
+
12
+ def test_empty_mesh_face_picking():
13
+ mesh = Mesh()
14
+ filter = FacePickingFilter()
15
+ mesh.attach(filter)
16
+ filter.enabled = True
17
+
18
+
19
+ @requires_application()
20
+ def test_mesh_face_picking():
21
+ vertices, faces, _ = create_plane(125, 125)
22
+ vertices = vertices["position"]
23
+ vertices[:, :2] += 125 / 2
24
+ mesh = Mesh(vertices=vertices, faces=faces)
25
+ filter = FacePickingFilter()
26
+ mesh.attach(filter)
27
+
28
+ with TestingCanvas(size=(125, 125)) as c:
29
+ view = c.central_widget.add_view()
30
+ view.add(mesh)
31
+ filter.enabled = True
32
+ mesh.update_gl_state(blend=False)
33
+ picking_render = c.render(bgcolor=(0, 0, 0, 0), alpha=True)
34
+
35
+ # unpack the IDs
36
+ ids = picking_render.view(np.uint32)
37
+ # the plane is made up of two triangles and nearly fills the view
38
+ # pick one point on each triangle
39
+ assert ids[125 // 2, 125 // 4] == 1
40
+ assert ids[125 // 2, 3 * 125 // 4] == 2
41
+
42
+
43
+ def test_empty_markers_picking():
44
+ markers = Markers()
45
+ filter = MarkerPickingFilter()
46
+ markers.attach(filter)
47
+ filter.enabled = True
48
+
49
+
50
+ @requires_application()
51
+ def test_markers_picking():
52
+ markers = Markers(
53
+ pos=np.array([[-0.5, -0.5], [0.5, 0.5]]),
54
+ size=5,
55
+ )
56
+ filter = MarkerPickingFilter()
57
+ markers.attach(filter)
58
+
59
+ with TestingCanvas(size=(125, 125)) as c:
60
+ view = c.central_widget.add_view(camera="panzoom")
61
+ view.camera.rect = (-1, -1, 2, 2)
62
+ view.add(markers)
63
+
64
+ filter.enabled = True
65
+ markers.update_gl_state(blend=False)
66
+ picking_render = c.render(bgcolor=(0, 0, 0, 0), alpha=True)
67
+ ids = picking_render.view(np.uint32)
68
+
69
+ assert ids[3 * 125 // 4, 125 // 4] == 1
70
+ assert ids[125 // 4, 3 * 125 // 4] == 2
@@ -2,6 +2,8 @@
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 functools import lru_cache
6
+
5
7
  import numpy as np
6
8
 
7
9
 
@@ -40,46 +42,49 @@ class DashAtlas(object):
40
42
  self._atlas[key] = [self._index / float(self._data.shape[0]), period]
41
43
  self._index += 1
42
44
  self._dirty = True
43
- # self.add_pattern(value)
44
-
45
- def make_pattern(self, pattern, caps=[1, 1]):
46
- """ """
47
-
48
- # A pattern is defined as on/off sequence of segments
49
- # It must be a multiple of 2
50
- if len(pattern) > 1 and len(pattern) % 2:
51
- pattern = [pattern[0] + pattern[-1]] + pattern[1:-1]
52
- P = np.array(pattern)
53
-
54
- # Period is the sum of all segment length
55
- period = np.cumsum(P)[-1]
56
-
57
- # Find all start and end of on-segment only
58
- C, c = [], 0
59
- for i in range(0, len(P) + 2, 2):
60
- a = max(0.0001, P[i % len(P)])
61
- b = max(0.0001, P[(i + 1) % len(P)])
62
- C.extend([c, c + a])
63
- c += a + b
64
- C = np.array(C)
65
-
66
- # Build pattern
45
+
46
+ def make_pattern(self, pattern, caps=(1, 1)):
67
47
  length = self._data.shape[1]
68
- Z = np.zeros((length, 4), dtype=np.float32)
69
- for i in np.arange(0, len(Z)):
70
- x = period * (i) / float(len(Z) - 1)
71
- index = np.argmin(abs(C - (x)))
72
- if index % 2 == 0:
73
- if x <= C[index]:
74
- dash_type = +1
75
- else:
76
- dash_type = 0
77
- dash_start, dash_end = C[index], C[index + 1]
48
+ return _make_pattern(length, pattern, caps)
49
+
50
+
51
+ @lru_cache(maxsize=32)
52
+ def _make_pattern(length, pattern, caps):
53
+ """Make a concrete dash pattern of a given length."""
54
+ # A pattern is defined as on/off sequence of segments
55
+ # It must be a multiple of 2
56
+ if len(pattern) > 1 and len(pattern) % 2:
57
+ pattern = [pattern[0] + pattern[-1]] + pattern[1:-1]
58
+ P = np.array(pattern)
59
+
60
+ # Period is the sum of all segment length
61
+ period = np.cumsum(P)[-1]
62
+
63
+ # Find all start and end of on-segment only
64
+ C, c = [], 0
65
+ for i in range(0, len(P) + 2, 2):
66
+ a = max(0.0001, P[i % len(P)])
67
+ b = max(0.0001, P[(i + 1) % len(P)])
68
+ C.extend([c, c + a])
69
+ c += a + b
70
+ C = np.array(C)
71
+
72
+ # Build pattern
73
+ Z = np.zeros((length, 4), dtype=np.float32)
74
+ for i in np.arange(0, len(Z)):
75
+ x = period * (i) / float(len(Z) - 1)
76
+ index = np.argmin(abs(C - (x)))
77
+ if index % 2 == 0:
78
+ if x <= C[index]:
79
+ dash_type = +1
80
+ else:
81
+ dash_type = 0
82
+ dash_start, dash_end = C[index], C[index + 1]
83
+ else:
84
+ if x > C[index]:
85
+ dash_type = -1
78
86
  else:
79
- if x > C[index]:
80
- dash_type = -1
81
- else:
82
- dash_type = 0
83
- dash_start, dash_end = C[index - 1], C[index]
84
- Z[i] = C[index], dash_type, dash_start, dash_end
85
- return Z, period
87
+ dash_type = 0
88
+ dash_start, dash_end = C[index - 1], C[index]
89
+ Z[i] = C[index], dash_type, dash_start, dash_end
90
+ return Z, period
vispy/visuals/markers.py CHANGED
@@ -11,12 +11,13 @@ from ..color import ColorArray
11
11
  from ..gloo import VertexBuffer
12
12
  from .shaders import Function, Variable
13
13
  from .visual import Visual
14
+ from ..util.event import Event
14
15
 
15
16
 
16
17
  _VERTEX_SHADER = """
17
18
  uniform float u_antialias;
18
19
  uniform float u_px_scale;
19
- uniform int u_scaling;
20
+ uniform bool u_scaling;
20
21
  uniform bool u_spherical;
21
22
 
22
23
  attribute vec3 a_position;
@@ -48,26 +49,15 @@ void main (void) {
48
49
  gl_Position = $framebuffer_to_render(fb_pos);
49
50
 
50
51
  // NOTE: gl_stuff uses framebuffer coords!
51
- if (u_scaling == 1) {
52
+ if (u_scaling) {
52
53
  // scaling == "scene": scale marker using entire visual -> framebuffer set of transforms
53
- x = $framebuffer_to_visual(fb_pos + vec4(big_float, 0, 0, 0));
54
- x = (x - pos);
55
- size_vec = $visual_to_framebuffer(pos + normalize(x) * a_size);
56
- $v_size = size_vec.x / size_vec.w - fb_pos.x / fb_pos.w;
57
- v_edgewidth = ($v_size / a_size) * a_edgewidth;
58
- }
59
- else if (u_scaling == 2) {
60
54
  // scaling == "visual": scale marker using only the Visual's transform
61
- // move horizontally in framebuffer space
62
- // then go to scene coordinates (not visual, so scaling is accounted for)
63
- x = $framebuffer_to_scene(fb_pos + vec4(big_float, 0, 0, 0));
64
- // subtract position, so we get the scene-coordinate vector describing
65
- // an "horizontal direction parallel to the screen"
66
- vec4 scene_pos = $framebuffer_to_scene(fb_pos);
67
- x = (x - scene_pos);
68
- // multiply that direction by the size (in scene space) and add it to the position
55
+ pos = $framebuffer_to_scene_or_visual(fb_pos);
56
+ x = $framebuffer_to_scene_or_visual(fb_pos + vec4(big_float, 0, 0, 0));
57
+ x = (x - pos);
58
+ // multiply that direction by the size and add it to the position
69
59
  // this gives us the position of the edge of the point, which we convert in screen space
70
- size_vec = $scene_to_framebuffer(scene_pos + normalize(x) * a_size);
60
+ size_vec = $scene_or_visual_to_framebuffer(pos + normalize(x) * a_size);
71
61
  // divide by `w` for perspective, and subtract pos
72
62
  // this gives us the actual screen-space size of the point
73
63
  $v_size = size_vec.x / size_vec.w - fb_pos.x / fb_pos.w;
@@ -83,12 +73,13 @@ void main (void) {
83
73
  gl_PointSize = $v_size + 4. * (v_edgewidth + 1.5 * u_antialias);
84
74
 
85
75
  if (u_spherical == true) {
76
+ // similar as above for scaling, but in towards the screen direction
86
77
  // Get the framebuffer z direction relative to this sphere in visual coords
87
- vec4 z = $framebuffer_to_visual(fb_pos + vec4(0, 0, big_float, 0));
78
+ vec4 z = $framebuffer_to_scene_or_visual(fb_pos + vec4(0, 0, big_float, 0));
88
79
  z = (z - pos);
89
80
  // Get the depth of the sphere in its middle point on the screen
90
81
  // size/2 because we need the radius, not the diameter
91
- vec4 depth_z_vec = $visual_to_framebuffer(pos + normalize(z) * a_size / 2);
82
+ vec4 depth_z_vec = $scene_or_visual_to_framebuffer(pos + normalize(z) * a_size / 2);
92
83
  v_depth_middle = depth_z_vec.z / depth_z_vec.w - fb_pos.z / fb_pos.w;
93
84
  // size ratio between aliased and non-aliased, needed for correct depth
94
85
  v_alias_ratio = gl_PointSize / $v_size;
@@ -560,6 +551,7 @@ class MarkersVisual(Visual):
560
551
  light_color='white', light_position=(1, -1, 1), light_ambient=0.3, **kwargs):
561
552
  self._vbo = VertexBuffer()
562
553
  self._data = None
554
+ self._scaling = "fixed"
563
555
 
564
556
  Visual.__init__(self, vcode=self._shaders['vertex'], fcode=self._shaders['fragment'])
565
557
  self._symbol_func = Function(self._symbol_shader)
@@ -573,11 +565,11 @@ class MarkersVisual(Visual):
573
565
  blend_func=('src_alpha', 'one_minus_src_alpha'))
574
566
  self._draw_mode = 'points'
575
567
 
568
+ self.events.add(data_updated=Event)
569
+
576
570
  if len(kwargs) > 0:
577
571
  self.set_data(**kwargs)
578
572
 
579
- self._scaling = "fixed"
580
- self._scaling_int = 0
581
573
  self.scaling = scaling
582
574
  self.antialias = antialias
583
575
  self.light_color = light_color
@@ -662,6 +654,7 @@ class MarkersVisual(Visual):
662
654
  self._vbo.set_data(data)
663
655
  self.shared_program.bind(self._vbo)
664
656
 
657
+ self.events.data_updated()
665
658
  self.update()
666
659
 
667
660
  @property
@@ -702,19 +695,17 @@ class MarkersVisual(Visual):
702
695
  @scaling.setter
703
696
  def scaling(self, value):
704
697
  scaling_modes = {
705
- False: 0,
706
- True: 1,
707
- "fixed": 0,
708
- "scene": 1,
709
- "visual": 2,
698
+ False: "fixed",
699
+ True: "scene",
700
+ "fixed": "fixed",
701
+ "scene": "scene",
702
+ "visual": "visual",
710
703
  }
711
704
  if value not in scaling_modes:
712
705
  possible_options = ", ".join(repr(opt) for opt in scaling_modes)
713
706
  raise ValueError(f"Unknown scaling option {value!r}, expected one of: {possible_options}")
714
- scaling_int = scaling_modes[value]
715
- self.shared_program['u_scaling'] = scaling_int
716
- self._scaling = value
717
- self._scaling_int = scaling_int
707
+ self._scaling = scaling_modes[value]
708
+ self.shared_program['u_scaling'] = self._scaling != "fixed"
718
709
  self.update()
719
710
 
720
711
  @property
@@ -799,16 +790,15 @@ class MarkersVisual(Visual):
799
790
 
800
791
  def _prepare_transforms(self, view):
801
792
  view.view_program.vert['visual_to_framebuffer'] = view.get_transform('visual', 'framebuffer')
802
- view.view_program.vert['framebuffer_to_visual'] = view.get_transform('framebuffer', 'visual')
803
793
  view.view_program.vert['framebuffer_to_render'] = view.get_transform('framebuffer', 'render')
804
- view.view_program.vert['framebuffer_to_scene'] = view.get_transform('framebuffer', 'scene')
805
- view.view_program.vert['scene_to_framebuffer'] = view.get_transform('scene', 'framebuffer')
794
+ scaling = view._scaling if view._scaling != "fixed" else "scene"
795
+ view.view_program.vert['framebuffer_to_scene_or_visual'] = view.get_transform('framebuffer', scaling)
796
+ view.view_program.vert['scene_or_visual_to_framebuffer'] = view.get_transform(scaling, 'framebuffer')
806
797
 
807
798
  def _prepare_draw(self, view):
808
799
  if self._data is None:
809
800
  return False
810
801
  view.view_program['u_px_scale'] = view.transforms.pixel_scale
811
- view.view_program['u_scaling'] = self._scaling_int
812
802
 
813
803
  def _compute_bounds(self, axis, view):
814
804
  pos = self._data['a_position']
@@ -1,5 +1,4 @@
1
- # -*- coding: utf-8 -*-
2
-
1
+ # cython: language_level=3, boundscheck=False, cdivision=True, wraparound=False, initializedcheck=False, nonecheck=False
3
2
  # A Cython implementation of the "eight-points signed sequential Euclidean
4
3
  # distance transform algorithm" (8SSEDT)
5
4
 
@@ -18,13 +17,14 @@ ctypedef np.complex64_t DTYPE_ct
18
17
  cdef DTYPE_ct MAX_VAL = (1e6 + 1e6j)
19
18
 
20
19
 
21
- @cython.boundscheck(False) # designed to stay within bounds
22
- @cython.wraparound(False) # we don't use negative indexing
23
20
  def _calc_distance_field(np.ndarray[DTYPE_t, ndim=2] pixels,
24
21
  int w, int h, DTYPE_t sp_f):
25
22
  # initialize grids
26
- cdef np.ndarray[DTYPE_ct, ndim=2] g0 = np.zeros((h, w), dtype_c)
27
- cdef np.ndarray[DTYPE_ct, ndim=2] g1 = np.zeros((h, w), dtype_c)
23
+ cdef np.ndarray[DTYPE_ct, ndim=2] g0_arr = np.zeros((h, w), dtype_c)
24
+ cdef np.ndarray[DTYPE_ct, ndim=2] g1_arr = np.zeros((h, w), dtype_c)
25
+ cdef DTYPE_ct[:, ::1] g0 = g0_arr
26
+ cdef DTYPE_ct[:, ::1] g1 = g1_arr
27
+ cdef DTYPE_t[:, :] pixels_view = pixels
28
28
  cdef Py_ssize_t y, x
29
29
  for y in range(h):
30
30
  g0[y, 0] = MAX_VAL
@@ -32,9 +32,9 @@ def _calc_distance_field(np.ndarray[DTYPE_t, ndim=2] pixels,
32
32
  g1[y, 0] = MAX_VAL
33
33
  g1[y, w-1] = MAX_VAL
34
34
  for x in range(1, w-1):
35
- if pixels[y, x] > 0:
35
+ if pixels_view[y, x] > 0:
36
36
  g0[y, x] = MAX_VAL
37
- if pixels[y, x] < 1:
37
+ if pixels_view[y, x] < 1:
38
38
  g1[y, x] = MAX_VAL
39
39
  for x in range(w):
40
40
  g0[0, x] = MAX_VAL
@@ -50,37 +50,35 @@ def _calc_distance_field(np.ndarray[DTYPE_t, ndim=2] pixels,
50
50
  cdef DTYPE_t r_sp_f_2 = 1. / (sp_f * 2.)
51
51
  for y in range(1, h-1):
52
52
  for x in range(1, w-1):
53
- pixels[y, x] = sqrt(dist(g0[y, x])) - sqrt(dist(g1[y, x]))
54
- if pixels[y, x] < 0:
55
- pixels[y, x] = (pixels[y, x] + sp_f) * r_sp_f_2
53
+ pixels_view[y, x] = sqrt(dist(g0[y, x])) - sqrt(dist(g1[y, x]))
54
+ if pixels_view[y, x] < 0:
55
+ pixels_view[y, x] = (pixels_view[y, x] + sp_f) * r_sp_f_2
56
56
  else:
57
- pixels[y, x] = 0.5 + pixels[y, x] * r_sp_f_2
58
- pixels[y, x] = max(min(pixels[y, x], 1), 0)
57
+ pixels_view[y, x] = 0.5 + pixels_view[y, x] * r_sp_f_2
58
+ pixels_view[y, x] = max(min(pixels_view[y, x], 1), 0)
59
59
 
60
60
 
61
- @cython.boundscheck(False) # designed to stay within bounds
62
- @cython.wraparound(False) # we don't use negative indexing
63
- cdef Py_ssize_t compare(DTYPE_ct *cell, DTYPE_ct xy, DTYPE_t *current):
61
+ cdef inline Py_ssize_t compare(DTYPE_ct *cell, DTYPE_ct xy, DTYPE_t *current) noexcept nogil:
64
62
  cdef DTYPE_t val = dist(xy)
65
63
  if val < current[0]:
66
64
  cell[0] = xy
67
65
  current[0] = val
68
66
 
69
67
 
70
- @cython.boundscheck(False) # designed to stay within bounds
71
- @cython.wraparound(False) # we don't use negative indexing
72
- cdef DTYPE_t dist(DTYPE_ct val):
68
+ cdef DTYPE_t dist(DTYPE_ct val) noexcept nogil:
73
69
  return val.real*val.real + val.imag*val.imag
74
70
 
75
71
 
76
- @cython.boundscheck(False) # designed to stay within bounds
77
- @cython.wraparound(False) # we don't use negative indexing
78
- cdef void _propagate(np.ndarray[DTYPE_ct, ndim=2] grid):
72
+ cdef void _propagate(DTYPE_ct[:, :] grid) noexcept nogil:
79
73
  cdef Py_ssize_t height = grid.shape[0]
80
74
  cdef Py_ssize_t width = grid.shape[1]
81
75
  cdef Py_ssize_t y, x
82
76
  cdef DTYPE_t current
83
- cdef DTYPE_ct a0=-1, a1=-1j, a2=-1-1j, a3=1-1j
77
+ cdef DTYPE_ct a0, a1, a2, a3
78
+ a0 = -1
79
+ a1 = -1j
80
+ a2 = -1 - 1j
81
+ a3 = 1 - 1j
84
82
  cdef DTYPE_ct b0=1
85
83
  cdef DTYPE_ct c0=1, c1=1j, c2=-1+1j, c3=1+1j
86
84
  cdef DTYPE_ct d0=-1
vispy/visuals/visual.py CHANGED
@@ -83,9 +83,10 @@ A compound visual will automatically handle forwarding transform system changes
83
83
  and filter attachments to its internally-wrapped visuals. To the user, this
84
84
  will appear to behave as a single visual.
85
85
  """
86
-
87
86
  from __future__ import division
88
87
  import weakref
88
+ from contextlib import contextmanager
89
+
89
90
  import numpy as np
90
91
 
91
92
  from .. import gloo
@@ -337,6 +338,7 @@ class Visual(BaseVisual):
337
338
  raise ValueError("Cannot specify both program and "
338
339
  "vcode/fcode arguments.")
339
340
 
341
+ self._prev_gl_state = []
340
342
  self._program = self._vshare.program.add_program()
341
343
  self._prepare_transforms(self)
342
344
  self._filters = []
@@ -347,6 +349,11 @@ class Visual(BaseVisual):
347
349
 
348
350
  The arguments are forwarded to :func:`vispy.gloo.wrappers.set_state`.
349
351
 
352
+ This can also be used as a context manager that will revert the
353
+ gl_state on exit. When used as a context manager, this function is
354
+ designed to be constructed directly in the header of the `with`
355
+ statement to avoid confusion about what state will be restored on exit.
356
+
350
357
  Parameters
351
358
  ----------
352
359
  preset : str
@@ -354,14 +361,21 @@ class Visual(BaseVisual):
354
361
  **kwargs : dict
355
362
  Keyword arguments.
356
363
  """
364
+ prev_gl_state = self._vshare.gl_state.copy()
365
+
357
366
  self._vshare.gl_state = kwargs
358
367
  self._vshare.gl_state['preset'] = preset
359
368
 
369
+ return _revert_gl_state([(self, prev_gl_state)])
370
+
360
371
  def update_gl_state(self, *args, **kwargs):
361
372
  """Modify the set of GL state parameters to use when drawing.
362
373
 
363
374
  The arguments are forwarded to :func:`vispy.gloo.wrappers.set_state`.
364
375
 
376
+ This can also be used as a context manager that will revert the
377
+ gl_state on exit.
378
+
365
379
  Parameters
366
380
  ----------
367
381
  *args : tuple
@@ -369,12 +383,61 @@ class Visual(BaseVisual):
369
383
  **kwargs : dict
370
384
  Keyword arguments.
371
385
  """
386
+ prev_gl_state = self._vshare.gl_state.copy()
387
+
372
388
  if len(args) == 1:
373
389
  self._vshare.gl_state['preset'] = args[0]
374
390
  elif len(args) != 0:
375
391
  raise TypeError("Only one positional argument allowed.")
376
392
  self._vshare.gl_state.update(kwargs)
377
393
 
394
+ return _revert_gl_state([(self, prev_gl_state)])
395
+
396
+ def push_gl_state(self, *args, **kwargs):
397
+ """Define the set of GL state parameters to use when drawing.
398
+
399
+ The arguments are forwarded to :func:`vispy.gloo.wrappers.set_state`.
400
+
401
+ This differs from :py:meth:`.set_gl_state` in that it stashes the
402
+ current state. See :py:meth:`.pop_gl_state` for restoring the state.
403
+
404
+ Parameters
405
+ ----------
406
+ *args : tuple
407
+ Arguments.
408
+ **kwargs : dict
409
+ Keyword arguments.
410
+ """
411
+ self._prev_gl_state.append(self._vshare.gl_state.copy())
412
+ self.set_gl_state(*args, **kwargs)
413
+
414
+ def push_gl_state_update(self, *args, **kwargs):
415
+ """Modify the set of GL state parameters to use when drawing.
416
+
417
+ The arguments are forwarded to :func:`vispy.gloo.wrappers.set_state`.
418
+
419
+ This differs from :py:meth:`.update_gl_state` in that it stashes the
420
+ current state. See :py:meth:`.pop_gl_state` for restoring the state.
421
+
422
+ Parameters
423
+ ----------
424
+ *args : tuple
425
+ Arguments.
426
+ **kwargs : dict
427
+ Keyword arguments.
428
+ """
429
+ self._prev_gl_state.append(self._vshare.gl_state.copy())
430
+ self.update_gl_state(*args, **kwargs)
431
+
432
+ def pop_gl_state(self):
433
+ """Restore a previous set of GL state parameters if available.
434
+
435
+ If no previous GL state is available (see :py:meth:`.push_gl_state`),
436
+ this has no effect.
437
+ """
438
+ if self._prev_gl_state:
439
+ self.set_gl_state(**self._prev_gl_state.pop())
440
+
378
441
  def _compute_bounds(self, axis, view):
379
442
  """Return the (min, max) bounding values of this visual along *axis*
380
443
  in the local coordinate system.
@@ -616,6 +679,9 @@ class CompoundVisual(BaseVisual):
616
679
 
617
680
  The arguments are forwarded to :func:`vispy.gloo.wrappers.set_state`.
618
681
 
682
+ This can also be used as a context manager that will revert the
683
+ gl_state on exit.
684
+
619
685
  Parameters
620
686
  ----------
621
687
  preset : str
@@ -623,14 +689,21 @@ class CompoundVisual(BaseVisual):
623
689
  **kwargs : dict
624
690
  Keyword arguments.
625
691
  """
692
+ prev_gl_state = []
626
693
  for v in self._subvisuals:
694
+ prev_gl_state.append((v, v._vshare.gl_state))
627
695
  v.set_gl_state(preset=preset, **kwargs)
628
696
 
697
+ return _revert_gl_state(prev_gl_state)
698
+
629
699
  def update_gl_state(self, *args, **kwargs):
630
700
  """Modify the set of GL state parameters to use when drawing.
631
701
 
632
702
  The arguments are forwarded to :func:`vispy.gloo.wrappers.set_state`.
633
703
 
704
+ This can also be used as a context manager that will revert the
705
+ gl_state on exit.
706
+
634
707
  Parameters
635
708
  ----------
636
709
  *args : tuple
@@ -638,9 +711,58 @@ class CompoundVisual(BaseVisual):
638
711
  **kwargs : dict
639
712
  Keyword arguments.
640
713
  """
714
+ prev_gl_state = []
641
715
  for v in self._subvisuals:
716
+ prev_gl_state.append((v, v._vshare.gl_state))
642
717
  v.update_gl_state(*args, **kwargs)
643
718
 
719
+ return _revert_gl_state(prev_gl_state)
720
+
721
+ def push_gl_state(self, *args, **kwargs):
722
+ """Define the set of GL state parameters to use when drawing.
723
+
724
+ The arguments are forwarded to :func:`vispy.gloo.wrappers.set_state`.
725
+
726
+ This differs from :py:meth:`.set_gl_state` in that it stashes the
727
+ current state. See :py:meth:`.pop_gl_state` for restoring the state.
728
+
729
+ Parameters
730
+ ----------
731
+ *args : tuple
732
+ Arguments.
733
+ **kwargs : dict
734
+ Keyword arguments.
735
+ """
736
+ for v in self._subvisuals:
737
+ v.push_gl_state(*args, **kwargs)
738
+
739
+ def push_gl_state_update(self, *args, **kwargs):
740
+ """Modify the set of GL state parameters to use when drawing.
741
+
742
+ The arguments are forwarded to :func:`vispy.gloo.wrappers.set_state`.
743
+
744
+ This differs from :py:meth:`.update_gl_state` in that it stashes the
745
+ current state. See :py:meth:`.pop_gl_state` for restoring the state.
746
+
747
+ Parameters
748
+ ----------
749
+ *args : tuple
750
+ Arguments.
751
+ **kwargs : dict
752
+ Keyword arguments.
753
+ """
754
+ for v in self._subvisuals:
755
+ v.push_gl_state_update(*args, **kwargs)
756
+
757
+ def pop_gl_state(self):
758
+ """Restore a previous set of GL state parameters if available.
759
+
760
+ If no previous GL state is available (see :py:meth:`.push_gl_state`),
761
+ this has no effect.
762
+ """
763
+ for v in self._subvisuals:
764
+ v.pop_gl_state()
765
+
644
766
  def attach(self, filt, view=None):
645
767
  """Attach a Filter to this visual
646
768
 
@@ -681,6 +803,25 @@ class CompoundVisual(BaseVisual):
681
803
  return bounds
682
804
 
683
805
 
806
+ @contextmanager
807
+ def _revert_gl_state(prev_gl_state):
808
+ """Context manager to store and revert GL state for a list of visuals.
809
+
810
+ Parameters
811
+ ----------
812
+ prev_gl_state : list
813
+ A list of (Visual, gl_state) tuples, where gl_state is a dictionary of
814
+ `gl_state` params as would be passed to :py:func:`set_gl_state`.
815
+ """
816
+ for v, state in prev_gl_state:
817
+ v._prev_gl_state.append(state)
818
+ try:
819
+ yield
820
+ finally:
821
+ for v, _ in prev_gl_state:
822
+ v.pop_gl_state()
823
+
824
+
684
825
  class CompoundVisualView(BaseVisualView, CompoundVisual):
685
826
  def __init__(self, visual):
686
827
  BaseVisualView.__init__(self, visual)
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vispy
3
- Version: 0.13.0
3
+ Version: 0.14.0
4
4
  Summary: Interactive visualization in Python
5
5
  Home-page: http://vispy.org
6
6
  Download-URL: https://pypi.python.org/pypi/vispy
7
7
  Author: Vispy contributors
8
8
  Author-email: vispy@googlegroups.com
9
- License: (new) BSD
9
+ License: BSD-3-Clause
10
10
  Keywords: visualization,OpenGl,ES,medical,imaging,3D,plotting,numpy,bigdata,ipython,jupyter,widgets
11
11
  Platform: any
12
12
  Classifier: Development Status :: 3 - Alpha
@@ -19,12 +19,13 @@ Classifier: Operating System :: MacOS :: MacOS X
19
19
  Classifier: Operating System :: Microsoft :: Windows
20
20
  Classifier: Operating System :: POSIX
21
21
  Classifier: Programming Language :: Python
22
- Classifier: Programming Language :: Python :: 3.6
23
- Classifier: Programming Language :: Python :: 3.7
24
22
  Classifier: Programming Language :: Python :: 3.8
23
+ Classifier: Programming Language :: Python :: 3.9
24
+ Classifier: Programming Language :: Python :: 3.10
25
+ Classifier: Programming Language :: Python :: 3.11
25
26
  Classifier: Framework :: IPython
26
27
  Provides: vispy
27
- Requires-Python: >=3.6
28
+ Requires-Python: >=3.8
28
29
  Description-Content-Type: text/x-rst
29
30
  License-File: LICENSE.txt
30
31
  Requires-Dist: numpy
@@ -47,7 +48,7 @@ Requires-Dist: Pillow ; extra == 'io'
47
48
  Provides-Extra: ipython-static
48
49
  Requires-Dist: ipython ; extra == 'ipython-static'
49
50
  Provides-Extra: pyglet
50
- Requires-Dist: pyglet (>=1.2) ; extra == 'pyglet'
51
+ Requires-Dist: pyglet >=1.2 ; extra == 'pyglet'
51
52
  Provides-Extra: pyqt5
52
53
  Requires-Dist: pyqt5 ; extra == 'pyqt5'
53
54
  Provides-Extra: pyqt6
@@ -1,10 +1,5 @@
1
- vispy-0.13.0.dist-info/RECORD,,
2
- vispy-0.13.0.dist-info/WHEEL,sha256=doY7Ynwndq62v9G9yH4QQiKEI6qNEsMeVrkmo76vHLE,111
3
- vispy-0.13.0.dist-info/top_level.txt,sha256=mciStn1SI48jYwEhJXlrMJ3HaJ90XOr5ZGJ8YdFI674,6
4
- vispy-0.13.0.dist-info/LICENSE.txt,sha256=SyGjHD8uWIdCicPnNsCNMhwjJomQCArryMh7QGkD3JI,1774
5
- vispy-0.13.0.dist-info/METADATA,sha256=k8JNLtGKFWluu5LvtZi3DJSH1iUMlN5OTES3HL_IqrQ,8275
6
1
  vispy/conftest.py,sha256=1qkMPiB5SHGll3Sbvz9iBIAVeyaIuW7YDw8VpfKTMzk,489
7
- vispy/version.py,sha256=f-9LbbVLxrMv9t2MAPnCkiOGpkpxS3NyVpWFhOoKg6A,162
2
+ vispy/version.py,sha256=uwy8vZw1JYKQhBZgHVBJvAgtZKgZS7cfmR39J3Flq0E,162
8
3
  vispy/__init__.py,sha256=Sm3hzOs_-fXTikZnwAFZoLOvETmFCw8BsSoAguXvhdY,902
9
4
  vispy/app/_detect_eventloop.py,sha256=hMht958_W6MVGEfJS7UMTRnIHyoODeJ6oVCaDXJZ5H8,6474
10
5
  vispy/app/timer.py,sha256=zIQwcF-XmowAHqe25z4nMwiRTMaNOnjz4IeTQClE8bg,5754
@@ -17,7 +12,7 @@ vispy/app/canvas.py,sha256=DXjjdCcIgoE8wYpbZBqOPXW_7eq3QR0OJTHSdayNLnk,28849
17
12
  vispy/app/backends/_pyside2.py,sha256=C8dxVEDRWKrzaH2QnlB98wM4gpFT048nzNEHZC0Gipk,1965
18
13
  vispy/app/backends/_jupyter_rfb.py,sha256=cN7sgJafp8L9Um36K89V1JS1EfUsn0AiUHBmWBMIUzk,8805
19
14
  vispy/app/backends/_pyside6.py,sha256=bihad8zmp1UsjGdeLyGnYlmHZivGe99H4h3A-UJG0jU,1816
20
- vispy/app/backends/_glfw.py,sha256=qQy8l1W5fRqO8rf63EFv1DNBaxF4gNNRdkTthewO7vg,17018
15
+ vispy/app/backends/_glfw.py,sha256=xTXcH1oms8XoZeAOKJ19yRcR7N63BVjaqEQCtKy-nyc,17021
21
16
  vispy/app/backends/_pyglet.py,sha256=vx8GsKA30APkn9U2_UVghJfVo5uuskt_cPxehAgP7Dw,15110
22
17
  vispy/app/backends/_pyqt6.py,sha256=w3_ky9vIxTfviVMnm4kqv3m_xhleIqMYTgwXXENFSqo,1422
23
18
  vispy/app/backends/_qt.py,sha256=-Frs4Cmjwo-3pqIVV385gX6FDRNE3NcsUr23ECsB1B8,35449
@@ -123,7 +118,7 @@ vispy/gloo/context.py,sha256=cFlSfTlr-WQLWfksOqFXjqCel2F5kk9nR1RTdjLhyPM,8640
123
118
  vispy/gloo/wrappers.py,sha256=38_YO3Td70B_CNFKysff61jfAyMaaDu_lteIeotVu0c,26460
124
119
  vispy/gloo/framebuffer.py,sha256=77lIv9iojJ3tPm6ZsE8hRS0Iv9UdyoCRDC0RtxKTR-o,9350
125
120
  vispy/gloo/buffer.py,sha256=P7zC2JsVMr_ng6rOWJgL93iQWfPJqCJofaoEh8EZ0cg,16746
126
- vispy/gloo/texture.py,sha256=vPP3z1JjLxHsPV2YI76K-UOsoNzFSbsqjQbJE2Iy-1g,38258
121
+ vispy/gloo/texture.py,sha256=u8dWlGa0zj0CAh84sfUhvd0DJNZHdBjObtUlNSmYN18,38422
127
122
  vispy/gloo/program.py,sha256=fsrl5kJ5aLX46M7cwPBl9QuZ4DP55Oa0I0QUprKUvok,22324
128
123
  vispy/gloo/tests/test_glir.py,sha256=e8hiifOuNehzDAqg4x5zqNKB-pzcPe-lP_RL4ZpLKoo,9644
129
124
  vispy/gloo/tests/test_buffer.py,sha256=RMbQeeKSG2lO2gxqmHx2CfMP841MbnbYDYNb6SxLc40,19819
@@ -152,7 +147,7 @@ vispy/gloo/gl/tests/test_basics.py,sha256=jTnERZwk3Qeg86__8N7oBfu3A02OHVfoT204cn
152
147
  vispy/gloo/gl/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
153
148
  vispy/gloo/gl/tests/test_use.py,sha256=C4S4BXTg2_2snJnwISvJecufesxVgC6zfqicTZINGb8,1838
154
149
  vispy/gloo/gl/tests/test_functionality.py,sha256=3OTTA_SHwiq-3fxNQKwQV8doAnr4Xe-BFmeqr93Xu3o,17340
155
- vispy/color/colormap.py,sha256=Nd0xApe-zYIAK9-4p-DunowQfgowSL1qVJhnsUnVQQ4,43634
150
+ vispy/color/colormap.py,sha256=Xp7e05oaJausMOvl1UciqDagPuq61GJA9iJq4wpdMfw,42586
156
151
  vispy/color/__init__.py,sha256=aePQV4OX_2QsRcILIHdzlirVfGvfAxFgmaajLFqMjXo,644
157
152
  vispy/color/color_space.py,sha256=WcVdx-Bio8GSC9-VTcjpXBZ970uE3OZGgDF27MpBFX0,6051
158
153
  vispy/color/color_array.py,sha256=egFLGVDcXkTTGSHE12cXynRGSFJWXbr1g9dFWMXpGwQ,14413
@@ -347,9 +342,9 @@ vispy/visuals/infinite_line.py,sha256=A5D8zn_6M_4U0SCa2JacRIxmofBGgQMxa4gn8f3wgA
347
342
  vispy/visuals/gridmesh.py,sha256=It0s6Mj4A5kVuDVy6-xkUqC0Yq1mgoqH8JF9uN02zf8,3346
348
343
  vispy/visuals/surface_plot.py,sha256=hwKaTVJW0ty9zXp2HCI4NWr2irUYuasI7IknQwreG8U,7279
349
344
  vispy/visuals/cube.py,sha256=Cly3BjLLr436IUrQdRuL8FmSxXO0siKrxpCVaDxM-6U,1723
350
- vispy/visuals/markers.py,sha256=I_4zKZ0qYs6eov_rKxg2w54g3jo_YY5mq97bQxG_Ywg,27423
345
+ vispy/visuals/markers.py,sha256=EsjarX9enN1CzfEN5JyGihX7sPbj_36eadvPglrF29I,26898
351
346
  vispy/visuals/rectangle.py,sha256=r7eKBo1HH0WAGTxeXFBfHJN0Zvjfue7JcDFTdcMiWmU,6664
352
- vispy/visuals/visual.py,sha256=yR7T5D0m_ZLxrGQy_XZKQzAz9901d4f0IU_nkTged-E,25118
347
+ vispy/visuals/visual.py,sha256=Oo-skll6aDN1lkzMgrxeSPN4Xjf9OwRoOGD9YF_58vU,29791
353
348
  vispy/visuals/ellipse.py,sha256=jG_fryPB4mDEJtzeX8YhK7xf_Jz-oAjIqXNu87S8S2Y,5092
354
349
  vispy/visuals/line_plot.py,sha256=eqV9Trg8kU8t2C9dXfINAIsM7QK4BeAgWG2QMkqde-E,4882
355
350
  vispy/visuals/spectrogram.py,sha256=cf8PfG_hHZ3aUM8SExpIG5Tmr57N9gEF0-R6jPQ2K00,4940
@@ -365,12 +360,14 @@ vispy/visuals/isoline.py,sha256=O8-01BlZVu9mJXIH09eSzczyQYVAS4JSJuzCLQCPxdY,8508
365
360
  vispy/visuals/filters/color.py,sha256=un6uQb3Bnt4fIrwD0V2XrHbTW_RZebIIXh-YOs1XFHI,5049
366
361
  vispy/visuals/filters/clipper.py,sha256=W-00FBPR4jl38K7e5X-E_p-LqzTitJDQIskh74Zy-4o,1741
367
362
  vispy/visuals/filters/clipping_planes.py,sha256=yH1Pe11bE79nbNsm1wSP8pXFxjpM8KWjEPTyPeLW16o,4527
368
- vispy/visuals/filters/__init__.py,sha256=sqml9pNgdNw8z5Z27VwJa8lhgc3WPTqgQj3Uxh5mD6Y,454
369
- vispy/visuals/filters/mesh.py,sha256=wunI3gZrJAjW7M_C_eMJk2_k-wffjWNap6-vVJ3jrr8,25296
363
+ vispy/visuals/filters/__init__.py,sha256=gdYW5_HESDnVumjKNiseMuDwffcsKwJRBqn_XEUVMUo,546
364
+ vispy/visuals/filters/mesh.py,sha256=ynY1Wl-PE3_-IdZ5OpB3K-vK9HCXheYJBVnEOExki0s,26100
365
+ vispy/visuals/filters/markers.py,sha256=A-cker7Q-byrQR-Mkqs6LkVNMKxcylza5Wf_axiFmaY,771
370
366
  vispy/visuals/filters/picking.py,sha256=tjlZPGwvocRbhxc0fo9baThE0XINMI3NyIPOzULqhGQ,1537
371
- vispy/visuals/filters/base_filter.py,sha256=IUByLoAvM-uGEzJ6YgPbGd2M8m0-s-AnCXqHzfk0CPM,3477
367
+ vispy/visuals/filters/base_filter.py,sha256=1feDQZCJs0KJamGORi9LqYqZSz0tCAdKEgSpx9uu2Oo,7524
372
368
  vispy/visuals/filters/tests/__init__.py,sha256=n37y2k9PfraS0MCHjzGmZMQwq4JrV8drcY6_LBPegSk,160
373
369
  vispy/visuals/filters/tests/test_wireframe_filter.py,sha256=dOC_Mbu6vaO4k1Df_RrlfdYNemdXhbm1abIn-Pd5KCo,491
370
+ vispy/visuals/filters/tests/test_primitive_picking_filters.py,sha256=psdK7V00cZHm2T-N5J8PtgUB0qUSCUAJ_f_jcMNJMSk,2142
374
371
  vispy/visuals/tests/test_rectangle.py,sha256=C4g0A3D5KxeYAkudF3h-xybobcapJAVf-etq1qMwe_k,6658
375
372
  vispy/visuals/tests/test_volume.py,sha256=VY3NBuGjc90d2EWAjbKv-rxPTRKdKZB4LQ8CSheck0I,17578
376
373
  vispy/visuals/tests/test_isosurface.py,sha256=D_z3tzVuOf8StocyDJ9YWu6Tcih1AsX48SqQbrzkIkg,469
@@ -410,7 +407,7 @@ vispy/visuals/transforms/_util.py,sha256=PV6m0Per-EP4yb-CnQwYucl7-d3xWYgJ5aRN5T2
410
407
  vispy/visuals/transforms/interactive.py,sha256=bIhovKNyYICNIl-WS5F2FlpUp3RXimklH4dWxbDVYOM,2770
411
408
  vispy/visuals/transforms/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
412
409
  vispy/visuals/transforms/tests/test_transforms.py,sha256=u8TbJ0ZJJBqGR78SCZTCC9HxyxShIlaaMK1EVFao2e4,6871
413
- vispy/visuals/line/dash_atlas.py,sha256=HLz-i7lnsXAGj6_hdeAkvVq78vBVKJ2gXUoZY30yWko,2952
410
+ vispy/visuals/line/dash_atlas.py,sha256=91R7NkXdZX1Jv5WydFaM2XfFOLz2L6txO5ZwbMYqjpc,2975
414
411
  vispy/visuals/line/line.py,sha256=bBGEO0RhfXd9HWMy994BpppB2D5IExmpLCg13TmWJq8,18520
415
412
  vispy/visuals/line/arrow.py,sha256=jOezGwNIf-uBlOjE67pq6rPfej6VUbAjs_rWVzhwNM4,10150
416
413
  vispy/visuals/line/__init__.py,sha256=YHmBu9i0u9YIDEGJtFfgVu8F2QaeJOU0CrWq6rpmleg,236
@@ -438,8 +435,8 @@ vispy/visuals/graphs/layouts/force_directed.py,sha256=uEquUaboL_26QG3IeIhN20AzAS
438
435
  vispy/visuals/graphs/layouts/random.py,sha256=ox3JXQ1ysTGYHYYicIDBAZzNzzIKBbFln2yY1dNRtz8,1674
439
436
  vispy/visuals/graphs/layouts/circular.py,sha256=hEKjN2ow9mtQRZCslOpIkZWMTp5QM_HI9NuObNH0P7g,1559
440
437
  vispy/visuals/graphs/layouts/networkx_layout.py,sha256=pqF1JClCUYj-Cjf-tcphzo4Z3Ym_ha5Arxo68tYj-es,3583
441
- vispy/visuals/text/_sdf_cpu.cpython-310-darwin.so,sha256=acUqTt3yei_JGaBqjI906G4u0qOBcuwntULre4pIXvM,67304
442
- vispy/visuals/text/_sdf_cpu.pyx,sha256=NJTaZbyTvz_R2mjezuiQTppatfUUxZwLP3YnElr54E4,3923
438
+ vispy/visuals/text/_sdf_cpu.cpython-310-darwin.so,sha256=nHlx02npOMax1TBktaqVH0Dl0wT22nwGznJhZvdJjZg,214344
439
+ vispy/visuals/text/_sdf_cpu.pyx,sha256=_P7SWbsPwKpEg3d7L0P-Rgw3l-YoClF6dOrtsahxZUk,3791
443
440
  vispy/visuals/text/__init__.py,sha256=XyFi9zGWKpgWKC6xm0uMQZD1yzWU5TBi3jzoIehZ-dM,357
444
441
  vispy/visuals/text/text.py,sha256=nyenaB3kj0YI6EpO9PCMH9RHMa4l9G1OoygZbOwksaE,25253
445
442
  vispy/visuals/text/_sdf_gpu.py,sha256=uQwtOfH0tzZSlOP7uSY28SUK4_yk-dWwmmJRPXVAgD0,10480
@@ -483,13 +480,13 @@ vispy/geometry/tests/test_triangulation.py,sha256=eh8K-pi55xnHbvpQgnvrfHkr8H4HdF
483
480
  vispy/geometry/tests/test_generation.py,sha256=4geYGZvwaY4jEs1OzNhXsLaH7EdW6FjyGko6qvzs9Kc,2110
484
481
  vispy/scene/events.py,sha256=InGc7WIv4C9KSZ1pinIuvhJg0QEAY8i6U3QOiBoq29I,2463
485
482
  vispy/scene/__init__.py,sha256=-YeoqfxnXOCd7MI98ud-ZFU7B4YPxH_c5N5oG91CGhU,1487
486
- vispy/scene/node.py,sha256=kW90B2JPaOUeulLkLFIe4x3ihHG2DQLyIRfr1MVszsg,19078
487
- vispy/scene/visuals.py,sha256=0iSBxfXyTSqrSMv1j6CbvjuoFUpJacv-rXpKiStSfgI,9867
483
+ vispy/scene/node.py,sha256=QhxUtCoUTI0-Aq4fhWUQPqT6nMAI3dLAvF-CdBLx8xY,19668
484
+ vispy/scene/visuals.py,sha256=tSJeXvKyGt3WgYBmb5F_dP18jS5f1NJAbkMf4VbfpZg,9936
488
485
  vispy/scene/subscene.py,sha256=fNU9YHRL7yqiT3kTTBpzc5nFm5Vd-OAQa6dcQES40JE,564
489
- vispy/scene/canvas.py,sha256=QBQl3zbzMxrniPFq2FAEMWrPRBBLXX8iyZ_XTHVgO30,22837
486
+ vispy/scene/canvas.py,sha256=xPr6jlneQ7jCEMgFlxq34NE1gIErsE4zx2YpjVoNzpc,22768
490
487
  vispy/scene/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
491
488
  vispy/scene/tests/test_canvas.py,sha256=dFcC9duJyOCg2JBaasUwgT-NEQjfAgb-XOcj_UWpt6E,4158
492
- vispy/scene/tests/test_visuals.py,sha256=XOuuIzi039LW3u6R758WKt-H1289NGW3Hti-_VjIIc8,1019
489
+ vispy/scene/tests/test_visuals.py,sha256=9CEdLH8Z6LTio8IMvCMILs_Bt2-wvdumSv473eltR0E,4365
493
490
  vispy/scene/tests/test_node.py,sha256=6bz5LpcsVtEhT2Y-TwvU2n1QOR4gBdKofMa7qiyk21U,4936
494
491
  vispy/scene/cameras/_base.py,sha256=R0Z8xl2BTu652doO8zcVrWSJD_hjWo00DYN81qRWnIA,1277
495
492
  vispy/scene/cameras/magnify.py,sha256=O4Ba-UVo0tr31KCgm1LqPTn-ohikLkY2VYCOF3VzjLc,5387
@@ -515,3 +512,8 @@ vispy/scene/widgets/viewbox.py,sha256=gZekx8GSoJj8vRvqJ7Hw5Y5L1kVCmQQrF_f5YQOkkE
515
512
  vispy/scene/widgets/label.py,sha256=2ynsXsQNgIRFFkLzftVr1p94nLYLRlr_WWATDuJq7-8,1308
516
513
  vispy/scene/widgets/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
517
514
  vispy/scene/widgets/tests/test_colorbar.py,sha256=Yp1A_nZKC9NTSzdTM6OYKAcMEk0QH1CYUlhpMuxmQ3A,1362
515
+ vispy-0.14.0.dist-info/RECORD,,
516
+ vispy-0.14.0.dist-info/WHEEL,sha256=3ggQLyEtwymd0rFFn7Y7bZeC-TPY--XSbFQw5jO7PyA,111
517
+ vispy-0.14.0.dist-info/top_level.txt,sha256=mciStn1SI48jYwEhJXlrMJ3HaJ90XOr5ZGJ8YdFI674,6
518
+ vispy-0.14.0.dist-info/LICENSE.txt,sha256=SyGjHD8uWIdCicPnNsCNMhwjJomQCArryMh7QGkD3JI,1774
519
+ vispy-0.14.0.dist-info/METADATA,sha256=FNXAytjqW3a0Nk_vCqgbqth3tH4H9tqTihynR7hEy4E,8328
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.40.0)
2
+ Generator: bdist_wheel (0.41.2)
3
3
  Root-Is-Purelib: false
4
4
  Tag: cp310-cp310-macosx_10_9_x86_64
5
5