vispy 0.14.2__cp312-cp312-macosx_11_0_arm64.whl → 0.15.0__cp312-cp312-macosx_11_0_arm64.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.

Files changed (40) hide show
  1. vispy/app/backends/_qt.py +46 -11
  2. vispy/color/colormap.py +106 -27
  3. vispy/color/tests/test_color.py +27 -1
  4. vispy/ext/cocoapy.py +1 -21
  5. vispy/geometry/__init__.py +1 -1
  6. vispy/geometry/calculations.py +30 -2
  7. vispy/geometry/meshdata.py +3 -1
  8. vispy/geometry/tests/test_triangulation.py +88 -0
  9. vispy/geometry/triangulation.py +14 -14
  10. vispy/gloo/buffer.py +4 -4
  11. vispy/gloo/gl/tests/test_functionality.py +4 -2
  12. vispy/gloo/glir.py +7 -2
  13. vispy/gloo/program.py +2 -2
  14. vispy/gloo/texture.py +4 -3
  15. vispy/scene/cameras/arcball.py +1 -2
  16. vispy/scene/cameras/base_camera.py +63 -50
  17. vispy/scene/cameras/panzoom.py +4 -1
  18. vispy/scene/cameras/perspective.py +6 -1
  19. vispy/scene/cameras/turntable.py +11 -1
  20. vispy/testing/_runners.py +3 -1
  21. vispy/util/__init__.py +15 -0
  22. vispy/version.py +9 -4
  23. vispy/visuals/_scalable_textures.py +7 -5
  24. vispy/visuals/collections/array_list.py +4 -4
  25. vispy/visuals/filters/mesh.py +6 -1
  26. vispy/visuals/gridlines.py +61 -5
  27. vispy/visuals/image.py +21 -8
  28. vispy/visuals/surface_plot.py +1 -1
  29. vispy/visuals/tests/test_gridlines.py +30 -0
  30. vispy/visuals/tests/test_image.py +17 -15
  31. vispy/visuals/tests/test_scalable_textures.py +16 -0
  32. vispy/visuals/tests/test_surface_plot.py +8 -3
  33. vispy/visuals/text/_sdf_cpu.cpython-312-darwin.so +0 -0
  34. vispy/visuals/text/_sdf_cpu.pyx +2 -0
  35. vispy/visuals/volume.py +35 -4
  36. {vispy-0.14.2.dist-info → vispy-0.15.0.dist-info}/METADATA +53 -28
  37. {vispy-0.14.2.dist-info → vispy-0.15.0.dist-info}/RECORD +40 -39
  38. {vispy-0.14.2.dist-info → vispy-0.15.0.dist-info}/WHEEL +2 -1
  39. {vispy-0.14.2.dist-info → vispy-0.15.0.dist-info/licenses}/LICENSE.txt +1 -1
  40. {vispy-0.14.2.dist-info → vispy-0.15.0.dist-info}/top_level.txt +0 -0
@@ -4,27 +4,31 @@
4
4
 
5
5
  from __future__ import division
6
6
 
7
+ import numpy as np
8
+
7
9
  from .image import ImageVisual
8
10
  from ..color import Color
9
11
  from .shaders import Function
10
12
 
11
13
 
