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.
- vispy/app/backends/_glfw.py +1 -1
- vispy/app/backends/_qt.py +78 -53
- vispy/color/color_array.py +8 -1
- vispy/color/colormap.py +4 -24
- vispy/geometry/meshdata.py +76 -38
- vispy/geometry/tests/test_meshdata.py +72 -0
- vispy/gloo/gl/_constants.py +3 -3
- vispy/gloo/program.py +1 -1
- vispy/gloo/texture.py +9 -4
- vispy/scene/cameras/base_camera.py +4 -0
- vispy/scene/cameras/panzoom.py +4 -1
- vispy/scene/cameras/perspective.py +6 -0
- vispy/scene/cameras/tests/test_perspective.py +37 -0
- vispy/scene/canvas.py +9 -5
- vispy/scene/events.py +9 -0
- vispy/scene/node.py +18 -1
- vispy/scene/tests/test_visuals.py +113 -0
- vispy/scene/visuals.py +5 -1
- vispy/util/gallery_scraper.py +8 -0
- vispy/util/tests/test_gallery_scraper.py +2 -0
- vispy/version.py +2 -3
- vispy/visuals/__init__.py +1 -0
- vispy/visuals/filters/__init__.py +3 -2
- vispy/visuals/filters/base_filter.py +120 -0
- vispy/visuals/filters/markers.py +28 -0
- vispy/visuals/filters/mesh.py +61 -6
- vispy/visuals/filters/tests/test_primitive_picking_filters.py +70 -0
- vispy/visuals/instanced_mesh.py +152 -0
- vispy/visuals/line/dash_atlas.py +46 -41
- vispy/visuals/markers.py +49 -24
- vispy/visuals/mesh.py +2 -2
- vispy/visuals/tests/test_instanced_mesh.py +50 -0
- vispy/visuals/tests/test_mesh.py +17 -0
- vispy/visuals/text/_sdf_cpu.cp310-win_amd64.pyd +0 -0
- vispy/visuals/text/_sdf_cpu.pyx +21 -23
- vispy/visuals/tube.py +1 -1
- vispy/visuals/visual.py +142 -1
- vispy/visuals/volume.py +19 -10
- {vispy-0.12.1.dist-info → vispy-0.14.0.dist-info}/LICENSE.txt +1 -1
- {vispy-0.12.1.dist-info → vispy-0.14.0.dist-info}/METADATA +7 -6
- {vispy-0.12.1.dist-info → vispy-0.14.0.dist-info}/RECORD +43 -39
- {vispy-0.12.1.dist-info → vispy-0.14.0.dist-info}/WHEEL +1 -1
- {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()
|
vispy/visuals/line/dash_atlas.py
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
|
-
|
|
51
|
-
//
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
//
|
|
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
|
-
|
|
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 = $
|
|
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 = $
|
|
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
|
|
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=
|
|
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
|
-
|
|
681
|
-
|
|
682
|
-
|
|
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.
|
|
770
|
-
view.view_program.vert['
|
|
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.
|
|
287
|
-
fun['cmax'] = self.
|
|
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()
|
vispy/visuals/tests/test_mesh.py
CHANGED
|
@@ -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'])
|
|
Binary file
|
vispy/visuals/text/_sdf_cpu.pyx
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
#
|
|
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]
|
|
27
|
-
cdef np.ndarray[DTYPE_ct, ndim=2]
|
|
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
|
|
35
|
+
if pixels_view[y, x] > 0:
|
|
36
36
|
g0[y, x] = MAX_VAL
|
|
37
|
-
if
|
|
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
|
-
|
|
54
|
-
if
|
|
55
|
-
|
|
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
|
-
|
|
58
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
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 +
|