Source code for stimuli

from __future__ import division
from lib import *

class Grating(stim(width=200.0,fs=10.0,ph=0.0,speed=0.0,contr=1.0,
[docs] box=False,Lbg=24.65,Lmin=0.0,Lmax=49.3,gamma=2.5,BTRR=63.2)): """A periodic modulation of luminance. The modulation is characterized by spatial and temporal frequencies. The stimulus is complete with a mask that can take the form of smooth (Gaussian) or sharp aperture. The grating can be tilted and slanted. :param width: size of stimulus in pixels :param fs: spatial frequncy :param ph: phase :param speed: speed :param contr: contrast This stimulus can be also used with the "Pelli-Zhang Video Switcher". The following parameters apply: :param box: logical True or 1.0 to enable Video Switcher :param Lbg: Luminance of background :param Lmin: Minimum measured luminance of monitor :param Lmax: Maximum measured luminance of monitor :param gamma: gamma correction constant :param BTRR: the BTRR parameter of you Video Switcher """ frag_source = """ precision highp float; uniform float fs; uniform float phase; uniform float contr; uniform float box; uniform float Lbg, Lmin, Lmax, gamma, BTRR; vec3 lum2image(float c) { vec3 vout; float U = 255.0*pow(((1+c)*Lbg-Lmin)/(Lmax-Lmin),1.0/gamma); float b1 = (BTRR+1.0)/BTRR; //float b = min(floor(U*b1),255.0); float b = min(floor(U*b1),255.0) + 1.0/32.0; //float b2 = floor((U-b/b1)*(BTRR+1.0)); float b2 = floor((U-b/b1)*(BTRR+1.0)) + 1.0/32.0; vout = vec3(b2/255.0, 0.0 ,b/255.0); return vout; } void main() { float x = gl_TexCoord[0].x - 0.5; float y = -(gl_TexCoord[0].y - 0.5); float m = exp(-0.5*(x*x + y*y)/pow(0.17,2.0)); float c = m*contr*cos(fs*x + phase); if (box) { gl_FragColor.rgba = vec4(lum2image(c),1.0); } else { c = (1.0+c)/2.0; gl_FragColor.rgba = vec4(c,c,c,1.0); } } """ def __init__(self,pos,params=Params()): self.pos = pos self.params = copy_params(self._defaults,params) self.clock = pyglet.clock.Clock() self.t0 = self.clock.time() self.shader = Shader(self.frag_source) self.program = self.shader.program self.uniforms = dict(map(self.shader.uniform, ['fs','phase','contr','box','Lbg','Lmin','Lmax','gamma','BTRR'])) glUseProgram(self.program) glUniform1f(self.uniforms['phase'],0.0) glUniform1f(self.uniforms['fs'],self.params.fs) glUniform1f(self.uniforms['contr'],self.params.contr) glUniform1f(self.uniforms['box'],self.params.box) glUniform1f(self.uniforms['Lbg'],self.params.Lbg) glUniform1f(self.uniforms['Lmin'],self.params.Lmin) glUniform1f(self.uniforms['Lmax'],self.params.Lmax) glUniform1f(self.uniforms['gamma'],self.params.gamma) glUniform1f(self.uniforms['BTRR'],self.params.BTRR) print self.params.box glUseProgram(0) def draw(self): p = self.params x, y = self.pos w2 = p.width/2.0 glUseProgram(self.program) ph = p.speed*(self.clock.time()-self.t0) glUniform1f(self.uniforms['phase'],ph) glPushMatrix() glLoadIdentity() glTranslatef(self.pos[0],self.pos[1],0.0) glBegin(GL_QUADS) glTexCoord2f(0.0,1.0) glVertex2f(-w2,-w2) glTexCoord2f(1.0,1.0) glVertex2f(w2,-w2) glTexCoord2f(1.0,0.0) glVertex2f(w2,w2) glTexCoord2f(0.0,0.0) glVertex2f(-w2,w2) glEnd() glPopMatrix() glUseProgram(0) class Rectangle(stim(width=200.0,height=200.0,theta=0.0)):
vert_source = """ uniform float angle; void main() { vec4 a = gl_Vertex; vec4 b = a; b.y = a.y*cos(angle) - a.z*sin(angle); b.z = a.z*cos(angle) + a.y*sin(angle); gl_TexCoord[0] = gl_MultiTexCoord0; gl_Position = gl_ModelViewProjectionMatrix*b; }""" frag_source = """ uniform float fs; uniform float phase; void main() { float x = gl_TexCoord[0].x - 0.5; float y = -(gl_TexCoord[0].y - 0.5); float c = (1.0 + cos(fs*x + phase))/2.0; float m = exp(-0.5*(x*x + y*y)/pow(0.17,2.0)); gl_FragColor.rgba = vec4(c,c,c,m); } """ def __init__(self,pos,params=Params()): self.pos = pos self.params = copy_params(self._defaults,params) self.clock = pyglet.clock.Clock() self.t0 = self.clock.time() self.shader = Shader(self.frag_source, self.vert_source) self.program = self.shader.program self.uniforms = dict(map(self.shader.uniform,['fs','phase','angle','w','h'])) glUseProgram(self.program) glUniform1f(self.uniforms['phase'],0.0) glUniform1f(self.uniforms['fs'],20.0) glUniform1f(self.uniforms['angle'],0.2) glUseProgram(0) def draw(self): glUseProgram(self.program) ph = 40.0*(self.clock.time()-self.t0) glUniform1f(self.uniforms['phase'],ph) glUniform1f(self.uniforms['angle'],ph/40.0) glMatrixMode(GL_MODELVIEW) glPushMatrix() glLoadIdentity() glTranslatef(self.pos[0],self.pos[1],0.0) w2 = self.params.width/2 glColor3f(255.0,0,0) glBegin(GL_QUADS) glTexCoord2f(0.0,1.0) glVertex2f(-w2,-w2) glTexCoord2f(1.0,1.0) glVertex2f(w2,-w2) glTexCoord2f(1.0,0.0) glVertex2f(w2,w2) glTexCoord2f(0.0,0.0) glVertex2f(-w2,w2) glEnd() glUseProgram(0) glPopMatrix() class Line(stim(length=100.0,width=10.0,angle=0.0,color=(1.0,1.0,1.0,1.0))): """A simple line stimulus. The grating can be tilted and slanted. :param length: length of the line along it major axis :param width: line thickness :param angle: rotation of the line in the presentation plane :param color: line color """ def __init__(self,pos,params=Params()): self.pos = pos if hasattr(params,'color') and not iterable(params.color): params.color = [params.color]*3 self.params = copy_params(self._defaults,params) p = self.params self.clock = pyglet.clock.Clock() self.t0 = self.clock.time() # make indexed vertex_list ind = array([0,1,2,0,2,3]) xy = r_[0.0,0.0,1.0,0.0,1.0,1.0,0.0,1.0].reshape(-1,2) - 0.5 self.vlist = pyglet.graphics.vertex_list_indexed(4, ind, 'v2d/stream', 'c4f') N = 4 self.xy = from_ctypes(self.vlist.vertices,'f8',(N,2)) self.xy[...] = xy * r_[p.length,p.width] self.colors = from_ctypes(self.vlist.colors,'f4',(N, 4)) self.colors[:,3] = 1.0 self.colors[:,:len(p.color)] = p.color self.vlist._vertices_cache.invalidate() self.vlist._colors_cache.invalidate() def draw(self): glMatrixMode(GL_MODELVIEW) glPushMatrix() glLoadIdentity() glTranslatef(self.pos[0],self.pos[1],0.0) glRotatef(self.params.angle,0.0,0.0,1.0) glDisable(GL_TEXTURE_2D) glEnable(GL_MULTISAMPLE_ARB) #glEnable(GL_POLYGON_SMOOTH); #glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST) self.vlist.draw(GL_TRIANGLES) glEnable(GL_TEXTURE_2D) #glDisable(GL_POLYGON_SMOOTH); #glHint(GL_POLYGON_SMOOTH_HINT, GL_DONT_CARE) glDisable(GL_MULTISAMPLE_ARB) glPopMatrix() class Movie(stim(fname=None,fps=60.0,scale=1.0,rotation=0.0)): """A movie stimulus. This stimulus opens a movie file and plays the movie's content on a tile. :param fname: movie file, can be a sequence of frames in MATLAB's .mat file or an arbitratry movie file recognized by ``ffmpeg`` (https://www.ffmpeg.org/) :param fps: frames-per-second for playback, default is 60.0, original framerate of movie is ignored :param scale: scaling factor of the movie (1.0 plays the movie in resolution defined in the movie file) :param rotation: rotation of the movie tile in the presentation plane """ def __init__(self,pos,params=Params()): self.pos = pos self.clock = pyglet.clock.Clock() self.params = copy_params(self._defaults,params) self.batch = pyglet.graphics.Batch() self.set_fname(self.params.fname) def set_fname(self,fname): im = None if fname: self.params.fname = fname if fname.endswith('.mat'): from scipy.io import loadmat frames = loadmat()['frames'] h, w = frames.shape[:2] frames = iter(frames) else: from movieiter import ffmpegsrc frames, w, h = ffmpegsrc(os.path.normpath(fname)) frm = frames.next() im = pyglet.image.ImageData(width=w,height=h,format='RGB',data=frm,pitch=-w*3) self.cframe = 0 im.anchor_x = w//2 im.anchor_y = h//2 self.spr = pyglet.sprite.Sprite(im,batch=self.batch) self.spr.position = (self.pos[0],self.pos[1]) self.spr.scale = self.params.scale self.spr.rotation = self.params.rotation self.frames = frames self.image = im def reset(self): self.frames, w, h = ffmpegsrc(self.params.fname) def draw(self): self.t0 = self.clock.time() self.draw_next() self.draw = self.draw_next def draw_next(self): glColor4f(255.0,255.0,255.0,255.0) if (self.clock.time() - self.t0) > 1.0/self.params.fps: self.image.set_data('RGB',-self.image.width*3,self.frames.next()) self.t0 = self.clock.time() if self.image: cx, cy = self.pos self.spr._group.texture = self.image.get_texture() self.spr.draw() class MovieGrid(stim(fname='movie/fft.mp4',fps=29.97, tilt=0.0, slant=0.0, tile_image_coords=[makerect(0,0,0.5,1.0),makerect(0.5,0,0.5,1.0)], tile_disp_coords=[makecrect(-200,0,150,120),makerect(200,0,150,120)], tile_disp_anchors=[(-200,0,0),(200,0,20)], tile_slants=[5,10], tile_tilts=[10,-10], tile_colors=[0.5,1], tile_alphas=[1.0,1.0],mask='')): """A complex movie stimulus. Movies stored in files can be played on tiles which show clipped parts of original movie frames. Multiple clip[s (tiles) can be extracted from movie's frames and controlled separately. Tiles can selected through polygons specified by vertices. :param fname: input movie file name :param fps: frames-per-second for playback, default is 60.0, original framerate of movie is ignored :param tilt: tilt(s) of individual tile(s) :param slant: slant(s) of individual tile(s) :param tile_image_coords: image coordinates is an array of XY-vertices (in normalized coordinates 0.0 .. 1.0) of image regions :param tile_disp_coords: display coordinates is an array of XY-vertices (in screen coordinates) of image regions. This array ahs to have the same size (shape) as `tile_image_coords` :param tile_disp_anchors: display anchors is an array of XY-pairs (in screen coordinates), one for each tile which provide reference for affine transformations (rotation, scaling). :param tile_slants: tilt(s) of individual tile(s) :param tile_tilts: slant(s) of individual tile(s) :param tile_colors: colors corresponding to vertices of tiles (movie pixel's luminance will be multiplied by this color) :param tile_alphas: transparency for each of tile's vertex (can be used to control tile's trassparency) :param mask: mask image (filename), an option to define complex transparency maps (gradients and holes) """ def __init__(self,pos,params): self.params = copy_params(self._defaults) self.params.__dict__.update(params.__dict__) p = self.params # load movie fname = p.fname from movieiter import ffmpegsrc if not os.path.exists(fname): raise Exception('Movie file "%s" does not exist!'%fname) frames, w, h = ffmpegsrc(fname) frm = frames.next() im = pyglet.image.ImageData(width=w,height=h,format='RGB',data=frm,pitch=-w) self.tex = im.get_texture() rx, ry = self.tex.tex_coords[6:8] p.tile_image_coords = array(p.tile_image_coords).reshape(-1,4,2)*r_[rx,ry] p.tile_disp_coords = array(p.tile_disp_coords).reshape(-1,4,3) p.tile_disp_anchors = array(p.tile_disp_anchors).reshape(-1,3) self.frames = frames self.clock = pyglet.clock.Clock() self.t0 = self.clock.time() # initialize N = p.tile_disp_anchors.shape[0] if not hasattr(p.tile_slants,'__len__'): p.tile_slants = [p.tile_slants] if not hasattr(p.tile_tilts,'__len__'): p.tile_tilts = [p.tile_tilts] if not hasattr(p.tile_colors,'__len__'): p.tile_colors = [p.tile_colors] p.tile_colors = reshape(p.tile_colors,(N,-1)) if not hasattr(p.tile_alphas,'__len__'): p.tile_alphas = [p.tile_alphas] p.tile_alphas = reshape(p.tile_alphas,(N,-1)) # check for shape time-series if hasattr(p,'series'): from itertools import cycle Nser = p.series imser = array(p.tile_image_coords).reshape(Nser,-1,4,2) dsser = array(p.tile_disp_coords).reshape(Nser,-1,4,3) p.tile_image_series = [ cycle(imser[i]) for i in xrange(imser.shape[0]) ] p.tile_disp_series = [ cycle(dsser[i]) for i in xrange(dsser.shape[0]) ] self.batch, self.vlist, self.indices = make_indarray(N) self.xy = from_ctypes(self.vlist.vertices,'f8',(4*N,3)) self.colors = from_ctypes(self.vlist.colors,'f4',(N,4,4)) self.txy = from_ctypes(self.vlist.tex_coords,'f4',(4*N,2)) # fill the arrays self.colors[...] = 1.0 for i in xrange(N): self.colors[i,:,:3] = p.tile_colors[i] self.colors[i,:,3] = p.tile_alphas[i] #self.colors[:,:,:3] = self.params.color for i in xrange(N): self.xy[i*4:(i+1)*4,:] = p.tile_disp_coords[i] self.txy[i*4:(i+1)*4,:] = p.tile_image_coords[i] # initialize mask self.set_mask(p.mask) # self.im = im self.N = N self.pos = pos # transform user-specified slants and tils from degrees to rads p.tile_slants = pi*array(p.tile_slants)/180.0 p.tile_tilts = pi*array(p.tile_tilts)/180.0 # transform each tile for i in xrange(self.N): xy = slanttilt(p.tile_disp_coords[i], p.tile_slants[i], p.tile_tilts[i], p.tile_disp_anchors[i]) self.xy[i*4:(i+1)*4,:] = xy self.vlist._vertices_cache.invalidate() frag_source = """ uniform sampler2D tex1; uniform sampler2D mask; uniform int usemask; uniform vec2 pixel; uniform vec2 offset; void main() { vec4 color1 = texture2D(tex1,gl_TexCoord[0].st); if (usemask == 1) { vec4 color2 = texture2D(mask,(gl_TexCoord[0].st-offset)*pixel); gl_FragColor.rgba = vec4(color1.r,color1.g,color1.b,color2.r); } else if (usemask < 0) { gl_FragColor.rgba = vec4(color1.r,color1.g,color1.b,color1.a); } else { gl_FragColor.rgba = vec4(color1.r,color1.g,color1.b,1.0); } } """ def setup_shader(self): self.shader = Shader(self.frag_source) self.program = self.shader.program self.uniforms = dict(map(self.shader.uniform, ['tex1','mask','usemask','pixel','offset'])) #rix, riy = self.tex.tex_coords[6:8] rix = (self.txy[1,0] - self.txy[0,0])#*rix*rix riy = (self.txy[2,1] - self.txy[1,1]) #ax = (self.txy[1,0] - self.txy[0,0])/rix #ay = (self.txy[2,1] - self.txy[1,1])/riy #rx, ry = (ax*ax)/self.tex.width, (ay*ay)/self.tex.height #rix, riy = ax, ay glUseProgram(self.program) glUniform1i(self.uniforms['tex1'],0) if self.mask == '': glUniform1i(self.uniforms['usemask'],0) elif self.mask == 'alpha': glUniform1i(self.uniforms['usemask'],-1) else: rmx, rmy = self.mask.tex_coords[6:8] #mx, my = self.mask.width, self.mask.height glUniform1i(self.uniforms['usemask'],1) glUniform1i(self.uniforms['mask'],1) glUniform2f(self.uniforms['pixel'],float(rmx)/rix,float(rmy)/riy) glUniform2f(self.uniforms['offset'],self.txy[0,0],self.txy[0,1]) glUseProgram(0) def set_mask(self,fname): self.mask = fname if fname: if fname == 'alpha': self.mask = 'alpha' else: self.mask = pyglet.image.load(fname).get_texture() self.setup_shader() def _frmupdate(self,t): if (self.clock.time() - self.t0) > 1.0/self.params.fps: self.im.set_data('RGB',-self.im.width*3,self.frames.next()) self.tex = self.im.get_texture() self.t0 = self.clock.time() if hasattr(self.params,'series'): p = self.params for i in xrange(len(p.tile_image_series)): self.txy[i*4:(i+1)*4,:] = p.tile_image_series[i].next().copy() xy = slanttilt(p.tile_disp_series[i].next(), p.tile_slants[i], p.tile_tilts[i], p.tile_disp_anchors[i]) self.xy[i*4:(i+1)*4,:] = xy self.vlist._vertices_cache.invalidate() self.vlist._tex_coords_cache.invalidate() def draw(self): self._frmupdate(self.clock.time()) p = self.params # ready to draw glPushMatrix() glLoadIdentity() #glEnable(GL_DEPTH_TEST) glTranslatef(self.pos[0],self.pos[1],0.0) glRotatef(-p.slant,1.0,0.0,0.0) glRotatef(p.tilt,0.0,1.0,0.0) glEnable(GL_TEXTURE_2D) glBindTexture(GL_TEXTURE_2D,self.tex.id) glUseProgram(self.program) glActiveTexture(GL_TEXTURE0) glBindTexture(GL_TEXTURE_2D,self.tex.id) if not isinstance(self.mask,str): glActiveTexture(GL_TEXTURE1) glBindTexture(GL_TEXTURE_2D,self.mask.id) self.vlist.draw(GL_TRIANGLES) glUseProgram(0) glDisable(GL_TEXTURE_2D) #glDisable(GL_DEPTH_TEST) glPopMatrix() class MaskNumpy(stim(width=500.0,height=500.0,sigma=200.0,edge=2.0)): """Mask stimulus computed using numpy. :param width: width of stimulus' tile :param height: height of stimulus' tile :param sigma: mask radius :param edge: power of apperture's Gaussian (slope) """ def __init__(self,pos,params=Params()): self.params = copy_params(self._defaults,params) p = self.params self.pos = pos # and (x,y) coordinate for each dot self.mask = make_mask(int(p.width),int(p.height),p.sigma,p.edge) def draw(self): p = self.params glPushMatrix() glLoadIdentity() glTranslatef(self.pos[0],self.pos[1],0.0) self.mask.draw() glPopMatrix() class Mask(stim(width=500.0,height=500.0,bgcolor=(0.0,0.0,0.0),sigma=200.0,edge=2.0)): """Mask stimulus computer on GPU (GLSL) :param width: width of stimulus' tile :param height: height of stimulus' tile :param sigma: mask radius :param edge: power of apperture's Gaussian (slope) """ frag_source = """ uniform vec3 bgcolor; uniform float sigma, edge; void main() { float x = gl_TexCoord[0].x - 0.5; float y = -(gl_TexCoord[0].y - 0.5); float m = exp(-0.5*pow(x*x + y*y,edge/2.0)/pow(sigma,edge)); gl_FragColor.rgba = vec4(bgcolor,1.0-m); } """ def __init__(self,pos,params=Params()): self.pos = pos self.params = copy_params(self._defaults,params) p = self.params self.shader = Shader(self.frag_source) self.program = self.shader.program self.uniforms = dict(map(self.shader.uniform,['bgcolor','sigma','edge'])) glUseProgram(self.program) glUniform3f(self.uniforms['bgcolor'],*p.bgcolor) glUniform1f(self.uniforms['sigma'],p.sigma) glUniform1f(self.uniforms['edge'],p.edge) glUseProgram(0) def draw(self): glUseProgram(self.program) glMatrixMode(GL_MODELVIEW) glPushMatrix() glLoadIdentity() glTranslatef(self.pos[0],self.pos[1],0.0) w2 = self.params.width/2 h2 = self.params.height/2 glBegin(GL_QUADS) glTexCoord2f(0.0,1.0) glVertex2f(-w2,-h2) glTexCoord2f(1.0,1.0) glVertex2f(w2,-h2) glTexCoord2f(1.0,0.0) glVertex2f(w2,h2) glTexCoord2f(0.0,0.0) glVertex2f(-w2,h2) glEnd() glUseProgram(0) glPopMatrix() DotLatticeBase = stim(ap_fs=0.0, ap_sigma=100.0, ap_edge=4.0, gamma=70.0, theta=10.0, dx=40.0, r=1.5, dot_size=150.0, dot_sigma=0.25, dot_fs=0.0, dot_edge=10.0, dot_c=0.4, dot_phi=0.2) class DotLattice(DotLatticeBase):
[docs] """A periodic array of dots organized along several orientations with a constant inter-dot distance within orientation. The "dots" can be solid disks or Gaussian blobs, or they can have more complex shapes, such as luminance gratings characterized by the same of different spatial frequencies and orientations. :param ap_fs: spatial frequency of the mask :param ap_sigma: spatial frequency of the mask :param ap_edge: power of apperture's Gaussian (slope) :param gamma: angle :param theta: angle of A nd B directions within lattice :param dx: parameter a, shortest distance :param r: aspect ratio :param dot_size: size of dot's texture :param dot_sigma: width of dot's Gaussian :param dot_fs: spatial frequency of dot's grating :param dot_edge: power of dot's Gaussian (slope) :param dot_c: dot color [0.0,1.0] :param dot_phi: phase of dot's grating """ def __init__(self,pos,params=Params()): self.params = copy_params(self._defaults,params) p = self.params self.pos = pos # prepare image of dot of the lattice self.dot = make_dot(p.dot_sigma,p.dot_fs,p.dot_phi,p.dot_edge).get_texture() # and (x,y) coordinate for each dot lw = xg(0.01,p.ap_sigma,p.ap_edge) # "xg" is a helper that return x at known y of Gaussian # genrate dot center coordintes within the circle of radius lw xy = make_lattice(lw,p.dx,p.r,p.gamma,p.theta) # to draw thw dots we will use OpenGL DrawArray through pyglet's vertex_list N = xy.shape[0] self.vlist = pyglet.graphics.vertex_list(N, 'v2d/stream', 'c4f') self.xy = from_ctypes(self.vlist.vertices,'f8',(N,2)) self.xy[...] = xy self.colors = from_ctypes(self.vlist.colors,'f4',(N, 4)) self.colors[:,:] = p.dot_c self.colors[:,3] = 1.0 # prepare the mask (apperture) w = 2*int(1.1*abs(self.xy).max()) self.mask = make_mask(w,w,p.ap_sigma,p.ap_edge) # store the parameter in this class def draw(self): p = self.params glPushMatrix() glLoadIdentity() glTranslatef(self.pos[0],self.pos[1],0.0) glRotatef(p.gamma,0.0,0.0,1.0) glEnable(GL_POINT_SPRITE) glEnable(GL_TEXTURE_2D) glPointSize(p.dot_size) glBindTexture(GL_TEXTURE_2D,self.dot.id) self.vlist.draw(pyglet.gl.GL_POINTS) glDisable(GL_TEXTURE_2D) glDisable(GL_POINT_SPRITE) # draw the mask (apperture) self.mask.draw() glPopMatrix() class Lattice(stim(width=400.0,height=400.0,
gamma=70.0, theta=10.0, dx=40.0, r=1.5, dot_size=150.0, dot_sigma=0.25, dot_fs=0.0, dot_edge=10.0, dot_c=0.4, dot_phi=0.2, slant=0.0,tilt=0.0)): """Dot-lattice stimulus :param width: width of stimulus in pixels :param height: height of stimulus in pixels :param gamma: angle :param theta: angle :param dx: parameter a, shortest distance :param r: aspect ratio :param dot_size: size of dot's texture :param dot_sigma: width of dot's Gaussian :param dot_fs: spatial frequency of dot's grating :param dot_edge: power of dot's Gaussian (slope) :param dot_c: dot contrast [0.0,1.0] :param dot_ph: phase of dot's grating """ def __init__(self,pos,params=Params()): self.params = copy_params(self._defaults,params) p = self.params self.pos = pos # prepare image of dot of the lattice self.dot = make_dot(p.dot_sigma,p.dot_fs,p.dot_phi,p.dot_edge).get_texture() # and (x,y) coordinate for each dot xy = make_lattice(p.width/2.0,p.dx,p.r,p.gamma,p.theta) # to draw the dots we will use OpenGL DrawArray through pyglet's vertex_list N = xy.shape[0] self.vlist = pyglet.graphics.vertex_list(N, 'v2d/stream', 'c4f') self.xy = from_ctypes(self.vlist.vertices,'f8',(N,2)) self.xy[...] = xy self.colors = from_ctypes(self.vlist.colors,'f4',(N, 4)) self.colors[:,:] = 1.0 def draw(self): p = self.params glPushMatrix() glLoadIdentity() glTranslatef(self.pos[0],self.pos[1],0.0) glEnable(GL_POINT_SPRITE) glEnable(GL_TEXTURE_2D) glPointSize(p.dot_size) glRotatef(-p.slant,1.0,0.0,0.0) glRotatef(p.tilt,0.0,1.0,0.0) glBindTexture(GL_TEXTURE_2D,self.dot.id) self.vlist.draw(pyglet.gl.GL_POINTS) glDisable(GL_TEXTURE_2D) glDisable(GL_POINT_SPRITE) glPopMatrix() class Pill(stim(th=0.0,R=50.0,d=3.0,alpha=1.0)):
[docs] """Orientation circle stimulus. This Stimulus is useful as a response choice for orientation selection. :param th: size of stimulus :param R: radius :param d: width of orientation bar :param alpha: stimulus' opacity (1.0 is opaque) """ def __init__(self,pos,params=Params()): self.params = copy_params(self._defaults,params) self.pos = pos p = self.params ld = lambda t,x,y: abs(-sin(t)*x+cos(t)*y) w2 = int(ceil(p.R*1.2)) y,x = mgrid[w2:-w2-1:-1,-w2:w2+1] apill = (255*g2f(x/p.R,y/p.R,1.0,40.0)).astype('u1') apill = (apill * (1-exp(-0.5*(ld(p.th,x,y)/p.d)**4.0))).astype('u1') h,w = apill.shape self.image = pyglet.image.ImageData(w,h,'L',apill.tostring(),-w) self.image.anchor_x = w//2 self.image.anchor_y = h//2 def draw(self): glColor4f(1.0,1.0,1.0,self.params.alpha) self.image.blit(self.pos[0],self.pos[1]) class OrientCircle(stim(th=0.0,R=50.0,d=3.0,alpha=1.0)):
"""Orientation circle stimulus :param th: size of stimulus :param R: radius :param d: width of orientation bar """ frag_source = """ uniform float th, sigma, gap, alpha; float edge = 40.0; float lined(float t, float x, float y) { return abs(-sin(t)*x+cos(t)*y); } float g(float x, float s2, float e2) { return exp(-0.5*pow(abs(x)/s2,e2)); } void main( void ) { float x = gl_TexCoord[0].x - 0.5; float y = -(gl_TexCoord[0].y - 0.5); float c = exp(-0.5*pow(x*x + y*y,edge/2.0)/pow(sigma,edge)); c = c*(1.0-g(lined(th,x,y),gap,4.0)); gl_FragColor = vec4( c,c,c,alpha ); } """ def __init__(self,pos,params=Params()): self.pos = pos self.params = copy_params(self._defaults,params) p = self.params self.shader = Shader(self.frag_source) self.program = self.shader.program self.uniforms = dict(map(self.shader.uniform,['th','sigma','gap','alpha'])) glUseProgram(self.program) self.w = p.R*2.5 glUniform1f(self.uniforms['th'],p.th) glUniform1f(self.uniforms['sigma'],0.4) glUniform1f(self.uniforms['gap'],p.d/self.w) glUniform1f(self.uniforms['alpha'],p.alpha) glUseProgram(0) def draw(self): p = self.params x, y = self.pos w2 = self.w/2.0 glUseProgram(self.program) glPushMatrix() glLoadIdentity() glTranslatef(self.pos[0],self.pos[1],0.0) glBegin(GL_QUADS) glTexCoord2f(0.0,1.0) glVertex2f(-w2,-w2) glTexCoord2f(1.0,1.0) glVertex2f(w2,-w2) glTexCoord2f(1.0,0.0) glVertex2f(w2,w2) glTexCoord2f(0.0,0.0) glVertex2f(-w2,w2) glEnd() glPopMatrix() glUseProgram(0) class Text(stim(msg='Text',size=24)): """Simple text label stimulus/ :param msg: string to display :params size: font size (default 24) """ def __init__(self,pos,params=Params()): self.params = copy_params(self._defaults,params) self.pos = pos p = self.params self.label = text.Label(p.msg, font_name='Times New Roman', font_size=p.size, anchor_x='center', anchor_y='center', x=pos[0], y=pos[1]) def draw(self): glEnable(GL_BLEND) glEnable(GL_TEXTURE_2D) self.label.draw() class Dot(stim(c=1.0)):
[docs] """Gaussian dot stimulus. :param c: contrast [0.0,1.0] """ def __init__(self,pos,params=Params()): self.params = copy_params(self._defaults,params) im = make_dot(0.17,0.0,0.0,2.0,64) w = im.width h = im.height im.anchor_x = w//2 im.anchor_y = h//2 self.image = im self.pos = pos def draw(self): c = self.params.c glColor4f(c,c,c,1.0) self.image.blit(self.pos[0],self.pos[1]) class RDK(stim(width=200.0,n0=500,n1=500,d0=[0.0,1.0],d1=[0.3,-1.0],
theta=0.0,tilt=0.0,slant=0.0)): """Random Dot Cinematogram Stimulus consists of two groups of dots. Dots within groups move together, motion of each group can be controlled independently. :param width: width and height in pixels :param n0: number of dots in group 1 :param n1: number of dots in group 2 :param d0: X,Y speed of group 1 (xy[t+1] = xy[t] + d0) :param d1: X,Y speed of group 2 (xy[t+1] = xy[t] + d0) :param theta: rotation in screen plane :param tilt: tilt angle :param slant: slant angle """ def __init__(self,pos,params=Params()): self.pos = pos self.batch = pyglet.graphics.Batch() self.params = copy_params(self._defaults,params) p = self.params self.clock = pyglet.clock.Clock() self.t0 = self.clock.time() N = p.n0 + p.n1 self.dot = make_dot(0.25,0.0,0.0,4.0).get_texture() self.vlist = self.batch.add(N, GL_POINTS, None, 'v3d', 'c4f/stream') self.xy = from_ctypes(self.vlist.vertices,'f8',(N,3)) self.xy0 = self.xy[:p.n0,:2] # Group0 xy coordinates self.xy1 = self.xy[p.n0:,:2] # Group1 xy coordinates self.xy0[...] = rand(p.n0,2)*r_[p.width,p.width] self.xy1[...] = rand(p.n1,2)*r_[p.width,p.width] self.colors = from_ctypes(self.vlist.colors,'f4',(N,4)) self.colors[...] = 1.0 self.vlist._vertices_cache.invalidate() self.vlist._colors_cache.invalidate() def draw(self): p = self.params t = self.clock.time()-self.t0 self.xy0[...] = (self.xy0 + array(p.d0))%p.width self.xy1[...] = (self.xy1 + array(p.d1))%p.width self.vlist._vertices_cache.invalidate() glPushMatrix() glLoadIdentity() glTranslatef(self.pos[0]-p.width/2.0,self.pos[1]-p.width/2.0,0.0) glRotatef(-p.slant,1.0,0.0,0.0) glRotatef(p.tilt,0.0,1.0,0.0) glRotatef(p.theta,0.0,0.0,1.0) glEnable(GL_POINT_SPRITE) glEnable(GL_TEXTURE_2D) glPointSize(10.0) glBindTexture(GL_TEXTURE_2D,self.dot.id) self.vlist.draw(pyglet.gl.GL_POINTS) glDisable(GL_TEXTURE_2D) glDisable(GL_POINT_SPRITE) glPopMatrix() class RDKMod(stim(width=200.0,n=500,theta=0.0,a=1.0,b=1.0,
[docs] f=0.0,phi=0.0,noise=0.0,life=500.0,gamma=0.0, tilt=0.0,slant=0.0)): """Random Dot Cinematogram with modulated speed Dots move together along axis controlled by "theta" parameter. Speed of each dot is controlled by vx = a + b*cos(2*pi*f*cos(gamma)*x[0] + phi) vy = a + b*cos(2*pi*f*sin(gamma)*y[0] + phi) The speed is computed from the initial location (x[0],y[0]) of each dot. Then R = rand-0.5 x[t+1] = x[t] + vx + vx*noise*R y[t+1] = y[t] + vy + vy*noise*R :param width: width and height in pixels :param n: number of dots :param theta: motion direction in radians :param gamma: angle of modulation relative to the motion direction in radians :param a: speed modulation parameter a :param b: speed modulation parameter b :param f: speed modulation parameter f :param phi: speed modulation parameter phi :param tilt: tilt angle :param slant: slant angle """ def __init__(self,pos,params=Params()): self.pos = pos self.batch = pyglet.graphics.Batch() self.params = copy_params(self._defaults,params) p = self.params self.clock = pyglet.clock.Clock() self.t0 = self.clock.time() N = p.n self.dot = make_dot(0.25,0.0,0.0,4.0).get_texture() self.vlist = self.batch.add(N, GL_POINTS, None, 'v3d', 'c4f/stream') self.xy = from_ctypes(self.vlist.vertices,'f8',(N,3)) self.xy0 = self.xy[:,:2] # Group0 xy coordinates self.xy0[...] = rand(p.n,2) #v = p.a + p.b*cos(2*pi*p.f*self.xy0[:,1] + p.phi) rx = cos(p.gamma)*self.xy0[:,0] + sin(p.gamma)*self.xy0[:,1] v = p.a + p.b*cos(2*pi*p.f*rx + p.phi) self.xy0 *= r_[p.width,p.width] self.speed = [1.0,0.0]*v.reshape(-1,1) self.colors = from_ctypes(self.vlist.colors,'f4',(N,4)) self.colors[...] = 1.0 self.vlist._vertices_cache.invalidate() self.vlist._colors_cache.invalidate() self.draw = self.draw0 def draw0(self): self.life = self.clock.time() + self.params.life/1000.0*rand(self.params.n) self.draw = self.draw1 def draw1(self): p = self.params i = self.clock.time() - self.life > p.life/1000.0 newxy = rand(i.sum(),2) rx = cos(p.gamma)*newxy[:,0] + sin(p.gamma)*newxy[:,1] v = p.a + p.b*cos(2*pi*p.f*rx + p.phi) self.xy0[i,:] = r_[p.width,p.width]*newxy self.speed[i,:] = [1.0,0.0]*v.reshape(-1,1) self.life[i] = self.clock.time() + self.params.life/1000.0*rand(i.sum()) vn = p.noise*(rand(p.n)-0.5) self.xy0[...] = (self.xy0 + self.speed + self.speed*vn.reshape(-1,1))%p.width self.vlist._vertices_cache.invalidate() glPushMatrix() glLoadIdentity() glTranslatef(self.pos[0],self.pos[1],0.0) glRotatef(180.0*p.theta/pi,0.0,0.0,1.0) glRotatef(-p.slant,1.0,0.0,0.0) glRotatef(p.tilt,0.0,1.0,0.0) glTranslatef(-p.width/2.0,-p.width/2.0,0.0) glEnable(GL_POINT_SPRITE) glEnable(GL_TEXTURE_2D) glPointSize(10.0) glBindTexture(GL_TEXTURE_2D,self.dot.id) self.vlist.draw(pyglet.gl.GL_POINTS) glDisable(GL_TEXTURE_2D) glDisable(GL_POINT_SPRITE) glPopMatrix() class Rectangle(stim(width=100.0,height=100.0,angle=0.0,linewidth=1.0,
facecolor=(1.0,1.0,1.0,1.0),edgecolor=[])): def __init__(self,pos,params=Params()): from itertools import cycle self.pos = pos if hasattr(params,'facecolor') and not iterable(params.facecolor): params.facecolor = [params.facecolor]*3 self.params = copy_params(self._defaults,params) p = self.params self.clock = pyglet.clock.Clock() self.t0 = self.clock.time() # make indexed vertex_list ind = array([0,1,2,0,2,3]) xy = r_[0.0,0.0,1.0,0.0,1.0,1.0,0.0,1.0].reshape(-1,2) - 0.5 self.vlist = pyglet.graphics.vertex_list_indexed(4, ind, 'v2d/stream', 'c4f') N = 4 self.xy = from_ctypes(self.vlist.vertices,'f8',(N,2)) self.xy[...] = xy * r_[p.width,p.height] self.colors = from_ctypes(self.vlist.colors,'f4',(N, 4)) self.colors[:,3] = 1.0 self.colors[:,:len(p.facecolor)] = p.facecolor self.vlist._vertices_cache.invalidate() self.vlist._colors_cache.invalidate() # outline self.edgelist = pyglet.graphics.vertex_list(4, 'v2d/stream', 'c4f') self.edgexy = from_ctypes(self.edgelist.vertices,'f8',(N,2)) self.edgexy[...] = self.xy self.edgecolors = from_ctypes(self.edgelist.colors,'f4',(N, 4)) self.edgecolors[:,3] = 1.0 self.edgecolors[:,:len(p.edgecolor)] = p.edgecolor self.edgelist._vertices_cache.invalidate() self.edgelist._colors_cache.invalidate() # if not iterable(p.angle): p.angle = [p.angle] self.angle = cycle(p.angle) def draw(self): p = self.params glMatrixMode(GL_MODELVIEW) glPushMatrix() glLoadIdentity() glTranslatef(self.pos[0],self.pos[1],0.0) glRotatef(self.angle.next(),0.0,0.0,1.0) glDisable(GL_TEXTURE_2D) glEnable(GL_MULTISAMPLE_ARB) #glEnable(GL_POLYGON_SMOOTH); #glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST) if p.facecolor: self.vlist.draw(GL_TRIANGLES) if p.edgecolor: glLineWidth(p.linewidth) self.edgelist.draw(GL_LINE_LOOP) glEnable(GL_TEXTURE_2D) #glDisable(GL_POLYGON_SMOOTH); #glHint(GL_POLYGON_SMOOTH_HINT, GL_DONT_CARE) glDisable(GL_MULTISAMPLE_ARB) glPopMatrix() class Cube(stim(width=100.0,height=100.0,depth=100.0,angles=0.0,linewidth=1.0, facecolor=(1.0,1.0,1.0,1.0),edgecolor=[])): """Cube rotated and scaled in three dimensions. The colors of vertices and faces are controlled independent of one another. :param width: cube's width :param height: cube's height :param depth: cube's depth :param angles: collection of angles, each frame is rendered with angle taken from cycle(angles) iterator :param linewidth: line thickness used for edges :param facecolor: cube's face colors :param edgecolor: edge colors color """ def __init__(self,pos,params=Params()): from itertools import cycle self.pos = pos if hasattr(params,'facecolor') and not iterable(params.facecolor): params.facecolor = [params.facecolor]*3 self.params = copy_params(self._defaults,params) p = self.params self.clock = pyglet.clock.Clock() self.t0 = self.clock.time() # make indexed vertex_list ind = array([0,1,2,0,2,3, 1,5,6,1,6,2, 5,4,7,5,7,6, 4,0,3,4,3,7, 0,1,5,0,5,4, 3,2,6,3,6,7]) xy = r_[0.0,0.0,0.0, 1.0,0.0,0.0, 1.0,1.0,0.0, 0.0,1.0,0.0, 0.0,0.0,1.0, 1.0,0.0,1.0, 1.0,1.0,1.0, 0.0,1.0,1.0].reshape(-1,3) - 0.5 N = xy.shape[0] self.vlist = pyglet.graphics.vertex_list_indexed(N, ind, 'v3d/stream', 'c4f') self.xy = from_ctypes(self.vlist.vertices,'f8',(N,3)) self.xy[...] = xy * r_[p.width,p.height,p.depth] self.colors = from_ctypes(self.vlist.colors,'f4',(N, 4)) self.colors[:,3] = 1.0 self.colors[:,:len(p.facecolor)] = p.facecolor self.vlist._vertices_cache.invalidate() self.vlist._colors_cache.invalidate() # outline indw = array([0,1,1,2,2,3,3,0, 1,5,5,6,6,2,2,1, 5,4,4,7,7,6,6,5, 4,0,0,3,3,7,7,4, 0,1,1,5,5,4,4,0, 3,2,2,6,6,7,7,3]) self.edgelist = pyglet.graphics.vertex_list_indexed(N, indw, 'v3d/stream', 'c4f') self.edgexy = from_ctypes(self.edgelist.vertices,'f8',(N,3)) self.edgexy[...] = self.xy self.edgecolors = from_ctypes(self.edgelist.colors,'f4',(N, 4)) self.edgecolors[:,3] = 1.0 self.edgecolors[:,:len(p.edgecolor)] = p.edgecolor self.edgelist._vertices_cache.invalidate() self.edgelist._colors_cache.invalidate() # if not iterable(p.angle): p.angle = [p.angle] self.angle = cycle(p.angle) def draw(self): p = self.params glMatrixMode(GL_MODELVIEW) glPushMatrix() glLoadIdentity() glTranslatef(self.pos[0],self.pos[1],0.0) glRotatef(self.angle.next(),0.0,1.0,1.0) glDisable(GL_TEXTURE_2D) glEnable(GL_MULTISAMPLE_ARB) #glEnable(GL_POLYGON_SMOOTH); #glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST) if p.facecolor: self.vlist.draw(GL_TRIANGLES) if p.edgecolor: glLineWidth(p.linewidth) self.edgelist.draw(GL_LINES) glEnable(GL_TEXTURE_2D) #glDisable(GL_POLYGON_SMOOTH); #glHint(GL_POLYGON_SMOOTH_HINT, GL_DONT_CARE) glDisable(GL_MULTISAMPLE_ARB) glPopMatrix() class Letters(stim(function='highlight_col',dt=0.1,color=1.0,dim=0.25)): """Alphabet Grid. A grid of letters commonly used in research of Brain Computer Interfaces (BCI). The letters are shown in a grid of 6x6 cells. The cells can be periodically highlighted. :param function: function controlling highlighting over time ['highlight_none','highlight_rand','highlight_row', 'highlight_col','fade'] :param dt: time between change of highlighted group (default 0.1s = 100ms) :param color: color or intensity of highlighted cell (default 1.0) :param dim: gain of a dimmed cell (default (0.25) """ def __init__(self,pos,params): self.params = copy_params(self._defaults) self.params.__dict__.update(params.__dict__) fname = 'images/grid.png' rows, cols = 6, 6 self.clock = clock.Clock() self.t0 = None self.pos = pos self.im = pyglet.image.load(fname) self.ig = pyglet.image.ImageGrid(self.im,rows,cols) self.tg = pyglet.image.TextureGrid(self.ig) self.tex = self.ig.texture # prepare image of dot of the lattice xy = make_lattice(400.0) # to draw thw dots we will use OpenGL DrawArray through pyglet's vertex_list N = xy.shape[0] self.batch, self.vlist, self.indices = make_indarray(N) self.xy = from_ctypes(self.vlist.vertices,'f8',(4*N,2)) self.colors = from_ctypes(self.vlist.colors,'f4',(N,4,4)) self.txy = from_ctypes(self.vlist.tex_coords,'f4',(4*N,2)) # fill the arrays self.colors[...] = 1.0 self.colors[:,:,:3] = self.params.color rect = r_[-0.5,-0.5,0.5,-0.5,0.5,0.5,-0.5,0.5].reshape(-1,2) for i in xrange(rows*cols): self.xy[i*4:(i+1)*4,:] = 64*rect + xy[i] self.txy[i*4:(i+1)*4,:] = array(self.tg[i].tex_coords).reshape(-1,3)[:,:2] self.prev = -1 self.previ = -1 self.prevj = -1 def highlight_none(self,t): self.colors[:,:,:3] = self.params.dim self.vlist._colors_cache.invalidate() def highlight_rand(self,t): now = int(t//(5*self.params.dt)) if now != self.prev: self.prev = now self.colors[:,:,:3] = self.params.dim self.colors[:,:,:3] = colors(rand(36)) self.vlist._colors_cache.invalidate() def highlight_row(self,t): j = int((-t//self.params.dt)%6) self.colors[:,:,:3] = self.params.dim self.colors[[j*6+arange(6)],:,:3] = 1.0 self.vlist._colors_cache.invalidate() if self.prevj != j: win.event('CHANGE',['ROW',j]) self.prevj = j def highlight_col(self,t): i = int((t//self.params.dt)%6) self.colors[:,:,:3] = self.params.dim self.colors[[i+arange(0,36,6)],:,:3] = 1.0 self.vlist._colors_cache.invalidate() if self.previ != i: win.event('CHANGE',['COL',i]) self.previ = i def fade(self,i): c = self.colors[0,0,0] self.colors[:,:,:3] = fmax(c - 0.01*c,self.params.dim) self.vlist._colors_cache.invalidate() def draw(self): if self.t0 is None: self.t0 = self.clock.time() func = getattr(self,self.params.function) func(self.clock.time()-self.t0) glPushMatrix() glLoadIdentity() glTranslatef(self.pos[0],self.pos[1],0.0) glEnable(GL_TEXTURE_2D) glBindTexture(GL_TEXTURE_2D,self.tex.id) self.vlist.draw(GL_TRIANGLES) glDisable(GL_TEXTURE_2D) glPopMatrix() class CheckerBoard(stim(width=512,height=512,pw=64,ph=64,Nrep=5, color0=(1.0,1.0,1.0,1.0),color1=(0.0,0.0,0.0,1.0))): def __init__(self,pos,params=Params()): import itertools as it self.pos = pos self.params = copy_params(self._defaults,params) self.clock = pyglet.clock.Clock() self.t0 = self.clock.time() C0 = tuple( int(255*x) for x in self.params.color0) C1 = tuple( int(255*x) for x in self.params.color1) pw, ph = self.params.pw, self.params.ph im1 = pyglet.image.CheckerImagePattern(C0,C1).create_image(pw,ph) im2 = pyglet.image.CheckerImagePattern(C1,C0).create_image(pw,ph) bg1 = pyglet.image.TileableTexture.create_for_image(im1) bg2 = pyglet.image.TileableTexture.create_for_image(im2) self.mask = pyglet.image.create(128,128,pyglet.image.SolidColorImagePattern((128,128,128,255))) self.mask.anchor_x = 64 self.mask.anchor_y = 64 self.bg = [bg1,bg2] self.stims = it.cycle( [bg1]*self.params.Nrep+[bg2]*self.params.Nrep ) def set_reps(self,Nrep): import itertools as it self.params.Nrep = Nrep self.stims = it.cycle( [self.bg[0]]*self.params.Nrep+[self.bg[1]]*self.params.Nrep ) def draw(self): x, y = self.pos p = self.params self.stims.next().blit_tiled(x-p.width//2,y-p.height//2,0,p.width,p.height) glEnable(GL_BLEND) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) glColor4f(1.0,1.0,1.0,0.5) self.mask.blit(x,y) glDisable(GL_BLEND) class Image(stim(fname='',width=400.0)): """Tiled Image stimulus. :param fname: image filename """ def __init__(self,pos,params): self.params = copy_params(self._defaults) self.params.__dict__.update(params.__dict__) fname = self.params.fname rows, cols = 6, 6 self.clock = clock.Clock() self.t0 = None self.pos = pos self.im = pyglet.image.load(fname) self.ig = pyglet.image.ImageGrid(self.im,rows,cols) self.tg = pyglet.image.TextureGrid(self.ig) self.tex = self.ig.texture # prepare image of dot of the lattice xy = make_lattice(400.0) # to draw thw dots we will use OpenGL DrawArray through pyglet's vertex_list N = xy.shape[0] self.batch, self.vlist, self.indices = make_indarray(N) self.xy = from_ctypes(self.vlist.vertices,'f8',(4*N,2)) self.colors = from_ctypes(self.vlist.colors,'f4',(N,4,4)) self.txy = from_ctypes(self.vlist.tex_coords,'f4',(4*N,2)) # fill the arrays self.colors[...] = 1.0 self.colors[:,:,:3] = self.params.color rect = r_[-0.5,-0.5,0.5,-0.5,0.5,0.5,-0.5,0.5].reshape(-1,2) for i in xrange(rows*cols): self.xy[i*4:(i+1)*4,:] = 64*rect + xy[i] self.txy[i*4:(i+1)*4,:] = array(self.tg[i].tex_coords).reshape(-1,3)[:,:2] def draw(self): if self.t0 is None: self.t0 = self.clock.time() func = getattr(self,self.params.function) func(self.clock.time()-self.t0) glPushMatrix() glLoadIdentity() glTranslatef(self.pos[0],self.pos[1],0.0) glEnable(GL_TEXTURE_2D) glBindTexture(GL_TEXTURE_2D,self.tex.id) self.vlist.draw(GL_TRIANGLES) glDisable(GL_TEXTURE_2D) glPopMatrix() class Lines(stim(x=[-100.0,100.0,100.0,-100.0],y=[-100.0,-100.0,100.0,100.0],z=0.0, colors=[1.0,1.0,1.0,1.0],linewidth=1.0)): def __init__(self,pos,params=Params()): from itertools import cycle self.pos = pos if hasattr(params,'colors') and not iterable(params.colors): params.colors = [params.colors]*len(params.x) self.params = copy_params(self._defaults,params) p = self.params self.clock = pyglet.clock.Clock() self.t0 = self.clock.time() # make vertex_list N = len(params.x) self.vlist = pyglet.graphics.vertex_list(N, 'v3d/stream', 'c4f') self.xy = from_ctypes(self.vlist.vertices,'f8',(N,3)) self.xy[:,0] = p.x self.xy[:,1] = p.y self.xy[:,2] = p.z self.colors = from_ctypes(self.vlist.colors,'f4',(N, 4)) self.colors[...] = p.colors self.vlist._vertices_cache.invalidate() self.vlist._colors_cache.invalidate() def draw(self): p = self.params glMatrixMode(GL_MODELVIEW) glPushMatrix() glLoadIdentity() glTranslatef(self.pos[0],self.pos[1],0.0) glDisable(GL_TEXTURE_2D) glEnable(GL_MULTISAMPLE_ARB) #glEnable(GL_POLYGON_SMOOTH); #glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST) glLineWidth(p.linewidth) self.vlist.draw(GL_LINES) glPointSize(p.linewidth) self.vlist.draw(GL_POINTS) glEnable(GL_TEXTURE_2D) #glDisable(GL_POLYGON_SMOOTH); #glHint(GL_POLYGON_SMOOTH_HINT, GL_DONT_CARE) glDisable(GL_MULTISAMPLE_ARB) glPopMatrix() grating_frag_source = """ precision highp float; uniform float time; uniform float fs; uniform float contr; void main() { float x = gl_TexCoord[0].x - 0.5; float y = -(gl_TexCoord[0].y - 0.5); float m = exp(-0.5*(x*x + y*y)/pow(0.17,2.0)); float c = m*contr*cos(fs*x + 2.0*time); c = (1.0+c)/2.0; gl_FragColor.rgba = vec4(c,c,c,1.0); } """ class GLSLStimulus(stim(width=300.0, height=300.0, vert_source='',frag_source=grating_frag_source, params=[('fs','glUniform1f',40.0), ('contr','glUniform1f',1.0)])): """GLSL stimulus :param width: size of stimulus in pixels :param height: size of stimulus in pixels :param vert_source: GLSL vertex program :param frag_source: GLSL fragment program :param params: GLSL program parameters """ def __init__(self,pos,params=Params()): self.pos = pos self.params = copy_params(self._defaults,params) self.clock = pyglet.clock.Clock() self.t0 = self.clock.time() self.shader = Shader(self.params.frag_source,self.params.vert_source) self.program = self.shader.program self.uniforms = {} self.uniforms['time'] = self.shader.uniform('time')[1] for name, glutype, val0 in self.params.params: self.uniforms[name] = self.shader.uniform(name)[1] glUseProgram(self.program) glUniform1f(self.uniforms['time'],self.t0) for name, glutype, val0 in self.params.params: getattr(gl,glutype,glUniform1f)(self.uniforms[name],val0) glUseProgram(0) def draw(self): p = self.params x, y = self.pos w2 = p.width/2.0 glUseProgram(self.program) time = self.clock.time()-self.t0 glUniform1f(self.uniforms['time'],time) glPushMatrix() glLoadIdentity() glTranslatef(self.pos[0],self.pos[1],0.0) glBegin(GL_QUADS) glTexCoord2f(0.0,1.0) glVertex2f(-w2,-w2) glTexCoord2f(1.0,1.0) glVertex2f(w2,-w2) glTexCoord2f(1.0,0.0) glVertex2f(w2,w2) glTexCoord2f(0.0,0.0) glVertex2f(-w2,w2) glEnd() glPopMatrix() glUseProgram(0)