vispy 0.12.1__cp310-cp310-win_amd64.whl → 0.14.0__cp310-cp310-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.

Files changed (43) hide show
  1. vispy/app/backends/_glfw.py +1 -1
  2. vispy/app/backends/_qt.py +78 -53
  3. vispy/color/color_array.py +8 -1
  4. vispy/color/colormap.py +4 -24
  5. vispy/geometry/meshdata.py +76 -38
  6. vispy/geometry/tests/test_meshdata.py +72 -0
  7. vispy/gloo/gl/_constants.py +3 -3
  8. vispy/gloo/program.py +1 -1
  9. vispy/gloo/texture.py +9 -4
  10. vispy/scene/cameras/base_camera.py +4 -0
  11. vispy/scene/cameras/panzoom.py +4 -1
  12. vispy/scene/cameras/perspective.py +6 -0
  13. vispy/scene/cameras/tests/test_perspective.py +37 -0
  14. vispy/scene/canvas.py +9 -5
  15. vispy/scene/events.py +9 -0
  16. vispy/scene/node.py +18 -1
  17. vispy/scene/tests/test_visuals.py +113 -0
  18. vispy/scene/visuals.py +5 -1
  19. vispy/util/gallery_scraper.py +8 -0
  20. vispy/util/tests/test_gallery_scraper.py +2 -0
  21. vispy/version.py +2 -3
  22. vispy/visuals/__init__.py +1 -0
  23. vispy/visuals/filters/__init__.py +3 -2
  24. vispy/visuals/filters/base_filter.py +120 -0
  25. vispy/visuals/filters/markers.py +28 -0
  26. vispy/visuals/filters/mesh.py +61 -6
  27. vispy/visuals/filters/tests/test_primitive_picking_filters.py +70 -0
  28. vispy/visuals/instanced_mesh.py +152 -0
  29. vispy/visuals/line/dash_atlas.py +46 -41
  30. vispy/visuals/markers.py +49 -24
  31. vispy/visuals/mesh.py +2 -2
  32. vispy/visuals/tests/test_instanced_mesh.py +50 -0
  33. vispy/visuals/tests/test_mesh.py +17 -0
  34. vispy/visuals/text/_sdf_cpu.cp310-win_amd64.pyd +0 -0
  35. vispy/visuals/text/_sdf_cpu.pyx +21 -23
  36. vispy/visuals/tube.py +1 -1
  37. vispy/visuals/visual.py +142 -1
  38. vispy/visuals/volume.py +19 -10
  39. {vispy-0.12.1.dist-info → vispy-0.14.0.dist-info}/LICENSE.txt +1 -1
  40. {vispy-0.12.1.dist-info → vispy-0.14.0.dist-info}/METADATA +7 -6
  41. {vispy-0.12.1.dist-info → vispy-0.14.0.dist-info}/RECORD +43 -39
  42. {vispy-0.12.1.dist-info → vispy-0.14.0.dist-info}/WHEEL +1 -1
  43. {vispy-0.12.1.dist-info → vispy-0.14.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,152 @@
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
+
7
+ """An instanced version of MeshVisual with arbitrary shifts, transforms, and colors."""
8
+
9
+ from __future__ import division
10
+
11
+ import numpy as np
12
+
13
+ from ..gloo import VertexBuffer
14
+ from ..gloo.texture import downcast_to_32bit_if_needed
15
+ from ..color import ColorArray
16
+ from .filters import InstancedShadingFilter
17
+ from .shaders import Variable
18
+
19
+ from .mesh import MeshVisual
20
+
21
+
22
+ _VERTEX_SHADER = """
23
+ uniform bool use_instance_colors;
24
+
25
+ // these attributes will be defined on an instance basis
26
+ attribute vec3 shift;
27
+ attribute vec3 transform_x;
28
+ attribute vec3 transform_y;
29
+ attribute vec3 transform_z;
30
+
31
+ varying vec4 v_base_color;
32
+ void main() {
33
+
34
+ v_base_color = $color_transform($base_color);
35
+
36
+ // transform is generated from column vectors (new basis vectors)
37
+ // https://en.wikibooks.org/wiki/GLSL_Programming/Vector_and_Matrix_Operations#Constructors
38
+ mat3 instance_transform = mat3(transform_x, transform_y, transform_z);
39
+ vec3 pos_rotated = instance_transform * $to_vec4($position).xyz;
40
+ vec4 pos_shifted = $to_vec4(pos_rotated + shift);
41
+ gl_Position = $transform(pos_shifted);
42
+ }
43
+ """
44
+
45
+
46
+ class InstancedMeshVisual(MeshVisual):
47
+ """Instanced Mesh visual.
48
+
49
+ Mostly identical to MeshVisual, but additionally takes arrays of
50
+ of positions and transforms (optionally colors) to create multiple
51
+ instances of the mesh.
52
+
53
+ Instancing is a rendering technique that re-uses the same mesh data
54
+ by applying transformations to vertices and vertex data or textures,
55
+ wich can drastically improve performance compared to having many
56
+ simple MeshVisuals.
57
+
58
+ Parameters
59
+ ----------
60
+ instance_positions : (I, 3) array
61
+ Coordinates for each instance of the mesh.
62
+ instance_transforms : (I, 3, 3) array
63
+ Matrices for the transforms to apply to each instance.
64
+ instance_colors : ColorArray
65
+ Matrices of colors for each instance. Colors
66
+ *args : list
67
+ Positional arguments to pass to :class:`~vispy.visuals.mesh.MeshVisual`.
68
+ **kwargs : dict
69
+ Keyword arguments to pass to :class:`~vispy.visuals.mesh.MeshVisual`.
70
+
71
+ Examples
72
+ --------
73
+ See example `scene/instanced_mesh_visual.py` in the gallery.
74
+ """
75
+
76
+ _shaders = {
77
+ 'vertex': _VERTEX_SHADER,
78
+ 'fragment': MeshVisual._shaders['fragment'],
79
+ }
80
+
81
+ _shading_filter_class = InstancedShadingFilter
82
+
83
+ def __init__(self, *args, instance_positions, instance_transforms, instance_colors=None, **kwargs):
84
+ self._instance_positions = None
85
+ self._instance_positions_vbo = None
86
+ self._instance_transforms = None
87
+ self._instance_transforms_vbos = None
88
+ self._instance_colors = None
89
+ self._instance_colors_vbo = None
90
+ super().__init__(*args, **kwargs)
91
+ self.instance_positions = instance_positions
92
+ self.instance_transforms = instance_transforms
93
+ self.instance_colors = instance_colors
94
+
95
+ @property
96
+ def instance_positions(self):
97
+ return self._instance_positions
98
+
99
+ @instance_positions.setter
100
+ def instance_positions(self, pos):
101
+ pos = np.reshape(pos, (-1, 3))
102
+ if pos.ndim != 2 or pos.shape[-1] != 3:
103
+ raise ValueError(f'positions must be 3D coordinates, but provided data has shape {pos.shape}')
104
+ self._instance_positions = downcast_to_32bit_if_needed(pos, dtype=np.float32)
105
+ self._instance_positions_vbo = VertexBuffer(self._instance_positions, divisor=1)
106
+ self.mesh_data_changed()
107
+
108
+ @property
109
+ def instance_transforms(self):
110
+ return self._instance_transforms
111
+
112
+ @instance_transforms.setter
113
+ def instance_transforms(self, matrix):
114
+ matrix = np.reshape(matrix, (-1, 3, 3))
115
+ if matrix.ndim != 3 or matrix.shape[1:] != (3, 3):
116
+ raise ValueError(f'transforms must be an array of 3x3 matrices, but provided data has shape {matrix.shape}')
117
+ self._instance_transforms = downcast_to_32bit_if_needed(matrix, dtype=np.float32)
118
+ # copy if not c contiguous
119
+ self._instance_transforms_vbos = (
120
+ VertexBuffer(np.ascontiguousarray(self._instance_transforms[..., 0]), divisor=1),
121
+ VertexBuffer(np.ascontiguousarray(self._instance_transforms[..., 1]), divisor=1),
122
+ VertexBuffer(np.ascontiguousarray(self._instance_transforms[..., 2]), divisor=1),
123
+ )
124
+ self.mesh_data_changed()
125
+
126
+ @property
127
+ def instance_colors(self):
128
+ return self._instance_colors
129
+
130
+ @instance_colors.setter
131
+ def instance_colors(self, colors):
132
+ if colors is not None:
133
+ colors = ColorArray(colors)
134
+ self._instance_colors_vbo = VertexBuffer(colors.rgba, divisor=1)
135
+ else:
136
+ self._instance_colors_vbo = Variable('base_color', self._color.rgba)
137
+
138
+ self._instance_colors = colors
139
+ self.mesh_data_changed()
140
+
141
+ def _update_data(self):
142
+ with self.events.data_updated.blocker():
143
+ super()._update_data()
144
+
145
+ # set instance buffers
146
+ self.shared_program.vert['base_color'] = self._instance_colors_vbo
147
+ self.shared_program['transform_x'] = self._instance_transforms_vbos[0]
148
+ self.shared_program['transform_y'] = self._instance_transforms_vbos[1]
149
+ self.shared_program['transform_z'] = self._instance_transforms_vbos[2]
150
+ self.shared_program['shift'] = self._instance_positions_vbo
151
+
152
+ self.events.data_updated()
@@ -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,6 +11,7 @@ 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 = """
@@ -43,28 +44,27 @@ void main (void) {
43
44
 
44
45
  vec4 pos = vec4(a_position, 1);
45
46
  vec4 fb_pos = $visual_to_framebuffer(pos);
47
+ vec4 x;
48
+ vec4 size_vec;
46
49
  gl_Position = $framebuffer_to_render(fb_pos);
47
50
 
48
51
  // NOTE: gl_stuff uses framebuffer coords!
49
-
50
- if (u_scaling == true) {
51
- // calculate point size from visual to framebuffer coords to determine size
52
- // move horizontally in framebuffer space
53
- // then go to scene coordinates (not visual, so scaling is accounted for)
54
- vec4 x = $framebuffer_to_scene(fb_pos + vec4(big_float, 0, 0, 0));
55
- // subtract position, so we get the scene-coordinate vector describing
56
- // an "horizontal direction parallel to the screen"
57
- vec4 scene_pos = $framebuffer_to_scene(fb_pos);
58
- x = (x - scene_pos);
59
- // multiply that direction by the size (in scene space) and add it to the position
52
+ if (u_scaling) {
53
+ // scaling == "scene": scale marker using entire visual -> framebuffer set of transforms
54
+ // scaling == "visual": scale marker using only the Visual's transform
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
60
59
  // this gives us the position of the edge of the point, which we convert in screen space
61
- vec4 size_vec = $scene_to_framebuffer(scene_pos + normalize(x) * a_size);
60
+ size_vec = $scene_or_visual_to_framebuffer(pos + normalize(x) * a_size);
62
61
  // divide by `w` for perspective, and subtract pos
63
62
  // this gives us the actual screen-space size of the point
64
63
  $v_size = size_vec.x / size_vec.w - fb_pos.x / fb_pos.w;
65
64
  v_edgewidth = ($v_size / a_size) * a_edgewidth;
66
65
  }
67
66
  else {
67
+ // scaling == "fixed": marker is always the same number of pixels
68
68
  $v_size = a_size * u_px_scale;
69
69
  v_edgewidth = a_edgewidth * u_px_scale;
70
70
  }
@@ -73,12 +73,13 @@ void main (void) {
73
73
  gl_PointSize = $v_size + 4. * (v_edgewidth + 1.5 * u_antialias);
74
74
 
75
75
  if (u_spherical == true) {
76
+ // similar as above for scaling, but in towards the screen direction
76
77
  // Get the framebuffer z direction relative to this sphere in visual coords
77
- 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));
78
79
  z = (z - pos);
79
80
  // Get the depth of the sphere in its middle point on the screen
80
81
  // size/2 because we need the radius, not the diameter
81
- 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);
82
83
  v_depth_middle = depth_z_vec.z / depth_z_vec.w - fb_pos.z / fb_pos.w;
83
84
  // size ratio between aliased and non-aliased, needed for correct depth
84
85
  v_alias_ratio = gl_PointSize / $v_size;
@@ -505,8 +506,20 @@ class MarkersVisual(Visual):
505
506
  The color used to draw each symbol interior.
506
507
  symbol : str or array
507
508
  The style of symbol used to draw each marker (see Notes).
508
- scaling : bool
509
- If set to True, marker scales when rezooming.
509
+ scaling : str | bool
510
+ Scaling method of individual markers. If set to "fixed" (default) then
511
+ no scaling is done and markers will always be the same number of
512
+ pixels on the screen. If set to "scene" then the chain of transforms
513
+ from the Visual's transform to the transform mapping to the OpenGL
514
+ framebuffer are used to scaling the marker. This has the effect of the
515
+ marker staying the same size in the "scene" coordinate space and
516
+ changing size as the visualization is zoomed in and out. If set to
517
+ "visual" the marker is scaled only using the transform of the Visual
518
+ and not the rest of the scene/camera. This means that something like
519
+ a camera changing the view will not affect the size of the marker, but
520
+ the user can still scale it using the Visual's transform. For
521
+ backwards compatibility this can be set to the boolean ``False`` for
522
+ "fixed" or ``True`` for "scene".
510
523
  alpha : float
511
524
  The opacity level of the visual.
512
525
  antialias : float
@@ -534,10 +547,11 @@ class MarkersVisual(Visual):
534
547
  _symbol_shader_values = symbol_shader_values
535
548
  _symbol_shader = symbol_func
536
549
 
537
- def __init__(self, scaling=False, alpha=1, antialias=1, spherical=False,
550
+ def __init__(self, scaling="fixed", alpha=1, antialias=1, spherical=False,
538
551
  light_color='white', light_position=(1, -1, 1), light_ambient=0.3, **kwargs):
539
552
  self._vbo = VertexBuffer()
540
553
  self._data = None
554
+ self._scaling = "fixed"
541
555
 
542
556
  Visual.__init__(self, vcode=self._shaders['vertex'], fcode=self._shaders['fragment'])
543
557
  self._symbol_func = Function(self._symbol_shader)
@@ -551,6 +565,8 @@ class MarkersVisual(Visual):
551
565
  blend_func=('src_alpha', 'one_minus_src_alpha'))
552
566
  self._draw_mode = 'points'
553
567
 
568
+ self.events.add(data_updated=Event)
569
+
554
570
  if len(kwargs) > 0:
555
571
  self.set_data(**kwargs)
556
572
 
@@ -638,6 +654,7 @@ class MarkersVisual(Visual):
638
654
  self._vbo.set_data(data)
639
655
  self.shared_program.bind(self._vbo)
640
656
 
657
+ self.events.data_updated()
641
658
  self.update()
642
659
 
643
660
  @property
@@ -677,9 +694,18 @@ class MarkersVisual(Visual):
677
694
 
678
695
  @scaling.setter
679
696
  def scaling(self, value):
680
- value = bool(value)
681
- self.shared_program['u_scaling'] = value
682
- self._scaling = value
697
+ scaling_modes = {
698
+ False: "fixed",
699
+ True: "scene",
700
+ "fixed": "fixed",
701
+ "scene": "scene",
702
+ "visual": "visual",
703
+ }
704
+ if value not in scaling_modes:
705
+ possible_options = ", ".join(repr(opt) for opt in scaling_modes)
706
+ raise ValueError(f"Unknown scaling option {value!r}, expected one of: {possible_options}")
707
+ self._scaling = scaling_modes[value]
708
+ self.shared_program['u_scaling'] = self._scaling != "fixed"
683
709
  self.update()
684
710
 
685
711
  @property
@@ -764,16 +790,15 @@ class MarkersVisual(Visual):
764
790
 
765
791
  def _prepare_transforms(self, view):
766
792
  view.view_program.vert['visual_to_framebuffer'] = view.get_transform('visual', 'framebuffer')
767
- view.view_program.vert['framebuffer_to_visual'] = view.get_transform('framebuffer', 'visual')
768
793
  view.view_program.vert['framebuffer_to_render'] = view.get_transform('framebuffer', 'render')
769
- view.view_program.vert['framebuffer_to_scene'] = view.get_transform('framebuffer', 'scene')
770
- 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')
771
797
 
772
798
  def _prepare_draw(self, view):
773
799
  if self._data is None:
774
800
  return False
775
801
  view.view_program['u_px_scale'] = view.transforms.pixel_scale
776
- view.view_program['u_scaling'] = self.scaling
777
802
 
778
803
  def _compute_bounds(self, axis, view):
779
804
  pos = self._data['a_position']
vispy/visuals/mesh.py CHANGED
@@ -283,8 +283,8 @@ class MeshVisual(Visual):
283
283
  clim_func = 'float cmap(float val) { return (val - $cmin) / ($cmax - $cmin); }'
284
284
  if colors.ndim == 2 and colors.shape[1] == 1:
285
285
  fun = Function(clim_func)
286
- fun['cmin'] = self.clim[0]
287
- fun['cmax'] = self.clim[1]
286
+ fun['cmin'] = self._clim_values[0]
287
+ fun['cmax'] = self._clim_values[1]
288
288
  fun = FunctionChain(None, [fun, Function(self.cmap.glsl_map)])
289
289
  else:
290
290
  fun = Function(null_color_transform)
@@ -0,0 +1,50 @@
1
+ import numpy as np
2
+ from vispy import scene, use
3
+
4
+ from vispy.testing import (TestingCanvas, requires_application,
5
+ run_tests_if_main, requires_pyopengl)
6
+
7
+
8
+ def setup_module(module):
9
+ use(gl='gl+')
10
+
11
+
12
+ def teardown_module(module):
13
+ use(gl='gl2')
14
+
15
+
16
+ @requires_pyopengl()
17
+ @requires_application()
18
+ def test_mesh_with_vertex_values():
19
+ size = (80, 60)
20
+ with TestingCanvas(size=size) as c:
21
+ use(gl='gl+')
22
+ vert = np.array([[0, 0, 0], [0, 30, 0], [40, 0, 0]])
23
+ faces = np.array([0, 1, 2])
24
+ pos = np.array([[0, 0, 0], [80, 60, 0]])
25
+ # identity and rotate 180
26
+ trans = np.array([
27
+ [
28
+ [1, 0, 0],
29
+ [0, 1, 0],
30
+ [0, 0, 1],
31
+ ],
32
+ [
33
+ [-1, 0, 0],
34
+ [0, -1, 0],
35
+ [0, 0, 1],
36
+ ],
37
+ ])
38
+ colors = ['red', 'blue']
39
+ mesh = scene.visuals.InstancedMesh(
40
+ vertices=vert, faces=faces, instance_positions=pos, instance_transforms=trans, instance_colors=colors
41
+ )
42
+ v = c.central_widget.add_view(border_width=0)
43
+ v.add(mesh)
44
+ render = c.render()
45
+ assert np.allclose(render[10, 10], (255, 0, 0, 255))
46
+ assert np.allclose(render[-10, -10], (0, 0, 255, 255))
47
+ assert np.allclose(render[30, 40], (0, 0, 0, 255))
48
+
49
+
50
+ run_tests_if_main()
@@ -31,6 +31,23 @@ def test_mesh_color():
31
31
  np.testing.assert_allclose(vertices['position'], new_vertices)
32
32
 
33
33
 
34
+ @requires_pyopengl()
35
+ @requires_application()
36
+ def test_mesh_with_vertex_values():
37
+ size = (45, 40)
38
+ with TestingCanvas(size=size) as c:
39
+ v = c.central_widget.add_view(border_width=0)
40
+ vertices = np.array([(0, 0, 0), (0, 0, 1), (1, 0, 0)], dtype=float)
41
+ faces = np.array([(0, 1, 2)])
42
+ mesh = scene.visuals.Mesh(
43
+ vertices=vertices,
44
+ faces=faces,
45
+ vertex_values=np.ones(len(vertices)),
46
+ )
47
+ v.add(mesh)
48
+ c.render()
49
+
50
+
34
51
  @requires_pyopengl()
35
52
  @requires_application()
36
53
  @pytest.mark.parametrize('shading', [None, 'flat', 'smooth'])
@@ -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/tube.py CHANGED
@@ -80,7 +80,7 @@ class TubeVisual(MeshVisual):
80
80
 
81
81
  # Add a vertex for each point on the circle
82
82
  v = np.arange(tube_points,
83
- dtype=np.float) / tube_points * 2 * np.pi
83
+ dtype=np.float32) / tube_points * 2 * np.pi
84
84
  cx = -1. * r * np.cos(v)
85
85
  cy = r * np.sin(v)
86
86
  grid[i] = (pos + cx[:, np.newaxis]*normal +