12
14
  _GRID_COLOR = """
15
+ uniform vec4 u_gridlines_bounds;
16
+ uniform float u_border_width;
17
+
13
18
  vec4 grid_color(vec2 pos) {
14
19
  vec4 px_pos = $map_to_doc(vec4(pos, 0, 1));
15
20
  px_pos /= px_pos.w;
16
21
 
17
22
  // Compute vectors representing width, height of pixel in local coords
18
- float s = 1.;
19
23
  vec4 local_pos = $map_doc_to_local(px_pos);
20
- vec4 dx = $map_doc_to_local(px_pos + vec4(1.0 / s, 0, 0, 0));
21
- vec4 dy = $map_doc_to_local(px_pos + vec4(0, 1.0 / s, 0, 0));
24
+ vec4 dx = $map_doc_to_local(px_pos + vec4(1.0, 0, 0, 0));
25
+ vec4 dy = $map_doc_to_local(px_pos + vec4(0, 1.0, 0, 0));
22
26
  local_pos /= local_pos.w;
23
27
  dx = dx / dx.w - local_pos;
24
28
  dy = dy / dy.w - local_pos;
25
29
 
26
30
  // Pixel length along each axis, rounded to the nearest power of 10
27
- vec2 px = s * vec2(abs(dx.x) + abs(dy.x), abs(dx.y) + abs(dy.y));
31
+ vec2 px = vec2(abs(dx.x) + abs(dy.x), abs(dx.y) + abs(dy.y));
28
32
  float log10 = log(10.0);
29
33
  float sx = pow(10.0, floor(log(px.x) / log10) + 1.) * $scale.x;
30
34
  float sy = pow(10.0, floor(log(px.y) / log10) + 1.) * $scale.y;
@@ -57,6 +61,17 @@ vec4 grid_color(vec2 pos) {
57
61
  if (alpha == 0.) {
58
62
  discard;
59
63
  }
64
+
65
+ if (any(lessThan(local_pos.xy + u_border_width / 2, u_gridlines_bounds.xz)) ||
66
+ any(greaterThan(local_pos.xy - u_border_width / 2, u_gridlines_bounds.yw))) {
67
+ discard;
68
+ }
69
+
70
+ if (any(lessThan(local_pos.xy - u_gridlines_bounds.xz, vec2(u_border_width / 2))) ||
71
+ any(lessThan(u_gridlines_bounds.yw - local_pos.xy, vec2(u_border_width / 2)))) {
72
+ alpha = 1;
73
+ }
74
+
60
75
  return vec4($color.rgb, $color.a * alpha);
61
76
  }
62
77
  """
@@ -73,9 +88,16 @@ class GridLinesVisual(ImageVisual):
73
88
  color : Color
74
89
  The base color for grid lines. The final color may have its alpha
75
90
  channel modified.
91
+ grid_bounds : tuple or None
92
+ The lower and upper bound for each axis beyond which no grid is rendered.
93
+ In the form of (minx, maxx, miny, maxy).
94
+ border_width : float
95
+ Tickness of the border rendered at the bounds of the grid.
76
96
  """
77
97
 
78
- def __init__(self, scale=(1, 1), color='w'):
98
+ def __init__(self, scale=(1, 1), color='w',
99
+ grid_bounds=None,
100
+ border_width=2):
79
101
  # todo: PlaneVisual should support subdivide/impostor methods from
80
102
  # image and gridlines should inherit from plane instead.
81
103
  self._grid_color_fn = Function(_GRID_COLOR)
@@ -86,6 +108,40 @@ class GridLinesVisual(ImageVisual):
86
108
  self.shared_program.frag['get_data'] = self._grid_color_fn
87
109
  cfun = Function('vec4 null(vec4 x) { return x; }')
88
110
  self.shared_program.frag['color_transform'] = cfun
111
+ self.unfreeze()
112
+ self.grid_bounds = grid_bounds
113
+ self.border_width = border_width
114
+ self.freeze()
115
+
116
+ @property
117
+ def grid_bounds(self):
118
+ return self._grid_bounds
119
+
120
+ @grid_bounds.setter
121
+ def grid_bounds(self, value):
122
+ if value is None:
123
+ value = (None,) * 4
124
+ grid_bounds = []
125
+ for i, v in enumerate(value):
126
+ if v is None:
127
+ if i % 2:
128
+ v = -np.inf
129
+ else:
130
+ v = np.inf
131
+ grid_bounds.append(v)
132
+ self.shared_program['u_gridlines_bounds'] = value
133
+ self._grid_bounds = grid_bounds
134
+ self.update()
135
+
136
+ @property
137
+ def border_width(self):
138
+ return self._border_width
139
+
140
+ @border_width.setter
141
+ def border_width(self, value):
142
+ self.shared_program['u_border_width'] = value
143
+ self._border_width = value
144
+ self.update()
89
145
 
90
146
  @property
91
147
  def size(self):
vispy/visuals/image.py CHANGED
@@ -16,6 +16,7 @@ from .transforms import NullTransform
16
16
  from .visual import Visual
17
17
  from ..io import load_spatial_filters
18
18
  from ._scalable_textures import CPUScaledTexture2D, GPUScaledTexture2D
19
+ from ..util import np_copy_if_needed
19
20
 
20
21
 
21
22
  _VERTEX_SHADER = """
