def __init__(self, canvas, spike_data): self.canvas = canvas self.program = ModularProgram(self.VERTEX_SHADER, self.FRAGMENT_SHADER) self._t0 = 0 self._dt = 10 self.mouse_t = self.t0 self._interval = 1 / 30.0 self.electrode = '' self.time_scale = 1 / 200 self._vert = np.zeros((120 * 6, 2), dtype=np.float32) self._color = np.zeros(120 * 6, dtype=np.float32) self.electrodes = [] self.spikes = spike_data for tag, df in spike_data.groupby('electrode'): self.electrodes.append( FlashingSpikeElectrode(tag, df['time'].values)) self._create_vertex_data() self.paused = True self.program['a_position'] = self._vert self.program['a_color'] = self._color self.program.vert['transform'] = canvas.tr_sys.get_full_transform() self.outline = visuals.LineVisual(color=Theme.yellow) self.electrode_cols = [c for c in 'ABCDEFGHJKLM'] self._rescale_outline() self.extra_text = ''
def __init__(self, emulate3d=True): app.Canvas.__init__(self, keys='interactive', size=((W*5), (H*5))) if emulate3d: tex_cls = gloo.TextureEmulated3D else: tex_cls = gloo.Texture3D self.texture = tex_cls(img_array, interpolation='nearest', wrapping='clamp_to_edge') self.program = ModularProgram(VERT_SHADER, FRAG_SHADER) self.program.frag['sampler_type'] = self.texture.glsl_sampler_type self.program.frag['sample'] = self.texture.glsl_sample self.program['u_texture'] = self.texture self.program['i'] = 0.0 self.program.bind(gloo.VertexBuffer(data)) self.view = np.eye(4, dtype=np.float32) self.model = np.eye(4, dtype=np.float32) self.projection = np.eye(4, dtype=np.float32) self.program['u_model'] = self.model self.program['u_view'] = self.view self.projection = ortho(0, W, 0, H, -1, 1) self.program['u_projection'] = self.projection self.i = 0 gloo.set_clear_color('white') self._timer = app.Timer('auto', connect=self.on_timer, start=True) self.show()
def __init__(self, canvas, spike_data): super().__init__() self.canvas = canvas self.program = ModularProgram(self.VERTEX_SHADER, self.FRAGMENT_SHADER) self._t0 = 0 self._dt = 10 self.mouse_t = self.t0 self._interval = 1 / 30.0 self.electrode = '' self.time_scale = 1 / 200 # Each quad is drawn as two triangles, so need 6 vertices per electrode self._vert = np.zeros((self.canvas.layout.count * 6, 2), dtype=np.float32) self._color = np.zeros(self.canvas.layout.count * 6, dtype=np.float32) self.electrodes = [] self.spikes = spike_data.copy() self.spikes.electrode = self.spikes.electrode.str.extract('(\w+)\.*') for tag, df in self.spikes.groupby('electrode'): self.electrodes.append(FlashingSpikeElectrode(tag, df['time'].values)) self._create_vertex_data() self.paused = True self.program['a_position'] = self._vert self.program['a_color'] = self._color self.program.vert['transform'] = canvas.tr_sys.get_transform() self.outline = visuals.LineVisual(color=Theme.yellow) self._rescale_outline() self.extra_text = '' self.configure_transforms()
class MemoryRenderer(object): def __init__(self, sight_texture): self.sight_tex = sight_texture self.size = sight_texture.shape[:2] self.memory_tex = vispy.gloo.Texture2D(shape=self.size + (4, ), format='rgba', interpolation='linear', wrapping='repeat') self.fbo = vispy.gloo.FrameBuffer(color=self.texture, depth=vispy.gloo.RenderBuffer( self.size)) vert = """ #version 330 compatibility in vec2 ij; void main (void) { gl_Position = vec4(ij, 0, 1); } """ frag = """ #version 330 compatibility void main (void) { gl_FragColor = vec4(0, 0, 1, 1); } """ self.program = ModularProgram(vert, frag, gcode=geom) self.program['opacity'] = vispy.gloo.Texture2D(opacity, format='luminance', interpolation='nearest') self.program['opacity_size'] = opacity.shape[:2][::-1] self.program['ij'] = np.mgrid[0:opacity.shape[1], 0:opacity.shape[0]].astype( 'float32').transpose(1, 2, 0) def render(self, pos, read=False): """ """ self.program['center'] = pos with self.fbo: vispy.gloo.clear(color=(1, 1, 1)) vispy.gloo.set_viewport(0, 0, *self.size[::-1]) #vispy.gloo.set_state(cull_face=True) self.program.draw(mode='points', check_error=True) vispy.gloo.set_viewport(0, 0, *self.scene.canvas.size) if read: img = self.fbo.read() if read: return img[::-1]
class LineCollection: VERTEX_SHADER = """ attribute vec2 a_position; attribute vec4 a_color; varying vec4 v_color; void main (void) { v_color = a_color; gl_Position = $transform(vec4(a_position, 0.0, 1.0)); } """ FRAGMENT_SHADER = """ varying vec4 v_color; void main() { gl_FragColor = v_color; } """ def __init__(self): self._vert = [] self._color = [] self._program = ModularProgram(LineCollection.VERTEX_SHADER, LineCollection.FRAGMENT_SHADER) def clear(self): self._vert = [] self._color = [] def append(self, pt1, pt2, color=[1, 1, 1, 1]): """ pt1 : 2 tuple The first point on the line, in screen coordinates. pt2 : 2 tuple The second point of the line, in screen coordinates. color : 4 tuple The color of the line in (r, g, b, alpha). """ self._vert.append(pt1) self._vert.append(pt2) self._color.append(color) self._color.append(color) self._program['a_position'] = np.array(self._vert, dtype=np.float32) self._program['a_color'] = np.array(self._color, dtype=np.float32) def draw(self, transforms): if len(self._vert) > 0: self._program.vert['transform'] = transforms.get_full_transform() self._program.draw('lines')
def __init__(self, scene, los_tex, size, supersample=4): vert = """ #version 120 attribute vec2 pos; varying vec2 v_pos; void main(void) { gl_Position = vec4(pos, 0, 1); v_pos = $transform(gl_Position).xy; } """ frag = """ #version 120 varying vec2 v_pos; uniform sampler2D los_tex; void main(void) { vec2 polar_pos = $transform(vec4(v_pos, 0, 1)).xy; float los_depth = texture2D(los_tex, vec2(polar_pos.x, 0.5)).r; float diff = (los_depth+1 - polar_pos.y); gl_FragColor = vec4(diff, diff, diff, 1); } """ self.scene = scene self.size = (size[0] * supersample, size[1] * supersample) self.vertices = np.array( [[-1, -1], [1, -1], [-1, 1], [-1, 1], [1, -1], [1, 1]], dtype='float32') self.program = ModularProgram(vert, frag) self.program['pos'] = self.vertices self.program['los_tex'] = los_tex self.program.vert['transform'] = STTransform( scale=(size[1] / 2., size[0] / 2.)) * STTransform(translate=(1, 1)) self.center = STTransform() self.program.frag['transform'] = STTransform( scale=(0.5 / np.pi, 1, 1), translate=(0.5, 0, 0)) * PolarTransform().inverse * self.center self.tex = vispy.gloo.Texture2D(shape=self.size + (4, ), format='rgba', interpolation='linear') self.fbo = vispy.gloo.FrameBuffer(color=self.tex, depth=vispy.gloo.RenderBuffer( self.size))
def __init__(self, n_volume_max=10, threshold=None, relative_step_size=0.8, emulate_texture=False): Visual.__init__(self) self._n_volume_max = n_volume_max # Only show back faces of cuboid. This is required because if we are # inside the volume, then the front faces are outside of the clipping # box and will not be drawn. self.set_gl_state('translucent', cull_face=False) tex_cls = TextureEmulated3D if emulate_texture else Texture3D # Storage of information of volume self._initial_shape = True self._vol_shape = () self._vertex_cache_id = () self._clim = None # Create gloo objects self._vbo = None self._tex = tex_cls((10, 10, 10), interpolation='linear', wrapping='clamp_to_edge') # Set custom vertex shader vert_shader, frag_shader = get_shaders(n_volume_max) frag_dict['additive_multi_volume'] = frag_shader # Create program self._program = ModularProgram(vert_shader) self.textures = [] for i in range(n_volume_max): self.textures.append( tex_cls((10, 10, 10), interpolation='linear', wrapping='clamp_to_edge')) self._program['u_volumetex_{0}'.format(i)] = self.textures[i] self._index_buffer = None # Set params self.method = 'additive_multi_volume' self.relative_step_size = relative_step_size for i in range(n_volume_max): self._program.frag['cmap{0:d}'.format(i)] = Function( get_colormap('grays').glsl_map) self._program['u_enabled_{0}'.format(i)] = 0 self._program['u_weight_{0}'.format(i)] = 1 self.xd = None self._block_size = None self.volumes = {} self._create_vertex_data()
def __init__(self, canvas, spike_data): self.canvas = canvas self.program = ModularProgram(self.VERTEX_SHADER, self.FRAGMENT_SHADER) self._t0 = 0 self._dt = 10 self.mouse_t = self.t0 self._interval = 1/30.0 self.electrode = '' self.time_scale = 1/200 self._vert = np.zeros((120*6, 2), dtype=np.float32) self._color = np.zeros(120*6, dtype=np.float32) self.electrodes = [] self.spikes = spike_data for tag, df in spike_data.groupby('electrode'): self.electrodes.append(FlashingSpikeElectrode(tag, df['time'].values)) self._create_vertex_data() self.paused = True self.program['a_position'] = self._vert self.program['a_color'] = self._color self.program.vert['transform'] = canvas.tr_sys.get_full_transform() self.outline = visuals.LineVisual(color=Theme.yellow) self.electrode_cols = [c for c in 'ABCDEFGHJKLM'] self._rescale_outline() self.extra_text = ''
def __init__(self, **kwargs): app.Canvas.__init__(self, **kwargs) self.geometry = 0, 0, 400, 400 mesh = create_sphere(10, 10, radius=2.) vertices = mesh.get_vertices() tris = mesh.get_faces() data=vertices[:, 2] self.filled_buf = gloo.IndexBuffer(tris) self.program = ModularProgram(VERT_CODE, FRAG_CODE) colormap = get_colormap('autumn') self.color = Function(colormap.glsl_map) self.program.frag['color'] = self.color('floor(v_value*10.+0.5)/10.') # Set attributes self.program['a_position'] = gloo.VertexBuffer(vertices) self.program['a_value'] = gloo.VertexBuffer(normalize(data, data.min(), data.max())) # Handle transformations self.init_transforms() gloo.set_clear_color((1, 1, 1, 1)) gloo.set_state(depth_test=True) self._timer = app.Timer('auto', connect=self.update_transforms) self._timer.start()
def __init__(self, data): super(SignalsVisual, self).__init__() self._program = ModularProgram(self.VERTEX_SHADER, self.FRAGMENT_SHADER) nsignals, nsamples = data.shape # nsamples, nsignals = data.shape self._data = data a_index = np.c_[np.repeat(np.arange(nsignals), nsamples), np.tile(np.arange(nsamples), nsignals)].astype( np.float32) # Doesn't seem to work nor to be very efficient. # indices = nsignals * np.arange(nsamples) # indices = indices[None, :] + np.arange(nsignals)[:, None] # indices = indices.flatten().astype(np.uint32) # self._ibuffer = gloo.IndexBuffer(indices) self._buffer = gloo.VertexBuffer(data.reshape(-1, 1)) self._program['a_position'] = self._buffer self._program['a_index'] = a_index x_transform = Function(X_TRANSFORM) x_transform['nsamples'] = nsamples self._program.vert['get_x'] = x_transform y_transform = Function(Y_TRANSFORM) y_transform['scale'] = Variable('uniform float u_signal_scale', 5.) y_transform['nsignals'] = nsignals self._program.vert['get_y'] = y_transform self._y_transform = y_transform colormap = Function(DISCRETE_CMAP) cmap = np.random.uniform(size=(1, nsignals, 3), low=.5, high=.9).astype(np.float32) tex = gloo.Texture2D((cmap * 255).astype(np.uint8)) colormap['colormap'] = Variable('uniform sampler2D u_colormap', tex) colormap['ncolors'] = nsignals self._program.frag['get_color'] = colormap
def __init__(self, sight_texture): self.sight_tex = sight_texture self.size = sight_texture.shape[:2] self.memory_tex = vispy.gloo.Texture2D(shape=self.size + (4, ), format='rgba', interpolation='linear', wrapping='repeat') self.fbo = vispy.gloo.FrameBuffer(color=self.texture, depth=vispy.gloo.RenderBuffer( self.size)) vert = """ #version 330 compatibility in vec2 ij; void main (void) { gl_Position = vec4(ij, 0, 1); } """ frag = """ #version 330 compatibility void main (void) { gl_FragColor = vec4(0, 0, 1, 1); } """ self.program = ModularProgram(vert, frag, gcode=geom) self.program['opacity'] = vispy.gloo.Texture2D(opacity, format='luminance', interpolation='nearest') self.program['opacity_size'] = opacity.shape[:2][::-1] self.program['ij'] = np.mgrid[0:opacity.shape[1], 0:opacity.shape[0]].astype( 'float32').transpose(1, 2, 0)
def __init__(self, data): super(SignalsVisual, self).__init__() self._program = ModularProgram(self.VERTEX_SHADER, self.FRAGMENT_SHADER) nsignals, nsamples = data.shape # nsamples, nsignals = data.shape self._data = data a_index = np.c_[np.repeat(np.arange(nsignals), nsamples), np.tile(np.arange(nsamples), nsignals) ].astype(np.float32) # Doesn't seem to work nor to be very efficient. # indices = nsignals * np.arange(nsamples) # indices = indices[None, :] + np.arange(nsignals)[:, None] # indices = indices.flatten().astype(np.uint32) # self._ibuffer = gloo.IndexBuffer(indices) self._buffer = gloo.VertexBuffer(data.reshape(-1, 1)) self._program['a_position'] = self._buffer self._program['a_index'] = a_index x_transform = Function(X_TRANSFORM) x_transform['nsamples'] = nsamples self._program.vert['get_x'] = x_transform y_transform = Function(Y_TRANSFORM) y_transform['scale'] = Variable('uniform float u_signal_scale', 5.) y_transform['nsignals'] = nsignals self._program.vert['get_y'] = y_transform self._y_transform = y_transform colormap = Function(DISCRETE_CMAP) cmap = np.random.uniform(size=(1, nsignals, 3), low=.5, high=.9).astype(np.float32) tex = gloo.Texture2D((cmap * 255).astype(np.uint8)) colormap['colormap'] = Variable('uniform sampler2D u_colormap', tex) colormap['ncolors'] = nsignals self._program.frag['get_color'] = colormap
class SignalsVisual(Visual): VERTEX_SHADER = """ attribute float a_position; attribute vec2 a_index; varying vec2 v_index; uniform float u_nsignals; uniform float u_nsamples; void main() { vec2 position = vec2($get_x(a_index.y), $get_y(a_index.x, a_position)); gl_Position = $transform(position); v_index = a_index; } """ FRAGMENT_SHADER = """ varying vec2 v_index; void main() { gl_FragColor = vec4($get_color(v_index.x), 1.); // Discard vertices between two signals. if ((fract(v_index.x) > 0.)) discard; } """ def __init__(self, data): super(SignalsVisual, self).__init__() self._program = ModularProgram(self.VERTEX_SHADER, self.FRAGMENT_SHADER) nsignals, nsamples = data.shape # nsamples, nsignals = data.shape self._data = data a_index = np.c_[np.repeat(np.arange(nsignals), nsamples), np.tile(np.arange(nsamples), nsignals)].astype( np.float32) # Doesn't seem to work nor to be very efficient. # indices = nsignals * np.arange(nsamples) # indices = indices[None, :] + np.arange(nsignals)[:, None] # indices = indices.flatten().astype(np.uint32) # self._ibuffer = gloo.IndexBuffer(indices) self._buffer = gloo.VertexBuffer(data.reshape(-1, 1)) self._program['a_position'] = self._buffer self._program['a_index'] = a_index x_transform = Function(X_TRANSFORM) x_transform['nsamples'] = nsamples self._program.vert['get_x'] = x_transform y_transform = Function(Y_TRANSFORM) y_transform['scale'] = Variable('uniform float u_signal_scale', 5.) y_transform['nsignals'] = nsignals self._program.vert['get_y'] = y_transform self._y_transform = y_transform colormap = Function(DISCRETE_CMAP) cmap = np.random.uniform(size=(1, nsignals, 3), low=.5, high=.9).astype(np.float32) tex = gloo.Texture2D((cmap * 255).astype(np.uint8)) colormap['colormap'] = Variable('uniform sampler2D u_colormap', tex) colormap['ncolors'] = nsignals self._program.frag['get_color'] = colormap @property def data(self): return self._data @data.setter def data(self, value): self._data = value self._buffer.set_subdata(value.reshape(-1, 1)) self.update() @property def signal_scale(self): return self._y_transform['scale'].value @signal_scale.setter def signal_scale(self, value): self._y_transform['scale'].value = value self.update() def draw(self, transform_system): self._program.draw('line_strip')
class Canvas(app.Canvas): def __init__(self, **kwargs): app.Canvas.__init__(self, **kwargs) self.geometry = 0, 0, 400, 400 mesh = create_sphere(10, 10, radius=2.) vertices = mesh.get_vertices() tris = mesh.get_faces() data=vertices[:, 2] self.filled_buf = gloo.IndexBuffer(tris) self.program = ModularProgram(VERT_CODE, FRAG_CODE) colormap = get_colormap('autumn') self.color = Function(colormap.glsl_map) self.program.frag['color'] = self.color('floor(v_value*10.+0.5)/10.') # Set attributes self.program['a_position'] = gloo.VertexBuffer(vertices) self.program['a_value'] = gloo.VertexBuffer(normalize(data, data.min(), data.max())) # Handle transformations self.init_transforms() gloo.set_clear_color((1, 1, 1, 1)) gloo.set_state(depth_test=True) self._timer = app.Timer('auto', connect=self.update_transforms) self._timer.start() def on_resize(self, event): width, height = event.size gloo.set_viewport(0, 0, width, height) self.projection = perspective(45.0, width / float(height), 2.0, 10.0) self.program['u_projection'] = self.projection def on_draw(self, event): gloo.clear() self.program.draw('triangles', self.filled_buf) def init_transforms(self): self.view = np.eye(4, dtype=np.float32) self.model = np.eye(4, dtype=np.float32) self.projection = np.eye(4, dtype=np.float32) self.theta = 0 self.phi = 0 translate(self.view, 0, 0, -5) self.program['u_model'] = self.model self.program['u_view'] = self.view def update_transforms(self, event): self.theta += .5 self.phi += .5 self.model = np.eye(4, dtype=np.float32) rotate(self.model, self.theta, 0, 0, 1) rotate(self.model, self.phi, 0, 1, 0) self.program['u_model'] = self.model self.update()
def __init__(self, pos=None, color=None, size=None): self._program = ModularProgram(self.VERTEX_SHADER, self.FRAGMENT_SHADER) self.set_data(pos=pos, color=color, size=size)
def __init__(self): self._vert = [] self._color = [] self._program = ModularProgram(LineCollection.VERTEX_SHADER, LineCollection.FRAGMENT_SHADER)
def __init__(self, scene, opacity, supersample=1): self.scene = scene self.size = (opacity.shape[0] * supersample, opacity.shape[1] * supersample) # for render to texture self.texture = vispy.gloo.Texture2D(shape=self.size + (4, ), format='rgba', interpolation='linear', wrapping='repeat') self.fbo = vispy.gloo.FrameBuffer(color=self.texture, depth=vispy.gloo.RenderBuffer( self.size)) # For render / read to CPU #self.fbo = vispy.gloo.FrameBuffer(color=vispy.gloo.RenderBuffer(self.size), #depth=vispy.gloo.RenderBuffer(self.size)) vert = """ #version 330 compatibility in vec2 ij; void main (void) { gl_Position = vec4(ij, 0, 1); } """ geom = """ #version 330 compatibility layout (points) in; layout (triangle_strip, max_vertices=10) out; uniform sampler2D opacity; uniform vec2 opacity_size; uniform vec2 center; void main (void) { vec4 pos = gl_in[0].gl_Position; // first get opacity of this block and its neighbors float opaque[5]; vec2 dij[5]; dij[0] = vec2(0, 0); dij[1] = vec2(-1, 0); dij[2] = vec2(1, 0); dij[3] = vec2(0, -1); dij[4] = vec2(0, 1); vec2 uv; for( int i=0; i<5; i++ ) { uv = (pos.xy + 0.5 + dij[i]) / opacity_size; opaque[i] = texture(opacity, uv).r; } // now construct shadows float w = 2./3.; dij[0] = vec2(w, w); dij[1] = vec2(1-w, w); dij[2] = vec2(1-w, 1-w); dij[3] = vec2(w, 1-w); dij[4] = vec2(w, w); // decide based on opacity of neighbors whether to join walls if( opaque[1] > 0.5 ) { dij[0].x = 0; dij[3].x = 0; dij[4].x = 0; } if( opaque[2] > 0.5 ) { dij[1].x = 1; dij[2].x = 1; } if( opaque[3] > 0.5 ) { dij[0].y = 0; dij[1].y = 0; dij[4].y = 0; } if( opaque[4] > 0.5 ) { dij[2].y = 1; dij[3].y = 1; } vec4 pos2; if( opaque[0] >= 1 ) { for( int n=0; n<5; n++ ) { pos2 = (pos + vec4(dij[n], 0, 0)); vec2 dx = normalize(pos2.xy - (center + 0.5)); //pos2 += vec4(dx * .5, 0, 0); gl_Position = pos2 * 2 / vec4(opacity_size, 1, 1) - 1; EmitVertex(); pos2 += vec4(dx * 1000, 0, 0); //pos2 = pos + vec4(0.5, 0.5, 0, 0); gl_Position = pos2 * 2 / vec4(opacity_size, 1, 1) - 1; EmitVertex(); } EndPrimitive(); } } """ frag = """ #version 330 compatibility void main (void) { gl_FragColor = vec4(0, 0, 0, 1); } """ self.program = ModularProgram(vert, frag, gcode=geom) self.program['opacity'] = vispy.gloo.Texture2D(opacity, format='luminance', interpolation='nearest') self.program['opacity_size'] = opacity.shape[:2][::-1] corner_coords = np.mgrid[0:opacity.shape[1], 0:opacity.shape[0]].astype( 'float32').transpose(1, 2, 0) self.program['ij'] = corner_coords.copy( ) # copy to prevent warning about discontiguous data
class MarkerVisual(Visual): # My full vertex shader, with just a `transform` hook. VERTEX_SHADER = """ #version 120 attribute vec2 a_position; attribute vec3 a_color; attribute float a_size; varying vec4 v_fg_color; varying vec4 v_bg_color; varying float v_radius; varying float v_linewidth; varying float v_antialias; void main (void) { v_radius = a_size; v_linewidth = 1.0; v_antialias = 1.0; v_fg_color = vec4(0.0,0.0,0.0,0.5); v_bg_color = vec4(a_color, 1.0); gl_Position = $transform(vec4(a_position,0,1)); gl_PointSize = 2.0*(v_radius + v_linewidth + 1.5*v_antialias); } """ FRAGMENT_SHADER = """ #version 120 varying vec4 v_fg_color; varying vec4 v_bg_color; varying float v_radius; varying float v_linewidth; varying float v_antialias; void main() { float size = 2.0*(v_radius + v_linewidth + 1.5*v_antialias); float t = v_linewidth/2.0-v_antialias; float r = length((gl_PointCoord.xy - vec2(0.5,0.5))*size); float d = abs(r - v_radius) - t; if( d < 0.0 ) gl_FragColor = v_fg_color; else { float alpha = d/v_antialias; alpha = exp(-alpha*alpha); if (r > v_radius) gl_FragColor = vec4(v_fg_color.rgb, alpha*v_fg_color.a); else gl_FragColor = mix(v_bg_color, v_fg_color, alpha); } } """ def __init__(self, pos=None, color=None, size=None): self._program = ModularProgram(self.VERTEX_SHADER, self.FRAGMENT_SHADER) self.set_data(pos=pos, color=color, size=size) def set_options(self): """Special function that is used to set the options. Automatically called at initialization.""" gloo.set_state(clear_color=(1, 1, 1, 1), blend=True, blend_func=('src_alpha', 'one_minus_src_alpha')) def set_data(self, pos=None, color=None, size=None): """I'm not required to use this function. We could also have a system of trait attributes, such that a user doing `visual.position = myndarray` results in an automatic update of the buffer. Here I just set the buffers manually.""" self._pos = pos self._color = color self._size = size def draw(self, transforms): # attributes / uniforms are not available until program is built tr = transforms.get_full_transform() self._program.vert['transform'] = tr.shader_map() self._program.prepare() # Force ModularProgram to set shaders self._program['a_position'] = gloo.VertexBuffer(self._pos) self._program['a_color'] = gloo.VertexBuffer(self._color) self._program['a_size'] = gloo.VertexBuffer(self._size) self._program.draw('points')
class LOSTextureRenderer(object): """Converts a 1D polar line-of-sight texture into a 2D (cartesian) shadow map. """ def __init__(self, scene, los_tex, size, supersample=4): vert = """ #version 120 attribute vec2 pos; varying vec2 v_pos; void main(void) { gl_Position = vec4(pos, 0, 1); v_pos = $transform(gl_Position).xy; } """ frag = """ #version 120 varying vec2 v_pos; uniform sampler2D los_tex; void main(void) { vec2 polar_pos = $transform(vec4(v_pos, 0, 1)).xy; float los_depth = texture2D(los_tex, vec2(polar_pos.x, 0.5)).r; float diff = (los_depth+1 - polar_pos.y); gl_FragColor = vec4(diff, diff, diff, 1); } """ self.scene = scene self.size = (size[0] * supersample, size[1] * supersample) self.vertices = np.array( [[-1, -1], [1, -1], [-1, 1], [-1, 1], [1, -1], [1, 1]], dtype='float32') self.program = ModularProgram(vert, frag) self.program['pos'] = self.vertices self.program['los_tex'] = los_tex self.program.vert['transform'] = STTransform( scale=(size[1] / 2., size[0] / 2.)) * STTransform(translate=(1, 1)) self.center = STTransform() self.program.frag['transform'] = STTransform( scale=(0.5 / np.pi, 1, 1), translate=(0.5, 0, 0)) * PolarTransform().inverse * self.center self.tex = vispy.gloo.Texture2D(shape=self.size + (4, ), format='rgba', interpolation='linear') self.fbo = vispy.gloo.FrameBuffer(color=self.tex, depth=vispy.gloo.RenderBuffer( self.size)) def render(self, pos): self.center.translate = (-pos[0] - 0.5, -pos[1] - 0.5) with self.fbo: vispy.gloo.clear(color=(0, 0, 0), depth=True) vispy.gloo.set_state(depth_test=True) vispy.gloo.set_viewport(0, 0, *self.size[::-1]) self.program.draw(mode='triangles', check_error=True) vispy.gloo.set_viewport(0, 0, *self.scene.canvas.size) img = self.fbo.read() return img
def __init__(self, scene, opacity, size=(100, 1000)): self.scene = scene self.size = size self.tex = vispy.gloo.Texture2D(shape=size + (4, ), format='rgba', interpolation='linear', wrapping='repeat') self.fbo = vispy.gloo.FrameBuffer(color=self.tex, depth=vispy.gloo.RenderBuffer(size)) vert = """ #version 120 attribute vec3 position; uniform float scale; uniform float underfill; varying vec3 depth; uniform sampler2D opacity; uniform vec2 opacity_size; void main (void) { vec3 cpos = position; float alpha = texture2D(opacity, (cpos.xy+vec2(0.5, 0.5)) / opacity_size).r; if( alpha > 0 ) { vec4 center = $transform(vec4(cpos, 1)); // Determine the min/max azimuthal angle occupied by this wall float min_theta = center.x; float max_theta = center.x; // Check for connection with adjacent walls, extend for( int i=0; i<2; i++ ) { for( int j=-1; j<2; j+=2 ) { vec3 dx = vec3(0, 0, 0); dx[i] = j; vec3 pos2 = cpos + dx; float alpha2 = texture2D(opacity, (pos2.xy+vec2(0.5, 0.5)) / opacity_size).r; if( alpha2 > 0 ) { dx[i] = j/1.95; // 1.95 gives a small amount of overlap to prevent gaps } else { dx[i] = j/4.; // unconnected walls still have some width } vec4 polar_pos = $transform(vec4(cpos + dx, 1)); if( polar_pos.x - center.x > 1 ) { // point wraps around between -pi and +pi polar_pos.x -= 2; } else if( center.x - polar_pos.x > 1 ) { polar_pos.x += 2; } min_theta = min(min_theta, polar_pos.x); max_theta = max(max_theta, polar_pos.x); } } if( min_theta < -1 ) { min_theta += 2; max_theta += 2; center.x += 2; } float theta = (min_theta + max_theta) / 2.0; theta = (theta + 1) * underfill - 1; // compress theta range to avoid overflowing the right edge gl_Position = vec4(theta, 0, center.y/1000., 1); gl_PointSize = scale * underfill * abs(max_theta - min_theta); // encode depth as rgb float r = int(center.y / 256.) / 255.; float g = int(center.y - (r*256)) / 255.; float b = center.y - int(center.y); depth = vec3(r, g, b); } else { // Not a wall gl_Position = vec4(-2, -2, -2, 1); gl_PointSize = 0; } } """ frag = """ #version 120 varying vec3 depth; void main (void) { gl_FragColor = vec4(depth, 1); } """ self.program = ModularProgram(vert, frag) self.underfill = 0.9 # Need to underfill the x axis because some points straddle the border between -pi and +pi self.program['underfill'] = self.underfill self.center = STTransform() self.transform = STTransform( scale=(1. / np.pi, 1, 1)) * PolarTransform().inverse * self.center self.program.vert['transform'] = self.transform self.program['scale'] = self.size[1] / 2.0 self.program['wrap'] = self.underfill self.program['opacity'] = opacity self.program['opacity_size'] = opacity.shape[:2][::-1]
class SightRenderer(object): """For computing 1d (polar) line of sight and shadows on GPU """ def __init__(self, scene, opacity, size=(100, 1000)): self.scene = scene self.size = size self.tex = vispy.gloo.Texture2D(shape=size + (4, ), format='rgba', interpolation='linear', wrapping='repeat') self.fbo = vispy.gloo.FrameBuffer(color=self.tex, depth=vispy.gloo.RenderBuffer(size)) vert = """ #version 120 attribute vec3 position; uniform float scale; uniform float underfill; varying vec3 depth; uniform sampler2D opacity; uniform vec2 opacity_size; void main (void) { vec3 cpos = position; float alpha = texture2D(opacity, (cpos.xy+vec2(0.5, 0.5)) / opacity_size).r; if( alpha > 0 ) { vec4 center = $transform(vec4(cpos, 1)); // Determine the min/max azimuthal angle occupied by this wall float min_theta = center.x; float max_theta = center.x; // Check for connection with adjacent walls, extend for( int i=0; i<2; i++ ) { for( int j=-1; j<2; j+=2 ) { vec3 dx = vec3(0, 0, 0); dx[i] = j; vec3 pos2 = cpos + dx; float alpha2 = texture2D(opacity, (pos2.xy+vec2(0.5, 0.5)) / opacity_size).r; if( alpha2 > 0 ) { dx[i] = j/1.95; // 1.95 gives a small amount of overlap to prevent gaps } else { dx[i] = j/4.; // unconnected walls still have some width } vec4 polar_pos = $transform(vec4(cpos + dx, 1)); if( polar_pos.x - center.x > 1 ) { // point wraps around between -pi and +pi polar_pos.x -= 2; } else if( center.x - polar_pos.x > 1 ) { polar_pos.x += 2; } min_theta = min(min_theta, polar_pos.x); max_theta = max(max_theta, polar_pos.x); } } if( min_theta < -1 ) { min_theta += 2; max_theta += 2; center.x += 2; } float theta = (min_theta + max_theta) / 2.0; theta = (theta + 1) * underfill - 1; // compress theta range to avoid overflowing the right edge gl_Position = vec4(theta, 0, center.y/1000., 1); gl_PointSize = scale * underfill * abs(max_theta - min_theta); // encode depth as rgb float r = int(center.y / 256.) / 255.; float g = int(center.y - (r*256)) / 255.; float b = center.y - int(center.y); depth = vec3(r, g, b); } else { // Not a wall gl_Position = vec4(-2, -2, -2, 1); gl_PointSize = 0; } } """ frag = """ #version 120 varying vec3 depth; void main (void) { gl_FragColor = vec4(depth, 1); } """ self.program = ModularProgram(vert, frag) self.underfill = 0.9 # Need to underfill the x axis because some points straddle the border between -pi and +pi self.program['underfill'] = self.underfill self.center = STTransform() self.transform = STTransform( scale=(1. / np.pi, 1, 1)) * PolarTransform().inverse * self.center self.program.vert['transform'] = self.transform self.program['scale'] = self.size[1] / 2.0 self.program['wrap'] = self.underfill self.program['opacity'] = opacity self.program['opacity_size'] = opacity.shape[:2][::-1] def render(self, pos): """Compute distance from pos to nearest object in all directions. Returns an array of shape (1, N), where [0,0] gives the distance to the nearest object at theta=-pi, and [0,-1] gives the nearest object distance at theta=+pi. Strategy is: 1) draw all opaque points mapped into polar coordinates (x=theta, z=depth), with depth encoded as fragment rgb 2) points that straddle the +pi/-pi boundary are shifted to +pi, and the texture is expanded to prevent clipping these overflow fragments 3) after rendering, the texture is downloaded and decoded, and overflow fragments are wrapped back to the left side of the texture """ self.center.translate = (-pos[0], -pos[1]) self.program['position'] = self.scene.txt.shared_program['position'] with self.fbo: vispy.gloo.clear(color=(0, 0, 0), depth=True) vispy.gloo.set_state(depth_test=True) vispy.gloo.set_viewport(0, 0, *self.size[::-1]) self.program.draw(mode='points', check_error=True) vispy.gloo.set_viewport(0, 0, *self.scene.canvas.size) img = self.fbo.read() # decode distance from rgb dist = img[..., 0] * 255 + img[..., 1] + img[..., 2] / 255. # wrap from right side overflow back to left side i = dist.shape[0] // 2 j = int(dist.shape[1] * self.underfill) v = dist[i, j] try: j2 = np.argwhere(dist[i, j:] != v)[0, 0] except IndexError: raise Exception("Error: overflowed line-of-sight render buffer :(") dist[:, :j2] = dist[:, j:j + j2] dist = dist[:, :j] return dist
class FlashingSpikeVisualization(Visualization): VERTEX_SHADER = """ attribute vec2 a_position; attribute float a_color; varying float v_color; void main(void) { gl_Position = $transform(vec4(a_position, 0.0, 1.0)); v_color = a_color; } """ FRAGMENT_SHADER = """ varying float v_color; void main() { gl_FragColor = vec4(v_color, v_color, v_color, 1); } """ mea_outline = np.array( [[3.0, 0.0], [9.0, 0.0], [9.0, 1.0], [10.0, 1.0], [10.0, 2.0], [11.0, 2.0], [11.0, 3.0], [12.0, 3.0], [12.0, 9.0], [11.0, 9.0], [11.0, 10.0], [10.0, 10.0], [10.0, 11.0], [9.0, 11.0], [9.0, 12.0], [3.0, 12.0], [3.0, 11.0], [2.0, 11.0], [2.0, 10.0], [1.0, 10.0], [1.0, 9.0], [0.0, 9.0], [0.0, 3.0], [1.0, 3.0], [1.0, 2.0], [2.0, 2.0], [2.0, 1.0], [3.0, 1.0], [3.0, 0.0]], dtype=np.float32) def __init__(self, canvas, spike_data): self.canvas = canvas self.program = ModularProgram(self.VERTEX_SHADER, self.FRAGMENT_SHADER) self._t0 = 0 self._dt = 10 self.mouse_t = self.t0 self._interval = 1 / 30.0 self.electrode = '' self.time_scale = 1 / 200 self._vert = np.zeros((120 * 6, 2), dtype=np.float32) self._color = np.zeros(120 * 6, dtype=np.float32) self.electrodes = [] self.spikes = spike_data for tag, df in spike_data.groupby('electrode'): self.electrodes.append( FlashingSpikeElectrode(tag, df['time'].values)) self._create_vertex_data() self.paused = True self.program['a_position'] = self._vert self.program['a_color'] = self._color self.program.vert['transform'] = canvas.tr_sys.get_full_transform() self.outline = visuals.LineVisual(color=Theme.yellow) self.electrode_cols = [c for c in 'ABCDEFGHJKLM'] self._rescale_outline() self.extra_text = '' @property def t0(self): return self._t0 @t0.setter def t0(self, val): self._t0 = util.clip(val, -self.spikes.time.max(), self.spikes.time.max()) self.mouse_t = self._t0 @property def dt(self): return self._dt @dt.setter def dt(self, val): self._dt = util.clip(val, 0.0025, self.spikes.time.max()) def _create_vertex_data(self): self._vert = np.zeros((120 * 6, 2), dtype=np.float32) for i, e in enumerate(self.electrodes): x, y = mea.coordinates_for_electrode(e.tag) size = self.canvas.size[1] / 14 x = self.canvas.size[0] / 2 - 6 * size + x * size y = y * size + size self._vert[6 * i] = [x, y] self._vert[6 * i + 1] = [x + size, y] self._vert[6 * i + 2] = [x + size, y + size] self._vert[6 * i + 3] = [x, y] self._vert[6 * i + 4] = [x + size, y + size] self._vert[6 * i + 5] = [x, y + size] def _rescale_outline(self): size = self.canvas.size[1] / 14 self.outline.set_data(self.mea_outline * size + [self.canvas.size[0] / 2 - 6 * size, size]) def draw(self): gloo.clear((0.0, 0.0, 0.0, 1)) self.outline.draw(self.canvas.tr_sys) self.program.draw('triangles') def pause(self): self.paused = True def run(self): self.paused = False def toggle_play(self): if self.paused: self.run() else: self.pause() def update(self): self.program['a_color'] = self._color def on_resize(self, event): self._create_vertex_data() self._rescale_outline() self.program['a_position'] = self._vert self.program.vert['transform'] = \ self.canvas.tr_sys.get_full_transform() def on_tick(self, event): if self.paused: return for i, e in enumerate(self.electrodes): e.update(self.t0, self._interval * self.time_scale) self._color[6 * i:6 * i + 6] = e.value self.t0 += self.time_scale * self._interval self.update() def on_key_release(self, event): if event.key == 'space': self.toggle_play() elif event.key == 'Left': self.t0 -= 4 * self.time_scale # Jump back in time def on_mouse_move(self, event): x, y = event.pos cell_size = self.canvas.size[1] / 14 row = int((y - cell_size) / cell_size) + 1 col = int((x - self.canvas.width / 2) / cell_size + 6) if row < 1 or row > 12 or col < 0 or col > 11: self.electrode = '' else: self.electrode = '%s%d' % (self.electrode_cols[col], row)
class FlashingSpikeVisualization(Visualization): VERTEX_SHADER = """ attribute vec2 a_position; attribute float a_color; varying float v_color; void main(void) { gl_Position = $transform(vec4(a_position, 0.0, 1.0)); v_color = a_color; } """ FRAGMENT_SHADER = """ varying float v_color; void main() { gl_FragColor = vec4(v_color, v_color, v_color, 1); } """ mea_outline = np.array( [[3.0, 0.0], [9.0, 0.0], [9.0, 1.0], [10.0, 1.0], [10.0, 2.0], [11.0, 2.0], [11.0, 3.0], [12.0, 3.0], [12.0, 9.0], [11.0, 9.0], [11.0, 10.0], [10.0, 10.0], [10.0, 11.0], [9.0, 11.0], [9.0, 12.0], [3.0, 12.0], [3.0, 11.0], [2.0, 11.0], [2.0, 10.0], [1.0, 10.0], [1.0, 9.0], [0.0, 9.0], [0.0, 3.0], [1.0, 3.0], [1.0, 2.0], [2.0, 2.0], [2.0, 1.0], [3.0, 1.0], [3.0, 0.0]], dtype=np.float32) def __init__(self, canvas, spike_data): self.canvas = canvas self.program = ModularProgram(self.VERTEX_SHADER, self.FRAGMENT_SHADER) self._t0 = 0 self._dt = 10 self.mouse_t = self.t0 self._interval = 1/30.0 self.electrode = '' self.time_scale = 1/200 self._vert = np.zeros((120*6, 2), dtype=np.float32) self._color = np.zeros(120*6, dtype=np.float32) self.electrodes = [] self.spikes = spike_data for tag, df in spike_data.groupby('electrode'): self.electrodes.append(FlashingSpikeElectrode(tag, df['time'].values)) self._create_vertex_data() self.paused = True self.program['a_position'] = self._vert self.program['a_color'] = self._color self.program.vert['transform'] = canvas.tr_sys.get_full_transform() self.outline = visuals.LineVisual(color=Theme.yellow) self.electrode_cols = [c for c in 'ABCDEFGHJKLM'] self._rescale_outline() self.extra_text = '' @property def t0(self): return self._t0 @t0.setter def t0(self, val): self._t0 = util.clip(val, -self.spikes.time.max(), self.spikes.time.max()) self.mouse_t = self._t0 @property def dt(self): return self._dt @dt.setter def dt(self, val): self._dt = util.clip(val, 0.0025, self.spikes.time.max()) def _create_vertex_data(self): self._vert = np.zeros((120*6, 2), dtype=np.float32) for i, e in enumerate(self.electrodes): x, y = mea.coordinates_for_electrode(e.tag) size = self.canvas.size[1] / 14 x = self.canvas.size[0] / 2 - 6 * size + x * size y = y * size + size self._vert[6*i] = [x, y] self._vert[6*i + 1] = [x + size, y] self._vert[6*i + 2] = [x + size, y + size] self._vert[6*i + 3] = [x, y] self._vert[6*i + 4] = [x + size, y + size] self._vert[6*i + 5] = [x, y + size] def _rescale_outline(self): size = self.canvas.size[1] / 14 self.outline.set_data(self.mea_outline * size + [self.canvas.size[0] / 2 - 6*size, size]) def draw(self): gloo.clear((0.0, 0.0, 0.0, 1)) self.outline.draw(self.canvas.tr_sys) self.program.draw('triangles') def pause(self): self.paused = True def run(self): self.paused = False def toggle_play(self): if self.paused: self.run() else: self.pause() def update(self): self.program['a_color'] = self._color def on_resize(self, event): self._create_vertex_data() self._rescale_outline() self.program['a_position'] = self._vert self.program.vert['transform'] = \ self.canvas.tr_sys.get_full_transform() def on_tick(self, event): if self.paused: return for i, e in enumerate(self.electrodes): e.update(self.t0, self._interval * self.time_scale) self._color[6*i:6*i+6] = e.value self.t0 += self.time_scale * self._interval self.update() def on_key_release(self, event): if event.key == 'space': self.toggle_play() elif event.key == 'Left': self.t0 -= 4*self.time_scale # Jump back in time def on_mouse_move(self, event): x, y = event.pos cell_size = self.canvas.size[1] / 14 row = int((y - cell_size) / cell_size) + 1 col = int((x - self.canvas.width/2) / cell_size + 6) if row < 1 or row > 12 or col < 0 or col > 11: self.electrode = '' else: self.electrode = '%s%d' % (self.electrode_cols[col], row)
class SignalsVisual(Visual): VERTEX_SHADER = """ attribute float a_position; attribute vec2 a_index; varying vec2 v_index; uniform float u_nsignals; uniform float u_nsamples; void main() { vec2 position = vec2($get_x(a_index.y), $get_y(a_index.x, a_position)); gl_Position = $transform(position); v_index = a_index; } """ FRAGMENT_SHADER = """ varying vec2 v_index; void main() { gl_FragColor = vec4($get_color(v_index.x), 1.); // Discard vertices between two signals. if ((fract(v_index.x) > 0.)) discard; } """ def __init__(self, data): super(SignalsVisual, self).__init__() self._program = ModularProgram(self.VERTEX_SHADER, self.FRAGMENT_SHADER) nsignals, nsamples = data.shape # nsamples, nsignals = data.shape self._data = data a_index = np.c_[np.repeat(np.arange(nsignals), nsamples), np.tile(np.arange(nsamples), nsignals) ].astype(np.float32) # Doesn't seem to work nor to be very efficient. # indices = nsignals * np.arange(nsamples) # indices = indices[None, :] + np.arange(nsignals)[:, None] # indices = indices.flatten().astype(np.uint32) # self._ibuffer = gloo.IndexBuffer(indices) self._buffer = gloo.VertexBuffer(data.reshape(-1, 1)) self._program['a_position'] = self._buffer self._program['a_index'] = a_index x_transform = Function(X_TRANSFORM) x_transform['nsamples'] = nsamples self._program.vert['get_x'] = x_transform y_transform = Function(Y_TRANSFORM) y_transform['scale'] = Variable('uniform float u_signal_scale', 5.) y_transform['nsignals'] = nsignals self._program.vert['get_y'] = y_transform self._y_transform = y_transform colormap = Function(DISCRETE_CMAP) cmap = np.random.uniform(size=(1, nsignals, 3), low=.5, high=.9).astype(np.float32) tex = gloo.Texture2D((cmap * 255).astype(np.uint8)) colormap['colormap'] = Variable('uniform sampler2D u_colormap', tex) colormap['ncolors'] = nsignals self._program.frag['get_color'] = colormap @property def data(self): return self._data @data.setter def data(self, value): self._data = value self._buffer.set_subdata(value.reshape(-1, 1)) self.update() @property def signal_scale(self): return self._y_transform['scale'].value @signal_scale.setter def signal_scale(self, value): self._y_transform['scale'].value = value self.update() def draw(self, transform_system): self._program.draw('line_strip')
class Canvas(app.Canvas): def __init__(self, emulate3d=True): app.Canvas.__init__(self, keys='interactive', size=((W*5), (H*5))) if emulate3d: tex_cls = gloo.TextureEmulated3D else: tex_cls = gloo.Texture3D self.texture = tex_cls(img_array, interpolation='nearest', wrapping='clamp_to_edge') self.program = ModularProgram(VERT_SHADER, FRAG_SHADER) self.program.frag['sampler_type'] = self.texture.glsl_sampler_type self.program.frag['sample'] = self.texture.glsl_sample self.program['u_texture'] = self.texture self.program['i'] = 0.0 self.program.bind(gloo.VertexBuffer(data)) self.view = np.eye(4, dtype=np.float32) self.model = np.eye(4, dtype=np.float32) self.projection = np.eye(4, dtype=np.float32) self.program['u_model'] = self.model self.program['u_view'] = self.view self.projection = ortho(0, W, 0, H, -1, 1) self.program['u_projection'] = self.projection self.i = 0 gloo.set_clear_color('white') self._timer = app.Timer('auto', connect=self.on_timer, start=True) self.show() def on_resize(self, event): width, height = event.physical_size gloo.set_viewport(0, 0, width, height) self.projection = ortho(0, width, 0, height, -100, 100) self.program['u_projection'] = self.projection # Compute the new size of the quad r = width / float(height) R = W / float(H) if r < R: w, h = width, width / R x, y = 0, int((height - h) / 2) else: w, h = height * R, height x, y = int((width - w) / 2), 0 data['a_position'] = np.array( [[x, y], [x + w, y], [x, y + h], [x + w, y + h]]) self.program.bind(gloo.VertexBuffer(data)) def on_timer(self, event): # cycle every 2 sec self.i = (self.i + 1./120.) % 1.0 self.update() def on_draw(self, event): gloo.clear(color=True, depth=True) self.program['i'] = 1.9 * np.abs(0.5 - self.i) self.program.draw('triangle_strip')
def __init__(self, name, active_points=None, color=None, size=None): self._program = ModularProgram(self.VERTEX_SHADER, self.FRAGMENT_SHADER) self.name = name self.set_data(active_points=active_points, color=color, size=size)
class FlashingSpikeVisualization(Visualization): VERTEX_SHADER = """ attribute vec2 a_position; attribute float a_color; varying float v_color; void main(void) { gl_Position = $transform(vec4(a_position, 0.0, 1.0)); v_color = a_color; } """ FRAGMENT_SHADER = """ varying float v_color; void main() { gl_FragColor = vec4(v_color, v_color, v_color, 1); } """ def __init__(self, canvas, spike_data): super().__init__() self.canvas = canvas self.program = ModularProgram(self.VERTEX_SHADER, self.FRAGMENT_SHADER) self._t0 = 0 self._dt = 10 self.mouse_t = self.t0 self._interval = 1 / 30.0 self.electrode = '' self.time_scale = 1 / 200 # Each quad is drawn as two triangles, so need 6 vertices per electrode self._vert = np.zeros((self.canvas.layout.count * 6, 2), dtype=np.float32) self._color = np.zeros(self.canvas.layout.count * 6, dtype=np.float32) self.electrodes = [] self.spikes = spike_data.copy() self.spikes.electrode = self.spikes.electrode.str.extract('(\w+)\.*') for tag, df in self.spikes.groupby('electrode'): self.electrodes.append(FlashingSpikeElectrode(tag, df['time'].values)) self._create_vertex_data() self.paused = True self.program['a_position'] = self._vert self.program['a_color'] = self._color self.program.vert['transform'] = canvas.tr_sys.get_transform() self.outline = visuals.LineVisual(color=Theme.yellow) self._rescale_outline() self.extra_text = '' self.configure_transforms() @property def t0(self): return self._t0 @t0.setter def t0(self, val): self._t0 = util.clip(val, -self.spikes.time.max(), self.spikes.time.max()) self.mouse_t = self._t0 @property def dt(self): return self._dt @dt.setter def dt(self, val): self._dt = util.clip(val, 0.0025, self.spikes.time.max()) def _create_vertex_data(self): self._vert = np.zeros((self.canvas.layout.count*6, 2), dtype=np.float32) half_cols = self.canvas.layout.columns / 2 for i, e in enumerate(self.electrodes): x, y = self.canvas.layout.coordinates_for_electrode(e.tag) size = self.canvas.size[1] / (self.canvas.layout.rows + 2) x = self.canvas.size[0] / 2 - half_cols * size + x * size y = y * size + size self._vert[6*i] = [x, y] self._vert[6*i + 1] = [x + size, y] self._vert[6*i + 2] = [x + size, y + size] self._vert[6*i + 3] = [x, y] self._vert[6*i + 4] = [x + size, y + size] self._vert[6*i + 5] = [x, y + size] def _rescale_outline(self): size = self.canvas.size[1] / (self.canvas.layout.rows + 2) self.outline.set_data(self.canvas.layout.outline * size + [self.canvas.size[0] / 2 - self.canvas.layout.columns*size / 2, size]) def draw(self): gloo.clear((0.0, 0.0, 0.0, 1)) self.outline.draw() self.program.draw('triangles') def pause(self): self.paused = True def run(self): self.paused = False def toggle_play(self): if self.paused: self.run() else: self.pause() def update(self): self.program['a_color'] = self._color def on_resize(self, event): self._create_vertex_data() self._rescale_outline() self.program['a_position'] = self._vert self.program.vert['transform'] = \ self.canvas.tr_sys.get_transform() def on_tick(self, event): if self.paused: return for i, e in enumerate(self.electrodes): e.update(self.t0, self._interval * self.time_scale) self._color[6*i:6*i+6] = e.value self.t0 += self.time_scale * self._interval self.update() def on_key_release(self, event): if event.key == 'space': self.toggle_play() elif event.key == 'Left': self.t0 -= 4*self.time_scale # Jump back in time def on_mouse_move(self, event): x, y = event.pos size = self.canvas.size[1] / (self.canvas.layout.rows + 2) row = int((y - size) / size) + 1 col = int((x - self.canvas.width/2) / size + (self.canvas.layout.columns / 2)) if (row < 1 or row > self.canvas.layout.rows or col < 0 or col > self.canvas.layout.columns): self.electrode = '' else: self.electrode = ( '%s' % self.canvas.layout.electrode_for_coordinate((col, row))) def configure_transforms(self): vp = (0, 0, self.canvas.physical_size[0], self.canvas.physical_size[1]) self.canvas.context.set_viewport(*vp) self.outline.transforms.configure(canvas=self.canvas, viewport=vp)