@@ -90,11 +91,9 @@ _TEXTURE_LOOKUP = """
90
91
 
91
92
  _APPLY_CLIM_FLOAT = """
92
93
  float apply_clim(float data) {
93
- // If data is NaN, don't draw it at all
94
- // http://stackoverflow.com/questions/11810158/how-to-deal-with-nan-or-inf-in-opengl-es-2-0-shaders
95
- if (!(data <= 0.0 || 0.0 <= data)) {
96
- discard;
97
- }
94
+ // pass through NaN values to get handled by the colormap
95
+ if (!(data <= 0.0 || 0.0 <= data)) return data;
96
+
98
97
  data = clamp(data, min($clim.x, $clim.y), max($clim.x, $clim.y));
99
98
  data = (data - $clim.x) / ($clim.y - $clim.x);
100
99
  return data;
@@ -102,7 +101,7 @@ _APPLY_CLIM_FLOAT = """
102
101
 
103
102
  _APPLY_CLIM = """
104
103
  vec4 apply_clim(vec4 color) {
105
- // Handle NaN values
104
+ // Handle NaN values (clamp them to the minimum value)
106
105
  // http://stackoverflow.com/questions/11810158/how-to-deal-with-nan-or-inf-in-opengl-es-2-0-shaders
107
106
  color.r = !(color.r <= 0.0 || 0.0 <= color.r) ? min($clim.x, $clim.y) : color.r;
108
107
  color.g = !(color.g <= 0.0 || 0.0 <= color.g) ? min($clim.x, $clim.y) : color.g;
@@ -116,6 +115,9 @@ _APPLY_CLIM = """
116
115
 
117
116
  _APPLY_GAMMA_FLOAT = """
118
117
  float apply_gamma(float data) {
118
+ // pass through NaN values to get handled by the colormap
119
+ if (!(data <= 0.0 || 0.0 <= data)) return data;
120
+
119
121
  return pow(data, $gamma);
120
122
  }"""
121
123
 
@@ -128,7 +130,7 @@ _APPLY_GAMMA = """
128
130
 
129
131
  _NULL_COLOR_TRANSFORM = 'vec4 pass(vec4 color) { return color; }'
130
132
 
131
- _C2L_RED = 'float cmap(vec4 color) { return color.r; }'
133
+ _C2L_RED = 'float color_to_luminance(vec4 color) { return color.r; }'
132
134
 
133
135
  _CUSTOM_FILTER = """
134
136
  vec4 texture_lookup(vec2 texcoord) {
@@ -380,7 +382,7 @@ class ImageVisual(Visual):
380
382
  texture_format : str or None
381
383
 
382
384
  """
383
- data = np.array(image, copy=copy)
385
+ data = np.array(image, copy=copy or np_copy_if_needed)
384
386
  if np.iscomplexobj(data):
385
387
  raise TypeError(
386
388
  "Complex data types not supported. Please use 'ComplexImage' instead"
@@ -455,6 +457,17 @@ class ImageVisual(Visual):
455
457
  self.shared_program.frag['color_transform'][2]['gamma'] = self._gamma
456
458
  self.update()
457
459
 
460
+ @property
461
+ def bad_color(self):
462
+ """Color used to render NaN values."""
463
+ return self._cmap.get_bad_color()
464
+
465
+ @bad_color.setter
466
+ def bad_color(self, color):
467
+ self._cmap.set_bad_color(color)
468
+ self._need_colortransform_update = True
469
+ self.update()
470
+
458
471
  @property
459
472
  def method(self):
460
473
  """Get rendering method name."""
@@ -126,7 +126,7 @@ class SurfacePlotVisual(MeshVisual):
126
126
  # convert (width, height, 4) to (num_verts, 4)
127
127
  vert_shape = self.__vertices.shape
128
128
  num_vertices = vert_shape[0] * vert_shape[1]
129
- colors = colors.reshape(num_vertices, 3)
129
+ colors = colors.reshape(num_vertices, colors.shape[-1])
130
130
  return colors
131
131
 
132
132
  def set_data(self, x=None, y=None, z=None, colors=None):
@@ -0,0 +1,30 @@
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
+
5
+ """
6
+ Tests for GridLines
7
+ """
8
+
9
+ import numpy as np
10
+
11
+ from vispy.scene import GridLines, STTransform
12
+ from vispy.testing import (requires_application, TestingCanvas,
13
+ run_tests_if_main)
14
+
15
+
16
+ @requires_application()
17
+ def test_gridlines():
18
+ with TestingCanvas(size=(80, 80)) as c:
19
+ grid = GridLines(parent=c.scene)
20
+ grid.transform = STTransform(translate=(40, 40))
21
+ render = c.render()
22
+ np.testing.assert_array_equal(render[40, 40], (151, 151, 151, 255))
23
+ np.testing.assert_array_equal(render[50, 50], (0, 0, 0, 255))
24
+
25
+ grid.grid_bounds = (-10, 10, -10, 10)
26
+ render = c.render()
27
+ np.testing.assert_array_equal(render[50, 50], (255, 255, 255, 255))
28
+
29
+
30
+ run_tests_if_main()
@@ -128,28 +128,30 @@ def test_image_clims_and_gamma(input_dtype, texture_format, num_channels,
128
128
  @pytest.mark.xfail(IS_CI, reason="CI environments sometimes treat NaN as 0")
129
129
  @requires_application()
130
130
  @pytest.mark.parametrize('texture_format', [None, 'auto'])
131
- def test_image_nan_single_band(texture_format):
132
- size = (40, 40)
133
- data = np.ones((40, 40))
134
- data[:5, :5] = np.nan
135
- data[:5, -5:] = 0
131
+ @pytest.mark.parametrize('bad_color', [None, (1, 0, 0, 1)])
132
+ def test_image_nan(texture_format, bad_color):
133
+ size = (80, 80)
134
+ data = np.ones(size)
135
+ data[:20, :20] = np.nan
136
+ data[-20: -20:] = 0
136
137
 
137
- expected = (np.ones((40, 40, 4)) * 255).astype(np.uint8)
138
- # black square
139
- expected[:5, -5:, :3] = 0
138
+ expected = (np.ones(size + (4,)) * 255).astype(np.uint8)
139
+ expected[-20: -20:] = (0, 0, 0, 225)
140
140
  if texture_format is None:
141
141
  # CPU scaling's NaNs get converted to 0s
142
- expected[:5, :5, :3] = 0
142
+ expected[:20, :20] = (0, 0, 0, 255)
143
143
  else:
144
144
  # GPU receives NaNs
145
- # nan - transparent square
146
- expected[:5, :5, 0] = 0
147
- expected[:5, :5, 1] = 255 # match the 'green' background
148
- expected[:5, :5, 2] = 0
145
+ # nan - mapped to bad color
146
+ if bad_color is None:
147
+ # no bad color means transparent, so we should see the green canvas
148
+ bad_color = (0, 1, 0, 1)
149
+ expected[:20, :20] = np.array(bad_color) * 255
149
150
 
150
151
  with TestingCanvas(size=size[::-1], bgcolor=(0, 1, 0)) as c:
151
- Image(data, cmap='grays',
152
+ image = Image(data, cmap='grays', clim=(0, 1),
152
153
  texture_format=texture_format, parent=c.scene)
154
+ image.bad_color = bad_color
153
155
  rendered = c.render()
154
156
  np.testing.assert_allclose(rendered, expected)
155
157
 
@@ -327,7 +329,7 @@ def test_image_interpolation():
327
329
  assert np.allclose(render[center_left], black)
328
330
  assert np.allclose(render[center_right], white)
329
331
 
330
- image.interpolation = 'bilinear'
332
+ image.interpolation = 'linear'
331
333
  render = c.render()
332
334
  assert np.allclose(render[left], black)
333
335
  assert np.allclose(render[right], white)
@@ -133,6 +133,22 @@ def test_clim_handling_cpu():
133
133
  assert st.clim == (5, 25)
134
134
  assert st.clim_normalized == (0, 1)
135
135
 
136
+ # u8 auto -> f32 auto
137
+ st = CPUScaledStub()
138
+ st.set_clim("auto")
139
+ assert st.clim == "auto"
140
+ st.scale_and_set_data(ref_data.astype(np.uint8))
141
+ assert st.clim == (5, 25)
142
+ assert st.clim_normalized == (0, 1)
143
+ # set new data with an out-of-range value
144
+ # it should clip at the limits of the original data type
145
+ st.set_clim("auto")
146
+ assert st.clim == "auto"
147
+ new_data = np.array([[10, 10, 5], [15, 2048, 15]], dtype=np.float32)
148
+ st.scale_and_set_data(new_data)
149
+ assert st.clim == (5, 255)
150
+ assert st.clim_normalized == (0, 1)
151
+
136
152
 
137
153
  def test_clim_handling_gpu():
138
154
  ref_data = np.array([[10, 10, 5], [15, 25, 15]])
@@ -12,7 +12,8 @@ import pytest
12
12
  @requires_application()
13
13
  @pytest.mark.parametrize('x1dim', [True, False])
14
14
  @pytest.mark.parametrize('y1dim', [True, False])
15
- def test_surface_plot(x1dim:bool, y1dim:bool):
15
+ @pytest.mark.parametrize('use_vertex_colors', [True, False])
16
+ def test_surface_plot(x1dim:bool, y1dim:bool, use_vertex_colors:bool):
16
17
  """Test SurfacePlot visual"""
17
18
  with TestingCanvas(bgcolor='w') as c:
18
19
 
@@ -29,7 +30,8 @@ def test_surface_plot(x1dim:bool, y1dim:bool):
29
30
  # color vertices
30
31
  cnorm = z / abs(np.amax(z))
31
32
  colormap = get_colormap("viridis").map(cnorm)
32
- colormap.reshape(z.shape + (-1,))
33
+ if not use_vertex_colors:
34
+ colormap = colormap.reshape(z.shape + (-1,))
33
35
 
34
36
  # 1 or 2 dimensional x and y data
35
37
  x_input = x if x1dim else xv
@@ -41,7 +43,10 @@ def test_surface_plot(x1dim:bool, y1dim:bool):
41
43
  y=y_input,
42
44
  shading=None)
43
45
 
44
- surface.mesh_data.set_vertex_colors(colormap)
46
+ if use_vertex_colors:
47
+ surface.mesh_data.set_vertex_colors(colormap)
48
+ else:
49
+ surface.set_data(colors=colormap)
45
50
 
46
51
  # c.draw_visual(surface)
47
52
  view.add(surface)
@@ -7,6 +7,8 @@ cimport numpy as np
7
7
  from libc.math cimport sqrt
8
8
  cimport cython
9
9
 
10
+ np.import_array()
11
+
10
12
  __all__ = ['_get_distance_field']
11
13
 
12
14
  dtype = np.float32
vispy/visuals/volume.py CHANGED
@@ -417,8 +417,8 @@ _ATTENUATED_MIP_SNIPPETS = dict(
417
417
  // Scale and clamp accumulation in `sumval` by contrast limits so that:
418
418
  // * attenuation value does not depend on data values
419
419
  // * negative values do not amplify instead of attenuate
420
- sumval = sumval + clamp((val - clim.x) / (clim.y - clim.x), 0.0, 1.0);
421
- scale = exp(-u_attenuation * (sumval - 1) / u_relative_step_size);
420
+ sumval = sumval + u_relative_step_size * clamp((val - clim.x) / (clim.y - clim.x), 0.0, 1.0);
421
+ scale = exp(-u_attenuation * (sumval - 1));
422
422
  if( maxval > scale * clim.y ) {
423
423
  // stop if no chance of finding a higher maxval
424
424
  iter = nsteps;
@@ -1165,9 +1165,40 @@ class VolumeVisual(Visual):
1165
1165
 
1166
1166
  @relative_step_size.setter
1167
1167
  def relative_step_size(self, value):
1168
+ """Set the relative step size used during raycasting.
1169
+
1170
+ Very small values give increased detail when rendering volumes with
1171
+ few voxels, but values that are too small give worse performance
1172
+ (framerate), in extreme cases causing a GPU hang and for the process
1173
+ to be killed by the OS. See discussion at:
1174
+
1175
+ https://github.com/vispy/vispy/pull/2587
1176
+
1177
+ For this reason, this setter issues a warning when the value is
1178
+ smaller than ``side_len / (2 * MAX_CANVAS_SIZE)``, where ``side_len``
1179
+ is the smallest side of the volume and ``MAX_CANVAS_SIZE`` is what
1180
+ we consider to be the largest likely monitor resolution along its
1181
+ longest side: 7680 pixels, equivalent to an 8K monitor.
1182
+
1183
+ This setter also raises a ValueError when the value is 0 or negative.
1184
+ """
1168
1185
  value = float(value)
1169
- if value < 0.1:
1170
- raise ValueError('relative_step_size cannot be smaller than 0.1')
1186
+ side_len = np.min(self._vol_shape)
1187
+ MAX_CANVAS_SIZE = 7680
1188
+ minimum_val = side_len / (2 * MAX_CANVAS_SIZE)
1189
+ if value <= 0:
1190
+ raise ValueError('relative_step_size cannot be 0 or negative.')
1191
+ elif value < minimum_val:
1192
+ warnings.warn(
1193
+ f'To display a volume of shape {self._vol_shape} without '
1194
+ f'artifacts, you need a step size no smaller than {side_len} /'
1195
+ f'(2 * {MAX_CANVAS_SIZE}) = {minimum_val:,.3g}. To prevent '
1196
+ 'extreme degradation in rendering performance, the provided '
1197
+ f'value of {value} is being clipped to {minimum_val:,.3g}. If '
1198
+ 'you believe you need a smaller step size, please raise an '
1199
+ 'issue at https://github.com/vispy/vispy/issues.'
1200
+ )
1201
+ value = minimum_val
1171
1202
  self._relative_step_size = value
1172
1203
  self.shared_program['u_relative_step_size'] = value
1173
1204
 
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: vispy
3
- Version: 0.14.2
3
+ Version: 0.15.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
@@ -19,13 +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.8
23
22
  Classifier: Programming Language :: Python :: 3.9
24
23
  Classifier: Programming Language :: Python :: 3.10
25
24
  Classifier: Programming Language :: Python :: 3.11
25
+ Classifier: Programming Language :: Python :: 3.12
26
26
  Classifier: Framework :: IPython
27
27
  Provides: vispy
28
- Requires-Python: >=3.8
28
+ Requires-Python: >=3.9
29
29
  Description-Content-Type: text/x-rst
30
30
  License-File: LICENSE.txt
31
31
  Requires-Dist: numpy
@@ -33,38 +33,63 @@ Requires-Dist: freetype-py
33
33
  Requires-Dist: hsluv
34
34
  Requires-Dist: kiwisolver
35
35
  Requires-Dist: packaging
36
- Provides-Extra: doc
37
- Requires-Dist: pydata-sphinx-theme ; extra == 'doc'
38
- Requires-Dist: numpydoc ; extra == 'doc'
39
- Requires-Dist: sphinxcontrib-apidoc ; extra == 'doc'
40
- Requires-Dist: sphinx-gallery ; extra == 'doc'
41
- Requires-Dist: myst-parser ; extra == 'doc'
42
- Requires-Dist: pillow ; extra == 'doc'
43
- Requires-Dist: pytest ; extra == 'doc'
44
- Requires-Dist: pyopengl ; extra == 'doc'
45
- Provides-Extra: io
46
- Requires-Dist: meshio ; extra == 'io'
47
- Requires-Dist: Pillow ; extra == 'io'
48
36
  Provides-Extra: ipython-static
49
- Requires-Dist: ipython ; extra == 'ipython-static'
37
+ Requires-Dist: ipython; extra == "ipython-static"
50
38
  Provides-Extra: pyglet
51
- Requires-Dist: pyglet >=1.2 ; extra == 'pyglet'
39
+ Requires-Dist: pyglet>=1.2; extra == "pyglet"
52
40
  Provides-Extra: pyqt5
53
- Requires-Dist: pyqt5 ; extra == 'pyqt5'
41
+ Requires-Dist: pyqt5; extra == "pyqt5"
54
42
  Provides-Extra: pyqt6
55
- Requires-Dist: pyqt6 ; extra == 'pyqt6'
43
+ Requires-Dist: pyqt6; extra == "pyqt6"
56
44
  Provides-Extra: pyside
57
- Requires-Dist: PySide ; extra == 'pyside'
45
+ Requires-Dist: PySide; extra == "pyside"
58
46
  Provides-Extra: pyside2
59
- Requires-Dist: PySide2 ; extra == 'pyside2'
47
+ Requires-Dist: PySide2; extra == "pyside2"
60
48
  Provides-Extra: pyside6
61
- Requires-Dist: PySide6 ; extra == 'pyside6'
49
+ Requires-Dist: PySide6; extra == "pyside6"
50
+ Provides-Extra: glfw
51
+ Requires-Dist: glfw; extra == "glfw"
62
52
  Provides-Extra: sdl2
63
- Requires-Dist: PySDL2 ; extra == 'sdl2'
64
- Provides-Extra: tk
65
- Requires-Dist: pyopengltk ; extra == 'tk'
53
+ Requires-Dist: PySDL2; extra == "sdl2"
66
54
  Provides-Extra: wx
67
- Requires-Dist: wxPython ; extra == 'wx'
55
+ Requires-Dist: wxPython; extra == "wx"
56
+ Provides-Extra: tk
57
+ Requires-Dist: pyopengltk; extra == "tk"
58
+ Provides-Extra: doc
59
+ Requires-Dist: pydata-sphinx-theme; extra == "doc"
60
+ Requires-Dist: numpydoc; extra == "doc"
61
+ Requires-Dist: sphinxcontrib-apidoc; extra == "doc"
62
+ Requires-Dist: sphinx-gallery; extra == "doc"
63
+ Requires-Dist: myst-parser; extra == "doc"
64
+ Requires-Dist: pillow; extra == "doc"
65
+ Requires-Dist: pytest; extra == "doc"
66
+ Requires-Dist: pyopengl; extra == "doc"
67
+ Provides-Extra: io
68
+ Requires-Dist: meshio; extra == "io"
69
+ Requires-Dist: Pillow; extra == "io"
70
+ Provides-Extra: test
71
+ Requires-Dist: pytest; extra == "test"
72
+ Requires-Dist: pytest-sugar; extra == "test"
73
+ Requires-Dist: meshio; extra == "test"
74
+ Requires-Dist: pillow; extra == "test"
75
+ Requires-Dist: sphinx_gallery; extra == "test"
76
+ Requires-Dist: imageio; extra == "test"
77
+ Dynamic: author
78
+ Dynamic: author-email
79
+ Dynamic: classifier
80
+ Dynamic: description
81
+ Dynamic: description-content-type
82
+ Dynamic: download-url
83
+ Dynamic: home-page
84
+ Dynamic: keywords
85
+ Dynamic: license
86
+ Dynamic: license-file
87
+ Dynamic: platform
88
+ Dynamic: provides
89
+ Dynamic: provides-extra
90
+ Dynamic: requires-dist
91
+ Dynamic: requires-python
92
+ Dynamic: summary
68
93
 
69
94
  VisPy: interactive scientific visualization in Python
70
95
  -----------------------------------------------------
@@ -205,7 +230,7 @@ External links
205
230
  - `Chat room <https://gitter.im/vispy/vispy>`__
206
231
  - `Developer chat room <https://gitter.im/vispy/vispy-dev>`__
207
232
  - `Wiki <http://github.com/vispy/vispy/wiki>`__
208
- - `Gallery <http://vispy.org/gallery.html>`__
233
+ - `Gallery <http://vispy.org/gallery/index.html>`__
209
234
  - `Documentation <http://vispy.readthedocs.org>`__
210
235
 
211
236
  .. |Build Status| image:: https://github.com/vispy/vispy/workflows/CI/badge.svg