visual.py :  » Game-2D-3D » PsychoPy » PsychoPy-0.96.02 » psychopy » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Game 2D 3D » PsychoPy 
PsychoPy » PsychoPy 0.96.02 » psychopy » visual.py
"""To control the screen and visual stimuli for experiments
"""
import psychopy.misc
import psychopy #so we can get the __path__
from psychopy import core,ext,log
import psychopy.event
import monitors
import Image
import sys, os, time, glob, copy
import makeMovies

import numpy
from numpy import sin,cos,pi

#shaders will work but require OpenGL2.0 drivers AND PyOpenGL3.0+
try:
    import ctypes
    import pyglet
    #pyglet.options['debug_gl'] = False#must be done before importing pyglet.gl or pyglet.window
    import pyglet.gl, pyglet.window, pyglet.image, pyglet.font, pyglet.media
    import _shadersPyglet
    import gamma
    havePyglet=True    
except:
    havePyglet=False    


#import _shadersPygame
try:
    import OpenGL.GL, OpenGL.GL.ARB.multitexture
    import pygame
    havePygame=True
    if OpenGL.__version__ > '3':
        cTypesOpenGL = True
    else:
        cTypesOpenGL = False
except:
    havePygame=False
global GL, GLU, GL_multitexture, _shaders#will use these later to assign the pyglet or pyopengl equivs

#check for advanced drawing abilities
#actually FBO isn't working yet so disable
try:
    import OpenGL.GL.EXT.framebuffer_object as FB
    haveFB=False
except:
    haveFB=False


#try to get GLUT
try:
    from OpenGL import GLUT
    haveGLUT=True
except:
    log.warning('GLUT not available - is the GLUT library installed on the path?')
    haveGLUT=False

global DEBUG; DEBUG=False

_depthIncrements = {'pyglet':+0.001, 'pygame':-0.001, 'glut':-0.001}

class Window:
    """Used to set up a context in which to draw objects,
    using either PyGame (python's SDL binding) or GLUT.
    These two types have different structure but can achieve
    similar results. Pygame follows a procedural model, that
    is you specify from line to line what happens next. This
    is usually more intuitive for psychophysics exps. Glut
    uses a callback or event-driven model, where you specify
    functions to be run on certain events (when a button is
    pressed do...).
    """
    def __init__(self,
                 size = (800,600),
                 pos = None,
                 rgb = (0.0,0.0,0.0),
                 dkl=None,
                 lms=None,
                 fullscr = 0,
                 allowGUI=True,
                 monitor=dict([]),
                 bitsMode=None,
                 winType=None,
                 units='norm',
                 gamma = None,
                 blendMode='avg',
                 screen=0):
        """
        **Arguments:**

            - **size** :  size of the window in pixels (X,Y)
            - **rgb** :  background color (R,G,B) from -1.0 to 1.0
            - **fullScr** :  0(in a window), 1(fullscreen) NB Try using fullScr=0, allowGUI=0
            - **allowGUI** :  0,1 If set to 1, window will be drawn with no frame and no buttons to close etc...
            - **winType** :  'pyglet', 'pygame' or 'glut' (if None then PsychoPy will try to use Pyglet, Pygame, GLUT in that order)
            - **monitor** :  the name of your monitor (from MonitorCentre) or an actual ``Monitor`` object
            - **units** :  'norm' (normalised),'deg','cm','pix' Defines the default units of stimuli drawn in the window (can be overridden by each stimulus)
            
        The following args will override **monitor** settings(see above):

            - **gamma** : 1.0, monitor gamma for linearisation (will use Bits++ if possible)
            - **bitsMode** : None, 'fast', ('slow' mode is deprecated). Defines how (and if) the Bits++ box will be used. 'Fast' updates every frame by drawing a hidden line on the top of the screen.

        """
        self.size = numpy.array(size, numpy.int)
        self.pos = pos                 
        if type(rgb)==float or type(rgb)==int: #user may give a luminance val
            self.rgb=numpy.array((rgb,rgb,rgb), float)
        else:
            self.rgb = numpy.asarray(rgb, float)
        self._defDepth=0.0
        
        #settings for the monitor: local settings (if available) override monitor
        #if we have a monitors.Monitor object (psychopy 0.54 onwards)
        #convert to a Monitor object
        if monitor==None:
            monitor = monitors.Monitor('__blank__')
        if type(monitor)==str:
            monitor = monitors.Monitor(monitor)
        elif type(monitor)==dict:
            #convert into a monitor object
            monitor = monitors.Monitor('temp',currentCalib=monitor,verbose=False)
        self.monitor = monitor

        #otherwise monitor will just be a dict
        self.scrWidthCM=monitor.getWidth()
        self.scrDistCM=monitor.getDistance()

        scrSize = monitor.getSizePix()
        if scrSize==None:
            self.scrWidthPIX=None
        else:self.scrWidthPIX=scrSize[0]

        if fullscr: self._isFullScr=1
        else:   self._isFullScr=0
        self.units = units
        self.screen = screen
        
        #setup bits++ if possible
        self.bitsMode = bitsMode #could be [None, 'fast', 'slow']
        if self.bitsMode!=None:
            from psychopy import bits
            self.bits = bits.BitsBox(self)
            self.haveBits = True

        #gamma
        if self.bitsMode!=None and hasattr(monitor, 'lineariseLums'):
            #ideally we use bits++ and provide a complete linearised lookup table
            #using monitor.lineariseLums(lumLevels)
            self.gamma=None
        if gamma != None and (type(gamma) in [float, int]):
            #an integer that needs to be an array
            self.gamma=[gamma,gamma,gamma]
        elif gamma != None:# and (type(gamma) not in [float, int]):
            #an array (hopefully!)
            self.gamma=gamma
        elif type(monitor.getGammaGrid())==numpy.ndarray:
            self.gamma = monitor.getGammaGrid()[1:,2]
        elif monitor.getGamma()!=None:
            self.gamma = monitor.getGamma()
        else: self.gamma = [1.0,1.0,1.0] #gamma wasn't set anywhere
        
        #colour conversions
        dkl_rgb = monitor.getDKL_RGB()
        if dkl_rgb!=None:
            self.dkl_rgb=dkl_rgb
        else: self.dkl_rgb = None
        lms_rgb = monitor.getLMS_RGB()
        if lms_rgb!=None:
            self.lms_rgb=lms_rgb
        else: self.lms_rgb = None

        #setup context and openGL()
        self.allowGUI = allowGUI
        if winType is None:#choose the default windowing
            if havePyglet: winType="pyglet"
            elif havePygame: winType="pygame"
            else: winType='glut'
                 
        #check whether FBOs are supported
        if blendMode=='add' and not haveFB:
            log.warning("""User requested a blendmode of "add" but framebuffer objects not available. You need PyOpenGL3.0+ to use this blend mode""")
            self.blendMode='average' #resort to the simpler blending without float rendering
        else: self.blendMode=blendMode
        
        #setup the context
        if winType is "glut": self._setupGlut()
        elif winType is "pygame": self._setupPygame()
        elif winType is "pyglet": self._setupPyglet()
        
        #check whether shaders are supported
        if winType=='pyglet':#we can check using gl_info
            if pyglet.gl.gl_info.get_version()>='2.0':
                    self._haveShaders=True
            else:
                self._haveShaders=False   
            #also check for GL_ARB_texture_float
            try:#some graphics cards require that a context is created before queries
                if not pyglet.gl_info.have_extension('GL_ARB_texture_float'):
                    self._haveShaders=False  
            except:
                pass
        else:
            self._haveShaders=False   
        self._setupGL()

        self.frameClock = core.Clock()#from psycho/core
        self.frames = 0         #frames since last fps calc
        self.movieFrames=[] #list of captured frames (Image objects)
        
        self.recordFrameIntervals=False
        self.frameIntervals=[]
        
        self._refreshThreshold=1/59.0
        if list(self.gamma)!=[1,1,1]:
            self.setGamma(self.gamma)#using either pygame or bits++
        self.lastFrameT = time.time()
        self.update()#do a screen refresh straight away


    def setRecordFrameIntervals(self, value=True):
        """To provide accurate measures of frame intervals, to determine whether frames
        are being dropped. Set this to False while the screen is not being updated
        e.g. during event.waitkeys() and set to True during critical parts of the script
        
        see also:
            Window.saveFrameIntervals()
        """
        self.recordFrameIntervals=value
    def saveFrameIntervals(self, fileName=None, clear=True):
        """Save recorded screen frame intervals to disk, as comma-separated values.
        
        Arguments:
            - fileName (=None). The filename (including path if necessary) in which 
            to store the data
        """
        if fileName==None: 
            fileName = 'lastFrameIntervals.log'
        if len(self.frameIntervals):
            intervalStr = str(self.frameIntervals)[1:-1]
            f = open(fileName, 'w')
            f.write(intervalStr)
            f.close()
        if clear:
            self.frameIntervals=[]
            
    def whenIdle(self,func):
        """Defines the function to use during idling (GLUT only)
        """
        GLUT.glutIdleFunc(func)
    def onResize(self, width, height):
        '''A default resize event handler.

        This default handler updates the GL viewport to cover the entire
        window and sets the ``GL_PROJECTION`` matrix to be orthagonal in
        window space.  The bottom-left corner is (0, 0) and the top-right
        corner is the width and height of the window in pixels.

        Override this event handler with your own to create another
        projection, for example in perspective.
        '''
        if height==0:
            height=1
        GL.glViewport(0, 0, width, height)
        GL.glMatrixMode(GL.GL_PROJECTION)
        GL.glLoadIdentity()
        GL.glOrtho(-1,1,-1,1, -1, 1)
        #GL.gluPerspective(90, 1.0*width/height, 0.1, 100.0)
        GL.glMatrixMode(GL.GL_MODELVIEW)
        GL.glLoadIdentity()
    
    def flip(self, clearBuffer=True):
        """Flip the front and back buffers after drawing everything for your frame.
        (This replaces the win.update() method, better reflecting what is happening underneath).
        
        win.flip(clearBuffer=True)#results in a clear screen after flipping        
        win.flip(clearBuffer=False)#the screen is not cleared (so represent the previous screen)
        """
        if haveFB:
            #need blit the frambuffer object to the actual back buffer

            FB.glBindFramebufferEXT(FB.GL_FRAMEBUFFER_EXT, 0)#unbind the framebuffer as the render target

            #before flipping need to copy the renderBuffer to the frameBuffer
            GL.glActiveTexture(GL.GL_TEXTURE0)
            GL.glEnable(GL.GL_TEXTURE_2D)
            GL.glBindTexture(GL.GL_TEXTURE_2D, self.frameTexture)
            GL.glBegin( GL.GL_QUADS )
            GL.glTexCoord2f( 0.0, 0.0 ) ; GL.glVertex2f( -1.0,-1.0 )
            GL.glTexCoord2f( 0.0, 1.0 ) ; GL.glVertex2f( -1.0, 1.0 )
            GL.glTexCoord2f( 1.0, 1.0 ) ; GL.glVertex2f( 1.0,   1.0 )
            GL.glTexCoord2f( 1.0, 0.0 ) ; GL.glVertex2f( 1.0,   -1.0 )
            GL.glEnd()

        #update the bits++ LUT
        if self.bitsMode == 'fast':
            self.bits._drawLUTtoScreen()

        if self.winType == "glut": GLUT.glutSwapBuffers()
        elif self.winType =="pyglet":
            #print "updating pyglet"
            #make sure this is current context            
            self.winHandle.switch_to()
            
            GL.glTranslatef(0.0,0.0,-5.0)

            self.winHandle.dispatch_events()#this might need to be done even more often than once per frame?
            pyglet.media.dispatch_events()#for sounds to be processed
            self.winHandle.flip()
            #self.winHandle.clear()
            GL.glLoadIdentity()
        else:
            if pygame.display.get_init():
                pygame.display.flip()
                pygame.event.pump()#keeps us in synch with system event queue
            else:
                core.quit()#we've unitialised pygame so quit
                        
        self.frames +=1
        now = core.getTime()
        deltaT = now - self.lastFrameT; self.lastFrameT=now
        if deltaT>self._refreshThreshold: log.warning('t of last frame was %.2fms (=1/%i)' %(deltaT*1000, 1/deltaT))
        
        if self.recordFrameIntervals:
            self.frameIntervals.append(deltaT)
        
        if haveFB:
            #set rendering back to the framebuffer object
            FB.glBindFramebufferEXT(FB.GL_FRAMEBUFFER_EXT, self.frameBuffer)
            
        #reset returned buffer for next frame
        if clearBuffer: GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT)
        else: GL.glClear(GL.GL_DEPTH_BUFFER_BIT)#always clear the depth bit
        self._defDepth=0.0#gets gradually updated through frame
        
    def update(self):
        """Deprecated: use Window.flip() instead        
        """
        self.flip(clearBuffer=True)#clearBuffer was the original behaviour for win.update()
        
    def getMovieFrame(self, buffer='front'):
        """
        Capture the current Window as an image.
        This can be done at any time (usually after a .update() command).

        Frames are stored in memory until a .saveMovieFrames(filename) command
        is issued. You can issue getMovieFrame() as often
        as you like and then save them all in one go when finished.
        """
        #GL.glLoadIdentity()
        #do the reading of the pixels
        if buffer=='back':
            GL.glReadBuffer(GL.GL_BACK)            
        else:
            GL.glReadBuffer(GL.GL_FRONT)
        
            #fetch the data with glReadPixels
        if self.winType=='pyglet':
            #pyglet.gl stores the data in a ctypes buffer
            bufferDat = (GL.GLubyte * (4 * self.size[0] * self.size[1]))()
            GL.glReadPixels(0,0,self.size[0],self.size[1], GL.GL_RGBA,GL.GL_UNSIGNED_BYTE,bufferDat)
            im = Image.fromstring(mode='RGBA',size=self.size, data=bufferDat)
        else:
            #pyopengl returns the data
            im = Image.fromstring(mode='RGBA',size=self.size,
                              data=GL.glReadPixels(0,0,self.size[0],self.size[1], GL.GL_RGBA,GL.GL_UNSIGNED_BYTE),
                          )
            
        im=im.transpose(Image.FLIP_TOP_BOTTOM)            
        im=im.convert('RGB')
        self.movieFrames.append(im)

    def saveMovieFrames(self, fileName, mpgCodec='mpeg1video'):
        """
        Writes any captured frames to disk. Will write any format
        that is understood by PIL (tif, jpg, bmp, png...)

        Usage:
            - ``myWin.writeMovieFrames('frame.tif')``
                #writes a series of frames as frame001.tif, frame002.tif etc...

        """
        fileRoot, fileExt = os.path.splitext(fileName)
        if len(self.movieFrames)==0:
            log.error('no frames to write - did you forget to update your window?')
            return
        else:
            log.info('writing %i frames' %len(self.movieFrames))
        if fileExt=='.gif': makeMovies.makeAnimatedGIF(fileName, self.movieFrames)
        elif fileExt in ['.mpg', '.mpeg']: makeMovies.makeMPEG(fileName, self.movieFrames, codec=mpgCodec)
        elif len(self.movieFrames)==1:
            self.movieFrames[0].save(fileName)
        else:
            frame_name_format = "%s%%0%dd%s" % (fileRoot, numpy.ceil(numpy.log10(len(self.movieFrames)+1)), fileExt)
            for frameN, thisFrame in enumerate(self.movieFrames):
               thisFileName = frame_name_format % (frameN+1,)
               thisFrame.save(thisFileName) 

    def fullScr(self):
        """Toggles fullscreen mode (GLUT only).

        Fullscreen mode for PyGame contexts must be set during initialisation
        of the Window() class
        """
        if self.winType is 'glut':
            if self._isFullScr:
                GLUT.glutReshapeWindow(int(self.size[0]), int(self.size[1]))
                self._isFullScr=0
            else:
                GLUT.glutFullScreen()
                self._isFullScr=1
        else:
            log.warning('fullscreen toggling is only available to glut contexts')

    def close(self):
        """Close the window (and reset the Bits++ if necess)."""
        if self.winType is 'GLUT':
            GLUT.glutDestroyWindow(self.handle)
        elif self.winType is 'pyglet':
            self.winHandle.close()
        else:
            #pygame.quit()
            pygame.display.quit()
        if self.bitsMode is not None:
            self.bits.reset()

    def go(self):
        """start the display loop (GLUT only)"""
        self.frameClock.reset()
        GLUT.glutMainLoop()

    def fps(self):
        """Report the frames per second since the last call to this function
        (or since the window was created if this is first call)"""
        fps = self.frames/(self.frameClock.getTime())
        self.frameClock.reset()
        self.frames = 0
        return fps
    
    def setScale(self, units, font='dummyFont', prevScale=[1.0,1.0]):
        """This method is called from within the draw routine and sets the
        scale of the OpenGL context to map between units. Could potentially be
        called by the user in order to draw OpenGl objects manually
        in each frame.

        The **units** can be 'norm'(normalised),'pix'(pixels),'cm' or
        'stroke_font'. The **font** argument is only used if units='stroke_font'
        """
        if units is "norm":
            thisScale = numpy.array([1.0,1.0])
        elif units in ["pix", "pixels"]:
            thisScale = 2.0/numpy.array(self.size)
        elif units is "cm":
            #windowPerCM = windowPerPIX / CMperPIX
            #                       = (window      /winPIX)        / (scrCm                               /scrPIX)
            if (self.scrWidthCM in [0,None]) or (self.scrWidthPIX in [0, None]):
                log.error('you didnt give me the width of the screen (pixels and cm). Check settings in MonitorCentre.')
                core.wait(1.0); core.quit()
            thisScale = (numpy.array([2.0,2.0])/self.size)/(float(self.scrWidthCM)/float(self.scrWidthPIX))
        elif units in ["deg", "degs"]:
            #windowPerDeg = winPerCM*CMperDEG
            #               = winPerCM              * tan(pi/180) * distance
            if (self.scrWidthCM in [0,None]) or (self.scrWidthPIX in [0, None]):
                log.error('you didnt give me the width of the screen (pixels and cm). Check settings in MonitorCentre.')
                core.wait(1.0); core.quit()
            cmScale = (numpy.array([2.0,2.0])/self.size)/(float(self.scrWidthCM)/float(self.scrWidthPIX))
            thisScale = cmScale * 0.017455 * self.scrDistCM
        elif units is "stroke_font":
            thisScale = numpy.array([2*font.letterWidth,2*font.letterWidth]/self.size/38.0)
        #actually set the scale as appropriate
        thisScale = thisScale/numpy.asarray(prevScale)#allows undoing of a previous scaling procedure
        GL.glScalef(thisScale[0], thisScale[1], 1.0)
        return thisScale #just in case the user wants to know?!

    def setGamma(self,gamma):
        """Set the monitor gamma, using Bits++ if possible"""
        if type(gamma) in [float, int]:
            self.gamma=[gamma,gamma,gamma]
        else:
            self.gamma=gamma

        if self.bitsMode != None:
            self.bits.setGamma(self.gamma)
        elif self.winType=='pygame':
            pygame.display.set_gamma(self.gamma[0], self.gamma[1], self.gamma[2])
        elif self.winType=='pyglet':
            self.winHandle.setGamma(self.winHandle, self.gamma)
            
    def _setupGlut(self):
        self.winType="glut"
        #initialise a window
        GLUT.glutInit(sys.argv)
        iconFile = os.path.join(psychopy.__path__[0], 'psychopy.gif')
        GLUT.glutSetIconTitle(iconFile)
        GLUT.glutInitDisplayMode(GLUT.GLUT_RGBA | GLUT.GLUT_DOUBLE | GLUT.GLUT_ALPHA | GLUT.GLUT_DEPTH)
        self.handle = GLUT.glutCreateWindow('PsychoPy')

        if self._isFullScr:      GLUT.glutFullScreen()
        else:  GLUT.glutReshapeWindow(int(self.size[0]), int(self.size[1]))
        #set the redisplay callback
        GLUT.glutDisplayFunc(self.update)
    def _setupPyglet(self):
        self.winType = "pyglet"
        #setup the global use of pyglet.gl
        global GL, GLU, GL_multitexture
        GL = pyglet.gl
        GLU = pyglet.gl
        GL_multitexture = pyglet.gl
        
        config = GL.Config(depth_size=8, double_buffer=True)
        allScrs = pyglet.window.get_platform().get_default_display().get_screens()
        if len(allScrs)>self.screen:
            thisScreen = allScrs[self.screen]
            print 'configured pyglet screen %i' %self.screen
        else: 
            print "Requested an unavailable screen number"
        if self._isFullScr:
            w,h = None,None
        else:
            w,h = self.size
        if self.allowGUI: style=None
        else: style='borderless'
        self.winHandle = pyglet.window.Window(width=w, height=h,
                                              caption="PsychoPy", 
                                              fullscreen=self._isFullScr,
                                              config=config,
                                              screen=thisScreen,
                                              style=style
                                          )
        #add these methods to the pyglet window                                  
        self.winHandle.setGamma = gamma.setGamma
        self.winHandle.setGammaRamp = gamma.setGammaRamp
        self.winHandle.getGammaRamp = gamma.getGammaRamp        
        self.winHandle.set_vsync(True)
        self.winHandle.on_key_press = psychopy.event._onPygletKey
        self.winHandle.on_mouse_press = psychopy.event._onPygletMousePress
        self.winHandle.on_mouse_release = psychopy.event._onPygletMouseRelease
        self.winHandle.on_mouse_scroll = psychopy.event._onPygletMouseWheel
        if not self.allowGUI: 
            #make mouse invisible. Could go further and make it 'exclusive' (but need to alter x,y handling then)
            self.winHandle._mouse_visible=False
        self.winHandle.on_resize = self.onResize
        if self.pos is None:
            #work out where the centre should be
            self.pos = [ (thisScreen.width-self.size[0])/2 , (thisScreen.height-self.size[1])/2 ]
        self.winHandle.set_location(self.pos[0]+thisScreen.x, self.pos[1]+thisScreen.y)#add the necessary amount for second screen
        
        try: #to load an icon for the window
            iconFile = os.path.join(psychopy.__path__[0], 'psychopy.png')
            icon = pyglet.image.load(filename=iconFile)
            self.winHandle.set_icon(icon)
        except: pass#doesn't matter

    def _setupPygame(self):
        self.winType = "pygame"
        
        #setup the global use of PyOpenGL (rather than pyglet.gl)
        global GL, GL_multitexture
        
        GL = OpenGL.GL
        GL_multitexture = OpenGL.GL.ARB.multitexture
        
        #pygame.mixer.pre_init(22050,16,2)#set the values to initialise sound system if it gets used
        pygame.init()

        try: #to load an icon for the window
            iconFile = os.path.join(psychopy.__path__[0], 'psychopy.png')
            icon = pygame.image.load(iconFile)
            pygame.display.set_icon(icon)
        except: pass#doesn't matter

        winSettings = pygame.OPENGL|pygame.DOUBLEBUF#|pygame.OPENGLBLIT #these are ints stored in pygame.locals
        if self._isFullScr:
            winSettings = winSettings | pygame.FULLSCREEN
        elif self.pos is None:
            #centre video
            os.environ['SDL_VIDEO_CENTERED']="1"
        else: os.environ['SDL_VIDEO_WINDOW_POS']= '%i,%i' %(self.pos[0], self.pos[1])
        if sys.platform=='win32':
            os.environ['SDL_VIDEODRIVER'] = 'windib'
        if not self.allowGUI:
            winSettings = winSettings |pygame.NOFRAME
            self.setMouseVisible(False)
            pygame.display.set_caption('PsychoPy (NB use with allowGUI=False when running properly)')
        else:
            self.setMouseVisible(True)
            pygame.display.set_caption('PsychoPy')
        self.winHandle = pygame.display.set_mode(self.size.astype('i'),winSettings)
        pygame.display.set_gamma(1.0) #this will be set appropriately later

        #get screen properties (which pygame has initialised for us)
        #GLUT.glutInitDisplayMode(GLUT.GLUT_RGBA | GLUT.GLUT_DOUBLE | GLUT.GLUT_ALPHA | GLUT.GLUT_DEPTH)
        #if self.scrWidthPIX == 0:
            #self.scrWidthPIX = GLUT.glutGet(GLUT.GLUT_SCREEN_WIDTH)
        #if self.scrWidthCM == 0:
            #self.scrWidthCM = GLUT.glutGet(GLUT.GLUT_SCREEN_WIDTH_MM)/10.0
        #print 'screen width: ', self.scrWidthCM, 'cm, ', self.scrWidthPIX, 'pixels'
    def _setupGL(self):
        global _shaders
        if self.winType=='pyglet': _shaders=_shadersPyglet
#        else: _shaders=_shadersPygame
        
        #do settings for openGL
        GL.glClearColor((self.rgb[0]+1.0)/2.0, (self.rgb[1]+1.0)/2.0, (self.rgb[2]+1.0)/2.0, 1.0)       # This Will Clear The Background Color To Black
        GL.glClearDepth(1.0)

        GL.glViewport(0, 0, int(self.size[0]), int(self.size[1]));

        GL.glMatrixMode(GL.GL_PROJECTION) # Reset The Projection Matrix
        GL.glLoadIdentity()                    
        if self.winType=='pyglet': GL.gluOrtho2D(-1,1,-1,1) 

        GL.glMatrixMode(GL.GL_MODELVIEW)# Reset The Projection Matrix
        GL.glLoadIdentity()                     

        GL.glEnable(GL.GL_DEPTH_TEST)                   # Enables Depth Testing
        GL.glDepthFunc(GL.GL_LESS)                      # The Type Of Depth Test To Do
        GL.glEnable(GL.GL_BLEND)
        GL.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA)

        GL.glShadeModel(GL.GL_SMOOTH)                   # Color Shading (FLAT or SMOOTH)
        GL.glEnable(GL.GL_POINT_SMOOTH)

        if self.winType!='pyglet':
            GL_multitexture.glInitMultitextureARB()

        if self.winType=='pyglet' and self._haveShaders:
            #we should be able to compile shaders (don't just 'try')
            self._progSignedTexMask = _shaders.compileProgram(_shaders.vertSimple, _shaders.fragSignedColorTexMask)#fragSignedColorTexMask
            self._progSignedTex = _shaders.compileProgram(_shaders.vertSimple, _shaders.fragSignedColorTex)
#        elif self.winType=='pygame':#on PyOpenGL we should try to get an init value
#            from OpenGL.GL.ARB import shader_objects
#            if shader_objects.glInitShaderObjectsARB():
#                self._haveShaders=True
#                self._progSignedTexMask = _shaders.compileProgram(_shaders.vertSimple, _shaders.fragSignedColorTexMask)#fragSignedColorTexMask
#                self._progSignedTex = _shaders.compileProgram(_shaders.vertSimple, _shaders.fragSignedColorTex)
#            else:
#                self._haveShaders=False
        
        GL.glClear(GL.GL_COLOR_BUFFER_BIT|GL.GL_DEPTH_BUFFER_BIT)

        if sys.platform=='darwin':
            ext.darwin.syncSwapBuffers(1)

        if haveFB:
            self._setupFrameBuffer()

    def _setupFrameBuffer(self):
        # Setup framebuffer
        self.frameBuffer = FB.glGenFramebuffersEXT(1)

        FB.glBindFramebufferEXT(FB.GL_FRAMEBUFFER_EXT, self.frameBuffer)
        # Setup depthbuffer
        self.depthBuffer = FB.glGenRenderbuffersEXT(1)
        FB.glBindRenderbufferEXT (FB.GL_RENDERBUFFER_EXT,self.depthBuffer)
        FB.glRenderbufferStorageEXT (FB.GL_RENDERBUFFER_EXT, GL.GL_DEPTH_COMPONENT, int(self.size[0]), int(self.size[1]))

        # Create texture to render to
        self.frameTexture = GL.glGenTextures (1)
        GL.glBindTexture (GL.GL_TEXTURE_2D, self.frameTexture)
        GL.glTexParameteri (GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR)
        GL.glTexParameteri (GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR)
        GL.glTexImage2D (GL.GL_TEXTURE_2D, 0, GL.GL_RGBA32F_ARB, int(self.size[0]), int(self.size[1]), 0,
                         GL.GL_RGBA, GL.GL_FLOAT, None)

        #attach texture to the frame buffer
        FB.glFramebufferTexture2DEXT (FB.GL_FRAMEBUFFER_EXT, GL.GL_COLOR_ATTACHMENT0_EXT,
                                      GL.GL_TEXTURE_2D, self.frameTexture, 0);
        FB.glFramebufferRenderbufferEXT(FB.GL_FRAMEBUFFER_EXT, GL.GL_DEPTH_ATTACHMENT_EXT,
                                        FB.GL_RENDERBUFFER_EXT, self.depthBuffer);

        status = FB.glCheckFramebufferStatusEXT (FB.GL_FRAMEBUFFER_EXT);
        if status != FB.GL_FRAMEBUFFER_COMPLETE_EXT:
            print "Error in framebuffer activation"
            return
        GL.glDisable(GL.GL_TEXTURE_2D)

    def setMouseVisible(self,visibility):
        """Sets the visibility of the mouse cursor.

        If Window was initilised with noGUI=True then the mouse is initially
        set to invisible, otherwise it will initially be visible.

        Usage:
            ``setMouseVisible(False)``
            ``setMouseVisible(True)``
        """
        if self.winType=='pygame':wasVisible = pygame.mouse.set_visible(visibility)
        elif self.winType=='pyglet':self.winHandle.set_mouse_visible(visibility)
        self.mouseVisible = visibility
#############################################################################
class _BaseVisualStim:
    """A template for a stimulus class, on which PatchStim, TextStim etc... are based.
    Not finished...?
    """
    def __init__(self):
        raise NotImplementedError('abstract')
    def draw(self):
        raise NotImplementedError('abstract')
        
    def setPos(self, newPos, operation=None, units=None):
        self._set('pos', val=newPos, op=operation)
    def setDepth(self,newDepth, operation=None):
        self._set('depth', newDepth, operation)
    def setSize(self, newSize, operation=None, units=None):
        if units==None: units=self.units#need to change this to create several units from one
        self._set('size', newSize, op=operation)
        self.needUpdate=True
    def setOri(self, newOri, operation=None):
        self._set('ori',val=newOri, op=operation)
    def setOpacity(self,newOpacity,operation=None):
        self._set('opacity', newOpacity, operation)
        #opacity is coded by the texture, if not using shaders
        if not self._useShaders:
            self.setMask(self._maskName)
    def setDKL(self, newDKL, operation=None):
        self._set('dkl', value=newDKL, op=operation)
        self.setRGB(psychopy.misc.dkl2rgb(self.dkl, self.win.dkl_rgb))
    def setLMS(self, newLMS, operation=None):
        self._set('lms', value=newLMS, op=operation)
        self.setRGB(psychopy.misc.lms2rgb(self.lms, self.win.lms_rgb))
    def setRGB(self, newRGB, operation):      
        self._set('rgb', newRGB, operation)
        #if we don't have shaders we need to rebuild the texture
        if not self._useShaders:
            self.setTex(self._texName)
    def _set(self, attrib, val, op=''):
        """
        Deprecated. Use methods specific to the parameter you want to set
        
        e.g. ::
        
          stim.setPos([3,2.5])
                stim.setOri(45)
                stim.setPhase(0.5, "+")
                
        NB this method does not flag the need for updates any more - that is 
        done by specific methods as described above.
        """
        if op is None: op=''
        #format the input value as float vectors
        if type(val) in [tuple,list]:
            val=numpy.asarray(val,float)
        
        if op=='':#this routine can handle single value inputs (e.g. size) for multi out (e.g. h,w)
            exec('self.'+attrib+'*=0') #set all values in array to 0
            exec('self.'+attrib+'+=val') #then add the value to array
        else:
            exec('self.'+attrib+op+'=val')
    def setUseShaders(self, val=True):
        """Set this stimulus to use shaders if possible.
        """
        #NB TextStim overrides this function, so changes here may need changing there too
        if val==True and self.win._haveShaders==False:
            log.error("Shaders were requested for PatchStim but aren't available. Shaders need OpenGL 2.0+ drivers")
        if val!=self._useShaders:
            self._useShaders=val
            self.setTex(self._texName)
            self.setMask(self._maskName)
            self.needUpdate=True
            
    def _updateList(self):
        """
        The user shouldn't need this method since it gets called
        after every call to .set() 
        Chooses between using and not using shaders each call.
        """
        if self._useShaders:
            self._updateListShaders()
        else: self._updateListNoShaders()  

class DotStim(_BaseVisualStim):
    """
    This stimulus class defines a field of dots, all with the same speed
    but various directions of motion. A subset of 'coherent' dots have
    the same direction of motion.
    """
    def __init__(self,
                 win,
                 units  ='',
                 nDots  =1,
                 coherence      =0.5,
                 fieldPos       =(0.0,0.0),
                 fieldSize      = (1.0,1.0),
                 fieldShape     = 'sqr',
                 dotSize        =2.0,
                 dotLife = 1,
                 dir    =0.0,
                 speed  =0.5,
                 rgb    =[1.0,1.0,1.0],
                 opacity        =1.0,
                 depth  =0,
                 element=None):
        """
        **Arguments:**

            - **win:** a Window() object required - the stimulus must know where to draw itself!

            - **units:**
                + None (use the current units of the Window)
                + **or** 'norm' (normalised: window voes from -1:1 in each direction)
                + **or**  'cm','pix','deg' (but these real-world units need you to give sufficient info about your monitor, see below)

            - **nDots:** number of dots to be generated. For a 'sqr' fieldShape this is exactly the number of dots
                drawn each frame. For other stimuli that use masks (e.g. fieldShape='circle') it is
                the *average* number of dots drawn.

            - **fieldPos:** a tuple (0.0,0.0) or a list [0.0,0.0] for the x and y of the centre of the stimulus.
                The origin is the screen centre, the units are determined
                by units (see above).

            - **fieldSize:** a tuple (0.5,0.5) or a list [0.5,0.5] for the x and y
                OR a single value (which will be applied to x and y).
                Units are specified by 'units' (see above).
                Sizes can be negative and can extend beyond the window.

            - **fieldShape:** *'circle'*,'sqr','gauss' Defines the envelope used to present the dots

            - **dotSize:** *2.0* in specified *units* [overridden if *element* is specified]

            - **dotLife:** Not currently implemented

            - **dir:** direction of the coherent dots (degrees)

            - **speed:** speed of the dots (in *units*/frame)

            - **rgb:** a tuple (1.0,1.0, 1.0) or a list [1.0,1.0, 1.0]
                or a single value (which will be applied to all guns).
                RGB vals are applied to simple textures and to greyscale
                image files but not to RGB images.

                **NB** units range -1:1 (so 0.0 is GREY). Again this is
                unconventional but it's great for vision scientists
                because they usually change things relative to a
                midpoint rather than relative to black.

                    [overridden if *element* is specified]

            - **opacity** 1.0,
                1.0 is opaque, 0.0 is transparent

            - **depth** 0,
                This can be used to choose which
                stimulus overlays which. (more negative values are nearer).
                At present the window does not do perspective rendering
                but could do if that's really useful(?!)

            - **element:** *None*.
                This can be any object that has a ``.draw()`` method and a
                ``.setPos([x,y])`` method (e.g. AlphaStim, TextStim...)!!
                """
        self.win = win
        if len(units): self.units = units
        else: self.units = win.units
        self.nDots = nDots
        self.fieldPos = fieldPos
        self.fieldSize = fieldSize
        self.fieldShape = fieldShape
        self.dotSize = dotSize
        self.dir = dir
        self.speed = speed
        self.opacity = opacity
        self.element = element

        if type(rgb) in [float, int]: #user may give a luminance val
            self.rgb=numpy.array((rgb,rgb,rgb), float)
        else:
            self.rgb = numpy.array(rgb, float)

        self.depth=depth
        #size
        if type(fieldSize) in [tuple,list]:
            self.fieldSize = numpy.array(fieldSize,float)
        else:
            self.fieldSize = numpy.array((fieldSize,fieldSize),float)#make a square if only given one dimension
        """initialise the dots themselves - give them all random dir and then
        fix the first n in the array to have the direction specified"""

        if self.fieldShape=='circle':self._nDotsTotal=int(nDots*4/pi) #control for smaller area of circle
        else: self._nDotsTotal = self.nDots
        self.coherence=round(coherence*self._nDotsTotal)/self._nDotsTotal#store actual coherence

        self._dotsXY = numpy.random.rand(self._nDotsTotal,2)*self.fieldSize - self.fieldSize/2 #initialise a random array of X,Y
        self._dotsDir = numpy.random.rand(self._nDotsTotal)*2*pi
        self._dotsDir[0:int(self.coherence*self._nDotsTotal)] = self.dir
        self._opacity = numpy.ones(self._nDotsTotal,'f')*self.opacity
        self._dotsSpeed = numpy.ones(self._nDotsTotal, 'f')*self.speed#all dots have the same speed
        self._dotsLife = dotLife*numpy.random.rand(self._nDotsTotal)
        self.frameClock = core.Clock()#we'll need a time record

        self._update_dotsXY()

    def _set(self, attrib, val, op=''):
        """Use this to set attributes of your stimulus after initialising it.

        **arguments:**
            - attrib = a string naming any of the attributes of the stimulus (set during init)
            - val = the value to be used in the operation on the attrib
            - op = a string representing the operation to be performed (optional) most maths operators apply ('+','-','*'...)

        **examples:**
            - myStim.set('rgb',0) #will simply set all guns to zero (black)
            - myStim.set('rgb',0.5,'+') #will increment all 3 guns by 0.5
            - myStim.set('rgb',(1.0,0.5,0.5),'*') # will keep the red gun the same and halve the others

        """
        #format the input value as float vectors
        if type(val) in [tuple,list]:
            val=numpy.array(val,float)

        #change the attribute as requested
        if op=='':
            #note: this routine can handle single value inputs (e.g. size) for multi out (e.g. h,w)
            exec('self.'+attrib+'*=0') #set all values in array to 0
            exec('self.'+attrib+'+=val') #then add the value to array
        else:
            exec('self.'+attrib+op+'=val')

        #update the actual coherence for the requested coherence and nDots
        if attrib in ['nDots','coherence']:
            if self.fieldShape=='circle': self._nDotsTotal=int(self.nDots*4/pi)
            else: self._nDotsTotal = self.nDots
            self.coherence=round(self.coherence*self._nDotsTotal)/self._nDotsTotal


    def set(self, attrib, val, op=''):
        """DotStim.set() is obselete and may not be supported in future
        versions of PsychoPy. Use the specific method for each parameter instead
        (e.g. setOri(), setSF()...)
        """
        self._set(attrib, val, op)


    def draw(self, win=None):
        """
        Draw the stimulus in its relevant window. You must call
        this method after every MyWin.flip() if you want the
        stimulus to appear on that frame and then update the screen
        again.
        """
        if win==None: win=self.win
        if win.winType=='pyglet': win.winHandle.switch_to()
        
        self._update_dotsXY()
        if self.depth==0:
            thisDepth = self.win._defDepth
            self.win._defDepth += _depthIncrements[self.win.winType]
        else:
            thisDepth=self.depth

        #draw the dots
        if self.element==None:
            #scale the drawing frame etc...
            GL.glPushMatrix()#push before drawing, pop after
            GL.glLoadIdentity()
            self.win.setScale(self.units)
            GL.glTranslatef(self.fieldPos[0],self.fieldPos[1],thisDepth)
            GL.glPointSize(self.dotSize)

            #load Null textures into multitexteureARB - they modulate with glColor
            GL.glActiveTexture(GL.GL_TEXTURE0)
            GL.glEnable(GL.GL_TEXTURE_2D)
            GL.glBindTexture(GL.GL_TEXTURE_2D, 0)
            GL.glActiveTexture(GL.GL_TEXTURE1)
            GL.glEnable(GL.GL_TEXTURE_2D)
            GL.glBindTexture(GL.GL_TEXTURE_2D, 0)

            visible = (self._opacity>0)
            visibleXY = self._dotsXY[visible,:]
            if self.win.winType == 'pyglet':
                #visibleXY = numpy.transpose(visibleXY)
                #visibleXY=visibleXY.flat
                GL.glVertexPointer(2, GL.GL_DOUBLE, 0, visibleXY.ctypes)#.data_as(ctypes.POINTER(ctypes.c_float)))
            else:
                GL.glVertexPointerd(visibleXY)

            GL.glColor4f(self.rgb[0], self.rgb[1], self.rgb[2], 1.0)
            GL.glEnableClientState(GL.GL_VERTEX_ARRAY)
            GL.glDrawArrays(GL.GL_POINTS, 0, len(visibleXY))
            GL.glDisableClientState(GL.GL_VERTEX_ARRAY)
            GL.glPopMatrix()
        else:
            #we don't want to do the screen scaling twice so for each dot subtract the screen centre
            initialDepth=self.element.depth
            for pointN in range(0,self._nDotsTotal):
                if self._opacity[pointN]>0.0:
#                    self.element.setDepth(0.0001,'-')# this will be done in the draw routine
                    self.element.setPos(self._dotsXY[pointN,:]-self.fieldPos)
                    self.element.draw()

            self.element.setDepth(initialDepth)#reset depth before going to next frame


    def _update_dotsXY(self):
        """
        The user shouldn't call this - its gets done within draw()
        """
        deltaT = self.frameClock.getTime() #measure time since last frame draw
        self.frameClock.reset()

        self._dotsLife -= deltaT #dots to be reborn will be negative
        deadDots = (self._dotsLife<0.0)
        """XXXX need to give deadDots new XY and direction etc"""
        self._dotsXY[:,0] += self.speed*numpy.reshape(numpy.cos(self._dotsDir),(self._nDotsTotal,))
        self._dotsXY[:,1] += self.speed*numpy.reshape(numpy.sin(self._dotsDir),(self._nDotsTotal,))# 0 radians=East!
        #handle boundaries of the field: square for now - circles not quite working
        if self.fieldShape is 'sqr':
            #gone outside the square
            self._dotsXY[:,0] = ((self._dotsXY[:,0]+self.fieldSize[0]/2) % self.fieldSize[0])-self.fieldSize[0]/2
            self._dotsXY[:,1] = ((self._dotsXY[:,1]+self.fieldSize[1]/2) % self.fieldSize[1])-self.fieldSize[1]/2
        elif self.fieldShape is 'circle':
            #gone outside the square
            self._dotsXY[:,0] = ((self._dotsXY[:,0]+self.fieldSize[0]/2) % self.fieldSize[0])-self.fieldSize[0]/2
            self._dotsXY[:,1] = ((self._dotsXY[:,1]+self.fieldSize[1]/2) % self.fieldSize[1])-self.fieldSize[1]/2
            #use a circular envelope and flips dot to opposite edge if they fall
            #beyond radius.
            #NB always circular - uses fieldSize in X only
            normXY = self._dotsXY/(self.fieldSize/2.0)
            dotDist = numpy.sqrt((normXY[:,0]**2.0 + normXY[:,1]**2.0))
            self._opacity = numpy.where(dotDist<1.0, self.opacity, 0.0)


class PatchStim(_BaseVisualStim):
    """Stimulus object for drawing arbitrary bitmaps, textures and shapes.
    One of the main stimuli for PsychoPy.

    Formally PatchStim is just a texture behind an optional
    transparency mask (an 'alpha mask'). Both the texture and mask can be
    arbitrary bitmaps and their combination allows an enormous variety of
    stimuli to be drawn in realtime.

    **Examples**::

        myGrat = PatchStim(tex='sin',mask='circle') #gives a circular patch of grating
        myGabor = PatchStim(tex='sin',mask='gauss') #gives a 'Gabor' patchgrating
        myImage = PatchStim(tex='face.jpg',mask=None) #simply draws the image face.jpg


    An PatchStim can be rotated scaled and shifted in position, its texture can
    be drifted in X and/or Y and it can have a spatial frequency in X and/or Y
    (for an image file that simply draws multiple copies in the patch).

    Also since transparency can be controlled two PatchStims can combine e.g.
    to form a plaid.

    At present all these operations are 2D (and stimuli are layered in the order
    they are initialised) but this could all be done in 3D too!
    """
    def __init__(self,
                 win,
                 tex     ="sin",
                 mask    ="none",
                 units   ="",
                 pos     =(0.0,0.0),
                 size    =(0.5,0.5),
                 sf      =(1.0,1.0),
                 ori     =0.0,
                 phase   =(0.0,0.0),
                 texRes =128,
                 rgb   =[1.0,1.0,1.0],
                 dkl=None,
                 lms=None,
                 contrast=1.0,
                 opacity=1.0,
                 depth=0,
                 rgbPedestal = [0.0,0.0,0.0],
                 interpolate=False):
        """
        **Arguments:**

            - **win:** 
                a Window() object required - the stimulus must know where to draw itself!

            - **tex** 
                The texture forming the image
                + 'sin','sqr',None
                + **or** the name of an image file (most formats supported)
                + **or** a numpy array (1xN or NxN) ranging -1:1

            - **mask:**
                The alpha mask (forming the shape of the image)
                + None, 'circle', 'gauss'
                + **or** the name of an image file (most formats supported)
                + **or** a numpy array (1xN or NxN) ranging -1:1

            - **units:**
                + None (use the current units of the Window)
                + **or** 'norm' (normalised: window voes from -1:1 in each direction)
                + **or**  'cm','pix','deg' (but these real-world units need you to give sufficient info about your monitor, see below)

            - **pos:** 
                a tuple (0.0,0.0) or a list [0.0,0.0] for the x and y of the centre of the stimulus.
                The origin is the screen centre, the units are determined
                by units (see above). Stimuli can be position beyond the
                window!
            - **size:** 
                a tuple (0.5,0.5) or a list [0.5,0.5] for the x and y
                OR a single value (which will be applied to x and y).
                Units are specified by 'units' (see above).
                Sizes can be negative and can extend beyond the window.
            - **sf:** 
                a tuple (1.0,1.0) or a list [1.0,1.0] for the x and y
                OR a single value (which will be applied to x and y).
                Units are in cycles per 'units' (see above). For *deg*, *cm* and *norm* units th
                default sf=1, for *pix* the default sf=1/size so that one cycle fits on the stim.
            - **ori:** 
                orientation of stimulus in degrees.
            - **phase:** 
                a tuple (0.0,0.0) or a list [0.0,0.0] for the x and y
                OR a single value (which will be applied to x and y).
                Phase of the stimulus in each direction.
                **NB** phase has modulus 1 (rather than 360 or 2*pi)
                This is a little unconventional but has the nice effect
                that setting phase=t*n drifts a stimulus at n Hz

            - **texRes:** 
                resolution of the texture (if not loading from an image file)

            - **rgb:** 
                a tuple (1.0,1.0, 1.0) or a list [1.0,1.0, 1.0]
                or a single value (which will be applied to all guns).
                RGB vals are applied to simple textures and to greyscale
                image files but not to RGB images.

                **NB** units range -1:1 (so 0.0 is GREY). Again this is
                unconventional but it's great for vision scientists
                because they usually change things relative to a
                midpoint rather than relative to black.

            - **dkl:** 
                a tuple (45.0,90.0, 1.0) or a list [45.0,90.0, 1.0]
                specifying the coordinates of the stimuli in cone-opponent
                space (Derrington, Krauskopf, Lennie 1984)
                Triplets represent [elevation, azimuth, magnitude].
                Note that the monitor must be calibrated for this to be
                accurate (if not, example phosphors from a Sony Trinitron
                CRT will be used).

            - **lms:** 
                a tuple (0.5, 1.0, 1.0) or a list [0.5, 1.0, 1.0]
                specifying the coordinates of the stimuli in cone space
                Triplets represent relative modulation of each cone [L, M, S].
                Note that the monitor must be calibrated for this to be
                accurate (if not, example phosphors from a Sony Trinitron
                CRT will be used).

            - **contrast**
                How far the stimulus deviates from the middle grey.
                Contrast can vary -1:1 (this is a multiplier for the
                values given in the color description of the stimulus)

            - **opacity**
                1.0 is opaque, 0.0 is transparent

            - **depth**
                This can potentially be used (not tested!) to choose which
                stimulus overlays which. (more negative values are nearer).
                At present the window does not do perspective rendering
                but could do if that's really useful(?!)

        """
        
        self.win = win
        assert isinstance(self.win, Window)
        if win._haveShaders: self._useShaders=True#by default, this is a good thing
        else: self._useShaders=False
        
        if units in [None, "", []]:
            self.units = win.units
        else:
            self.units = units
        self.ori = float(ori)
        self.texRes = texRes #must be power of 2
        self.contrast = float(contrast)
        self.opacity = opacity
        self.interpolate=interpolate
        
        #for rgb allow user to give a single val and apply to all channels
        if type(rgb)==float or type(rgb)==int: #user may give a luminance val
            self.rgb=numpy.array((rgb,rgb,rgb), float)
        else:
            self.rgb = numpy.asarray(rgb, float)
        if type(rgbPedestal)==float or type(rgbPedestal)==int: #user may give a luminance val
            self.rgbPedestal=numpy.array((rgbPedestal,rgbPedestal,rgbPedestal), float)
        else:
            self.rgbPedestal = numpy.asarray(rgbPedestal, float)
        
        if dkl is not None:
            self.dkl = dkl
            self.rgb = psychopy.misc.dkl2rgb(dkl, win.dkl_rgb)
        elif lms is not None:
            self.lms = lms
            #log.warning('LMS-to-RGB conversion is not properly tested yet - it should NOT be used for proper research!')
            self.rgb = psychopy.misc.lms2rgb(lms, win.lms_rgb)

        #phase (ranging 0:1)
        if type(phase) in [tuple,list]:
            self.phase = numpy.array(phase)
        else:
            self.phase = numpy.array((phase,0),float)

        #size
        if type(size) in [tuple,list]:
            self.size = numpy.array(size,float)
        else:
            self.size = numpy.array((size,size),float)#make a square if only given one dimension

        #sf
        if units=='pix' and sf==(1,1): #if using pix and sf wasn't given
            sf = 1.0/self.size#so that exactly
        if type(sf) in [float, int] or len(sf)==1:
            self.sf = numpy.array((sf,sf),float)
        else:
            self.sf = numpy.array(sf,float)

        self.pos = numpy.array(pos)

        self.depth=depth

        #initialise textures for stimulus
        if self.win.winType=="pyglet":
            self.texID=GL.GLuint()
            GL.glGenTextures(1, ctypes.byref(self.texID))
            self.maskID=GL.GLuint()
            GL.glGenTextures(1, ctypes.byref(self.maskID))
        elif cTypesOpenGL:
            (tID, mID) = GL.glGenTextures(2)
            self.texID = GL.GLuint(int(tID))#need to convert to GLUint (via ints!!)
            self.maskID = GL.GLuint(int(mID))
        else:
            (self.texID, self.maskID) = GL.glGenTextures(2)
        self.setTex(tex)
        self.setMask(mask)
        #generate a displaylist ID
        self._listID = GL.glGenLists(1)
        self._updateList()#ie refresh display list


    def setSF(self,value,operation=None):
        self._set('sf', value, operation)
        self.needUpdate = 1
    def setPhase(self,value, operation=None):
        self._set('phase', value, operation)
        self.needUpdate = 1
    def setContrast(self,value,operation=None):
        self._set('contrast', value, operation)
        #if we don't have shaders we need to rebuild the texture
        if not self._useShaders:
            self.setTex(self._texName)
    def setTex(self,value):
        self._texName = value
        createTexture(value, id=self.texID, pixFormat=GL.GL_RGB, stim=self, res=self.texRes)
    def setMask(self,value):        
        self._maskName = value
        createTexture(value, id=self.maskID, pixFormat=GL.GL_ALPHA, stim=self, res=self.texRes)
    def draw(self, win=None):
        """
        Draw the stimulus in its relevant window. You must call
        this method after every MyWin.flip() if you want the
        stimulus to appear on that frame and then update the screen
        again.
        """
        #set the window to draw to
        if win==None: win=self.win
        if win.winType=='pyglet': win.winHandle.switch_to()
        
        #work out next default depth
        if self.depth==0:
            thisDepth = self.win._defDepth
            self.win._defDepth += _depthIncrements[self.win.winType]
        else:
            thisDepth=self.depth

        #do scaling
        GL.glPushMatrix()#push before the list, pop after
        #GL.glLoadIdentity() #implicitly done by push/pop?
        #scale the viewport to the appropriate size
        self.win.setScale(self.units)
        #move to centre of stimulus and rotate
        GL.glTranslatef(self.pos[0],self.pos[1],thisDepth)
        GL.glRotatef(-self.ori,0.0,0.0,1.0)
        #the list just does the texture mapping

        desiredRGB = (self.rgb*self.contrast+1)/2.0#RGB in range 0:1 and scaled for contrast
        if numpy.any(desiredRGB**2.0>1.0):
            desiredRGB=[0.6,0.6,0.4]
        GL.glColor4f(desiredRGB[0],desiredRGB[1],desiredRGB[2], self.opacity)

        if self.needUpdate: self._updateList()
        GL.glCallList(self._listID)

        #return the view to previous state
        GL.glPopMatrix()

    def _updateListShaders(self):
        """
        The user shouldn't need this method since it gets called
        after every call to .set() Basically it updates the OpenGL
        representation of your stimulus if some parameter of the
        stimulus changes. Call it if you change a property manually
        rather than using the .set() command
        """
        #print 'updating Shaders list'
        self.needUpdate=0
        GL.glNewList(self._listID,GL.GL_COMPILE)
        #setup the shaderprogram        
        GL.glUseProgram(self.win._progSignedTexMask)
        GL.glUniform1i(GL.glGetUniformLocation(self.win._progSignedTexMask, "texture"), 0) #set the texture to be texture unit 0
        GL.glUniform1i(GL.glGetUniformLocation(self.win._progSignedTexMask, "mask"), 1)  # mask is texture unit 1

        #mask
        GL.glActiveTexture(GL.GL_TEXTURE1)
        GL.glBindTexture(GL.GL_TEXTURE_2D, self.maskID)
        GL.glEnable(GL.GL_TEXTURE_2D)#implicitly disables 1D

        #main texture
        GL.glActiveTexture(GL.GL_TEXTURE0)
        GL.glBindTexture(GL.GL_TEXTURE_2D, self.texID)
        GL.glEnable(GL.GL_TEXTURE_2D)
        #calculate coords in advance:
        L = -self.size[0]/2#vertices
        R =      self.size[0]/2
        T =      self.size[1]/2
        B = -self.size[1]/2
        #depth = self.depth
        if self.units=='norm':#sf is dependent on size (openGL default)
            Ltex = -self.sf[0]/2 - self.phase[0]+0.5
            Rtex = +self.sf[0]/2 - self.phase[0]+0.5
            Ttex = +self.sf[1]/2 - self.phase[1]+0.5
            Btex = -self.sf[1]/2 - self.phase[1]+0.5
        else: #we should scale to become independent of size
            Ltex = -self.sf[0]*self.size[0]/2 - self.phase[0]+0.5
            Rtex = +self.sf[0]*self.size[0]/2 - self.phase[0]+0.5
            Ttex = +self.sf[1]*self.size[1]/2 - self.phase[1]+0.5
            Btex = -self.sf[1]*self.size[1]/2 - self.phase[1]+0.5
        Lmask=Bmask= 0.0; Tmask=Rmask=1.0#mask

        GL.glBegin(GL.GL_QUADS)                  # draw a 4 sided polygon
        # right bottom
        GL.glMultiTexCoord2f(GL.GL_TEXTURE0,Rtex, Btex)
        GL.glMultiTexCoord2f(GL.GL_TEXTURE1,Rmask,Bmask)
        GL.glVertex2f(R,B)
        # left bottom
        GL.glMultiTexCoord2f(GL.GL_TEXTURE0,Ltex,Btex)
        GL.glMultiTexCoord2f(GL.GL_TEXTURE1,Lmask,Bmask)
        GL.glVertex2f(L,B)
        # left top
        GL.glMultiTexCoord2f(GL.GL_TEXTURE0,Ltex,Ttex)
        GL.glMultiTexCoord2f(GL.GL_TEXTURE1,Lmask,Tmask)
        GL.glVertex2f(L,T)
        # right top
        GL.glMultiTexCoord2f(GL.GL_TEXTURE0,Rtex,Ttex)
        GL.glMultiTexCoord2f(GL.GL_TEXTURE1,Rmask,Tmask)
        GL.glVertex2f(R,T)
        GL.glEnd()

        #unbind the textures
        GL.glActiveTexture(GL.GL_TEXTURE1)
        GL.glBindTexture(GL.GL_TEXTURE_2D, 0)
        GL.glDisable(GL.GL_TEXTURE_2D)#implicitly disables 1D
        #main texture
        GL.glActiveTexture(GL.GL_TEXTURE0)
        GL.glBindTexture(GL.GL_TEXTURE_2D, 0)
        GL.glDisable(GL.GL_TEXTURE_2D)

        GL.glUseProgram(0)

        GL.glEndList()

    #for the sake of older graphics cards------------------------------------
    def _updateListNoShaders(self):
        """
        The user shouldn't need this method since it gets called
        after every call to .set() Basically it updates the OpenGL
        representation of your stimulus if some parameter of the
        stimulus changes. Call it if you change a property manually
        rather than using the .set() command
        """
        #print 'updating No Shaders list'
        self.needUpdate=0

        GL.glNewList(self._listID,GL.GL_COMPILE)
        GL.glColor4f(1.0,1.0,1.0,1.0)#glColor can interfere with multitextures
        #mask
        GL_multitexture.glActiveTextureARB(GL_multitexture.GL_TEXTURE1_ARB)
        GL.glEnable(GL.GL_TEXTURE_2D)#implicitly disables 1D
        GL.glBindTexture(GL.GL_TEXTURE_2D, self.maskID)

        #main texture
        GL_multitexture.glActiveTextureARB(GL_multitexture.GL_TEXTURE0_ARB)
        GL.glEnable(GL.GL_TEXTURE_2D)
        GL.glBindTexture(GL.GL_TEXTURE_2D, self.texID)
        #calculate coords in advance:
        L = -self.size[0]/2#vertices
        R =      self.size[0]/2
        T =      self.size[1]/2
        B = -self.size[1]/2
        #depth = self.depth
        if self.units=='norm':#sf is dependent on size (openGL default)
            Ltex = -self.sf[0]/2 - self.phase[0]+0.5
            Rtex = +self.sf[0]/2 - self.phase[0]+0.5
            Ttex = +self.sf[1]/2 - self.phase[1]+0.5
            Btex = -self.sf[1]/2 - self.phase[1]+0.5
        else: #we should scale to become independent of size
            Ltex = -self.sf[0]*self.size[0]/2 - self.phase[0]+0.5
            Rtex = +self.sf[0]*self.size[0]/2 - self.phase[0]+0.5
            Ttex = +self.sf[1]*self.size[1]/2 - self.phase[1]+0.5
            Btex = -self.sf[1]*self.size[1]/2 - self.phase[1]+0.5
        Lmask=Bmask= 0.0; Tmask=Rmask=1.0#mask

        GL.glBegin(GL.GL_QUADS)                  # draw a 4 sided polygon
        # right bottom
        GL_multitexture.glMultiTexCoord2fARB(GL_multitexture.GL_TEXTURE0_ARB,Rtex, Btex)
        GL_multitexture.glMultiTexCoord2fARB(GL_multitexture.GL_TEXTURE1_ARB,Rmask,Bmask)
        GL.glVertex2f(R,B)
        # left bottom
        GL_multitexture.glMultiTexCoord2fARB(GL_multitexture.GL_TEXTURE0_ARB,Ltex,Btex)
        GL_multitexture.glMultiTexCoord2fARB(GL_multitexture.GL_TEXTURE1_ARB,Lmask,Bmask)
        GL.glVertex2f(L,B)
        # left top
        GL_multitexture.glMultiTexCoord2fARB(GL_multitexture.GL_TEXTURE0_ARB,Ltex,Ttex)
        GL_multitexture.glMultiTexCoord2fARB(GL_multitexture.GL_TEXTURE1_ARB,Lmask,Tmask)
        GL.glVertex2f(L,T)
        # right top
        GL_multitexture.glMultiTexCoord2fARB(GL_multitexture.GL_TEXTURE0_ARB,Rtex,Ttex)
        GL_multitexture.glMultiTexCoord2fARB(GL_multitexture.GL_TEXTURE1_ARB,Rmask,Tmask)
        GL.glVertex2f(R,T)
        GL.glEnd()

        GL.glDisable(GL.GL_TEXTURE_2D)
        GL.glEndList()



    def clearTextures(self):
        """
        Clear the textures associated with the given stimulus. You should call this before
        de-referencing your stimulus or the textures associated with it will be kept in memory.
        
        For example, the following will eventually crash::
        
            from psychopy import visual
            win = visual.Window([400,400])
            for frameN in range(3000):
                #creating a new stimulus every time
                stim = visual.PatchStim(win, texRes=512)
                stim.draw()
                win.flip()
                
        Whereas this will not, because it removes uneeded textures from memory::
        
            from psychopy import visual
            win = visual.Window([400,400])
            for frameN in range(3000):
                #creating a new stimulus every time
                stim = visual.PatchStim(win, texRes=512)
                stim.draw()
                stim.clearTextures()
                win.flip()         
        """
        #only needed for pyglet
        if self.win.winType=='pyglet':
            GL.glDeleteTextures(1, self.texID)
            GL.glDeleteTextures(1, self.maskID)
class AlphaStim(PatchStim):
    """DEPRECATED. Use PatchStim instead/n/n"""
    def __init__(self, *arguments, **keywords):
        PatchStim.__init__(self, *arguments, **keywords)
        
class RadialStim(PatchStim):
    """Stimulus object for drawing radial stimuli, like an annulus, a rotating wedge,
    a checkerboard etc...

    Ideal for fMRI retinotopy stimuli!

    Many of the capabilities are built on top of the PatchStim.

    This stimulus is still relatively new and I'm finding occasional gliches. it also takes longer to draw
    than a typical PatchStim, so not recommended for tasks where high frame rates are needed.
    """
    def __init__(self,
                 win,
                 tex     ="sqrXsqr",
                 mask    ="none",
                 units   ="",
                 pos     =(0.0,0.0),
                 size    =(1.0,1.0),
                 radialCycles=3,
                 angularCycles=4,
                 radialPhase=0,
                 angularPhase=0,
                 ori     =0.0,
                 texRes =64,
                 angularRes=100,
                 visibleWedge=[0, 360],
                 rgb   =[1.0,1.0,1.0],
                 dkl=None,
                 lms=None,
                 contrast=1.0,
                 opacity=1.0,
                 depth=0,
                 rgbPedestal = [0.0,0.0,0.0],
                 interpolate=False):
        """
        **Arguments:**

            - **win:**
                a Window() object required - the stimulus must know where to draw itself!

            - **tex**
                The texture forming the image
                + 'sqrXsqr', 'sinXsin', 'sin','sqr',None
                + **or** the name of an image file (most formats supported)
                + **or** a numpy array (1xN or NxN) ranging -1:1

            - **mask:**
                Unlike the mask in the AlphaStim, this is a 1-D mask dictating the behaviour
                from the centre of the stimulus to the surround.

            - **units:**
                + None (use the current units of the Window)
                + **or** 'norm' (normalised: window goes from -1:1 in each direction)
                + **or**  'cm','pix','deg' (but these real-world units need you to give sufficient info about your monitor, see below)

            - **pos:**
                    a tuple (0.0,0.0) or a list [0.0,0.0] for the x and y of the centre of the stimulus.
                    The origin is the screen centre, the units are determined
                    by units (see above). Stimuli can be position beyond the
                    window!

            - **size:**
                    a tuple (0.5,0.5) or a list [0.5,0.5] for the x and y
                    OR a single value (which will be applied to x and y).
                    Units are specified by 'units' (see above).

                    Sizes can be negative and can extend beyond the window.

            - **sf:**
                    + a tuple (1.0,1.0) or a list [1.0,1.0] for the x and y
                    + OR a single value (which will be applied to x and y).

                    Units are in cycles per 'units' (see above)

            - **ori:** orientation of stimulus in degrees.

            - **texRes:** 128, resolution of the texture (if not loading from an image file)

            - **angularRes:** 100, the number of triangles used to make the stim

            - **radialPhase:**
                    the phase of the texture from the centre to the perimeter
                    of the stimulus

            - **angularPhase:** the phase of the texture around the stimulus

            - **rgb:**
                    a tuple (1.0,1.0, 1.0) or a list [1.0,1.0, 1.0]
                    or a single value (which will be applied to all guns).
                    RGB vals are applied to simple textures and to greyscale
                    image files but not to RGB images.

                    **NB** units range -1:1 (so 0.0 is GREY). This might seem
                    strange but it's great for vision scientists
                    because they usually change things relative to a
                    midpoint rather than relative to black.

            - **dkl:** a tuple (45.0,90.0, 1.0) or a list [45.0,90.0, 1.0]
                    specifying the coordinates of the stimuli in cone-opponent
                    space (Derrington, Krauskopf, Lennie 1984)
                    Triplets represent [elevation, azimuth, magnitude].
                    Note that the monitor must be calibrated for this to be
                    accurate (if not, example phosphors from a Sony Trinitron
                    CRT will be used).

            - **lms:** a tuple (0.5, 1.0, 1.0) or a list [0.5, 1.0, 1.0]
                    specifying the coordinates of the stimuli in cone space
                    Triplets represent relative modulation of each cone [L, M, S].
                    Note that the monitor must be calibrated for this to be
                    accurate (if not, example phosphors from a Sony Trinitron
                    CRT will be used).

            - **contrast** 1.0,
                    How far the stimulus deviates from the middle grey.
                    Contrast can vary -1:1 (this is a multiplier for the
                    values given in the color description of the stimulus)

            - **opacity** 1.0,
                    1.0 is opaque, 0.0 is transparent

            - **depth** 0,
                    This can potentially be used (not tested!) to choose which
                    stimulus overlays which. (more negative values are nearer).
                    At present the window does not do perspective rendering
                    but could do if that's really useful(?!)

        """
        self.win = win
        if win._haveShaders: self._useShaders=True#by default, this is a good thing
        else: self._useShaders=False
        if len(units): self.units = units
        else: self.units = win.units
        self.ori = float(ori)
        self.texRes = texRes #must be power of 2
        self.angularRes = angularRes
        self.radialPhase = radialPhase
        self.radialCycles = radialCycles
        self.maskRadialPhase = 0
        self.visibleWedge = visibleWedge
        self.angularCycles = angularCycles
        self.angularPhase = angularPhase
        self.contrast = float(contrast)
        self.opacity = opacity
        self.pos = numpy.array(pos)
        self.interpolate=interpolate

        #these are defined by the AlphaStim but will just cause confusion here!
        self.setSF = None
        self.setPhase = None
        self.setSF = None

        #for rgb allow user to give a single val and apply to all channels
        if type(rgb)==float or type(rgb)==int: #user may give a luminance val
            self.rgb=numpy.array((rgb,rgb,rgb), float)
        else:
            self.rgb = numpy.asarray(rgb, float)
        if type(rgbPedestal)==float or type(rgbPedestal)==int: #user may give a luminance val
            self.rgbPedestal=numpy.array((rgbPedestal,rgbPedestal,rgbPedestal), float)
        else:
            self.rgbPedestal = numpy.asarray(rgbPedestal, float)

        if dkl:
            self.dkl = dkl
            self.rgb = psychopy.misc.dkl2rgb(dkl, win.dkl_rgb)
        elif lms:
            self.lms = lms
            #warn('LMS-to-RGB conversion is not properly tested yet - it should NOT be used for proper research!')
            self.rgb = psychopy.misc.lms2rgb(lms, win.lms_rgb)

        self.depth=depth

        #size
        if type(size) in [tuple,list]:
            self.size = numpy.array(size,float)
        else:
            self.size = numpy.array((size,size),float)#make a square if only given one dimension
        #initialise textures for stimulus
        if self.win.winType=="pyglet":
            self.texID=GL.GLuint()
            GL.glGenTextures(1, ctypes.byref(self.texID))
            self.maskID=GL.GLuint()
            GL.glGenTextures(1, ctypes.byref(self.maskID))
        else:
            (self.texID, self.maskID) = GL.glGenTextures(2)
        self.setTex(tex)
        self.setMask(mask)
        
        #generate a displaylist ID
        self._listID = GL.glGenLists(1)

        #
        self._triangleWidth = pi*2/self.angularRes
        self._angles = numpy.arange(0,pi*2, self._triangleWidth, dtype='float64')
        #which vertices are visible?
        self._visible = (self._angles>=(self.visibleWedge[0]*pi/180))#first edge of wedge
        self._visible[(self._angles+self._triangleWidth)*180/pi>(self.visibleWedge[1])] = False#second edge of wedge
        self._nVisible = numpy.sum(self._visible)*3

        self._updateTextureCoords()
        self._updateMaskCoords()
        self._updateXY()
        self._updateList()#ie refresh display list

    def setSize(self, value, operation=None):
        exec('self.size' + operation+ '=value')
        self._updateXY()
        self.needUpdate=True
    def setAngularCycles(self,value,operation=None):
        """set the number of cycles going around the stimulus"""
        exec('self.angularCycles' + operation+ '=value')
        self._updateTextureCoords()
        self.needUpdate=True
    def setRadialCycles(self,value,operation=None):
        """set the number of texture cycles from centre to periphery"""
        exec('self.radialCycles' + operation+ '=value')
        self._updateTextureCoords()
        self.needUpdate=True
    def setAngularPhase(self,value, operation=None):
        """set the angular phase of the texture"""
        exec('self.angularPhase' + operation+ '=value')
        self._updateTextureCoords()
        self.needUpdate=True
    def setRadialPhase(self,value, operation=None):
        """set the radial phase of the texture"""
        self._updateTextureCoords()
        exec('self.radialPhase' + operation+ '=value')
        self.needUpdate=True

    def draw(self, win=None):
        """
        Draw the stimulus in its relevant window. You must call
        this method after every MyWin.flip() if you want the
        stimulus to appear on that frame and then update the screen
        again.
        
        If win is specified then override the normal window of this stimulus.
        """
        #set the window to draw to
        if win==None: win=self.win
        if win.winType=='pyglet': win.winHandle.switch_to()

        #work out next default depth
        if self.depth==0:
            thisDepth = self.win._defDepth
            self.win._defDepth += _depthIncrements[self.win.winType]
        else:
            thisDepth=self.depth

        #do scaling
        GL.glPushMatrix()#push before the list, pop after
        #GL.glLoadIdentity() #implicitly done by push/pop?
        #scale the viewport to the appropriate size
        win.setScale(self.units)
        #move to centre of stimulus and rotate
        GL.glTranslatef(self.pos[0],self.pos[1],thisDepth)
        GL.glRotatef(-self.ori,0.0,0.0,1.0)

        #setup color
        desiredRGB = (self.rgb*self.contrast+1)/2.0#RGB in range 0:1 and scaled for contrast
        if numpy.any(desiredRGB**2.0>1.0):
            desiredRGB=[0.6,0.6,0.4]
        GL.glColor4f(desiredRGB[0],desiredRGB[1],desiredRGB[2], self.opacity)

        #the list does the texture mapping
        if self.needUpdate: self._updateList()
        GL.glCallList(self._listID)

        #return the view to previous state
        GL.glPopMatrix()

    def _updateXY(self):
        """Update if the SIZE changes"""
        #triangles = [trisX100, verticesX3, xyX2]
        self._XY = numpy.zeros([self.angularRes, 3, 2])
        self._XY[:,1,0] = numpy.sin(self._angles)*self.size[0]/2 #x position of 1st outer vertex
        self._XY[:,1,1] = numpy.cos(self._angles)*self.size[1]/2#y position of 1st outer vertex
        self._XY[:,2,0] = numpy.sin(self._angles+self._triangleWidth)*self.size[0]/2#x position of 2nd outer vertex
        self._XY[:,2,1] = numpy.cos(self._angles+self._triangleWidth)*self.size[1]/2#y position of 2nd outer vertex

        self._visibleXY = self._XY[self._visible,:,:]
        self._visibleXY = self._visibleXY.reshape(self._nVisible,2)

    def _updateTextureCoords(self):
        #calculate texture coordinates if angularCycles or Phase change
        self._textureCoords = numpy.zeros([self.angularRes, 3, 2])
        self._textureCoords[:,0,0] = (self._angles+self._triangleWidth/2)*self.angularCycles/(2*pi)+self.angularPhase #x position of inner vertex
        self._textureCoords[:,0,1] = -self.radialPhase #y position of inner vertex
        self._textureCoords[:,1,0] = (self._angles)*self.angularCycles/(2*pi)+self.angularPhase #x position of 1st outer vertex
        self._textureCoords[:,1,1] = self.radialCycles-self.radialPhase#y position of 1st outer vertex
        self._textureCoords[:,2,0] = (self._angles+self._triangleWidth)*self.angularCycles/(2*pi)+self.angularPhase#x position of 2nd outer vertex
        self._textureCoords[:,2,1] = self.radialCycles-self.radialPhase#y position of 2nd outer vertex
        self._visibleTexture = self._textureCoords[self._visible,:,:].reshape(self._nVisible,2)

    def _updateMaskCoords(self):
        #calculate mask coords
        self._maskCoords = numpy.zeros([self.angularRes, 3, 2]) + self.maskRadialPhase
        self._maskCoords[:,1:3,:] = 1 + self.maskRadialPhase#all outer points have mask value of 1
        self._visibleMask = self._maskCoords[self._visible,:,:].reshape(self._nVisible,2)


    def _updateListShaders(self):
        """
        The user shouldn't need this method since it gets called
        after every call to .set() Basically it updates the OpenGL
        representation of your stimulus if some parameter of the
        stimulus changes. Call it if you change a property manually
        rather than using the .set() command
        """
        self.needUpdate=0
        GL.glNewList(self._listID,GL.GL_COMPILE)

        #setup the shaderprogram
        GL.glUseProgram(self.win._progSignedTexMask)
        GL.glUniform1i(GL.glGetUniformLocation(self.win._progSignedTex, "texture"), 0) #set the texture to be texture unit 0
        GL.glUniform1i(GL.glGetUniformLocation(self.win._progSignedTex, "mask"), 1)  # mask is texture unit 1

        #assign vertex array
        if self.win.winType=='pyglet':
            arrPointer = self._visibleXY.ctypes.data_as(ctypes.POINTER(ctypes.c_float))
            GL.glVertexPointer(2, GL.GL_FLOAT, 0, arrPointer) 
        else:
            GL.glVertexPointerd(self._visibleXY)#must be reshaped in to Nx2 coordinates
        GL.glEnableClientState(GL.GL_VERTEX_ARRAY)

        #bind and enable textures
        #main texture
        GL.glActiveTexture(GL.GL_TEXTURE0)
        GL.glBindTexture(GL.GL_TEXTURE_2D, self.texID)
        GL.glEnable(GL.GL_TEXTURE_2D)
        #mask
        GL.glActiveTexture(GL.GL_TEXTURE1)
        GL.glBindTexture(GL.GL_TEXTURE_2D, self.maskID)
        GL.glEnable(GL.GL_TEXTURE_2D)

        #set pointers to visible textures
        GL.glClientActiveTexture(GL.GL_TEXTURE0)
        if self.win.winType=='pyglet':
            arrPointer = self._visibleTexture.ctypes.data_as(ctypes.POINTER(ctypes.c_float))
            GL.glTexCoordPointer(2, GL.GL_FLOAT, 0, arrPointer) 
        else:
            GL.glTexCoordPointerd(self._visibleTexture)
        GL.glEnableClientState(GL.GL_TEXTURE_COORD_ARRAY)
        #mask
        GL.glClientActiveTexture(GL.GL_TEXTURE1)
        if self.win.winType=='pyglet':
            arrPointer = self._visibleMask.ctypes.data_as(ctypes.POINTER(ctypes.c_float))
            GL.glTexCoordPointer(2, GL.GL_FLOAT, 0, arrPointer) 
        else:
            GL.glTexCoordPointerd(self._visibleMask)
        GL.glEnableClientState(GL.GL_TEXTURE_COORD_ARRAY)

        #do the drawing
        GL.glDrawArrays(GL.GL_TRIANGLES, 0, self._nVisible)

        #disable set states
        GL.glDisableClientState(GL.GL_VERTEX_ARRAY)
        GL.glDisableClientState(GL.GL_TEXTURE_COORD_ARRAY)
        GL.glDisable(GL.GL_TEXTURE_2D)

        #setup the shaderprogram
        GL.glUseProgram(0)
        GL.glEndList()

    def _updateListNoShaders(self):
        """
        The user shouldn't need this method since it gets called
        after every call to .set() Basically it updates the OpenGL
        representation of your stimulus if some parameter of the
        stimulus changes. Call it if you change a property manually
        rather than using the .set() command
        """
        self.needUpdate=0
        GL.glNewList(self._listID,GL.GL_COMPILE)
        GL.glColor4f(1.0,1.0,1.0,1.0)#glColor can interfere with multitextures

        #assign vertex array
        if self.win.winType=='pyglet':
            arrPointer = self._visibleXY.ctypes.data_as(ctypes.POINTER(ctypes.c_double))
            GL.glVertexPointer(2, GL.GL_DOUBLE, 0, arrPointer) 
        else:
            GL.glVertexPointerd(self._visibleXY)#must be reshaped in to Nx2 coordinates
        GL.glEnableClientState(GL.GL_VERTEX_ARRAY)

        #bind and enable textures
        #main texture
        GL_multitexture.glActiveTextureARB(GL_multitexture.GL_TEXTURE0_ARB)
        GL.glBindTexture(GL.GL_TEXTURE_2D, self.texID)
        GL.glEnable(GL.GL_TEXTURE_2D)
        #mask
        GL_multitexture.glActiveTextureARB(GL_multitexture.GL_TEXTURE1_ARB)
        GL.glBindTexture(GL.GL_TEXTURE_1D, self.maskID)
        GL.glDisable(GL.GL_TEXTURE_2D)
        GL.glEnable(GL.GL_TEXTURE_1D)

        #set pointers to visible textures
        GL_multitexture.glClientActiveTextureARB(GL_multitexture.GL_TEXTURE0_ARB)
        if self.win.winType=='pyglet':
            arrPointer = self._visibleTexture.ctypes.data_as(ctypes.POINTER(ctypes.c_double))
            GL.glTexCoordPointer(2, GL.GL_DOUBLE, 0, arrPointer) 
        else:
            GL.glTexCoordPointerd(self._visibleTexture)
        GL.glEnableClientState(GL.GL_TEXTURE_COORD_ARRAY)
        #mask
        GL_multitexture.glClientActiveTextureARB(GL_multitexture.GL_TEXTURE1_ARB)
        if self.win.winType=='pyglet':
            arrPointer = self._visibleMask.ctypes.data_as(ctypes.POINTER(ctypes.c_double))
            GL.glTexCoordPointer(2, GL.GL_DOUBLE, 0, arrPointer) 
        else:
            GL.glTexCoordPointerd(self._visibleMask)
        GL.glEnableClientState(GL.GL_TEXTURE_COORD_ARRAY)

        #do the drawing
        GL.glDrawArrays(GL.GL_TRIANGLES, 0, self._nVisible)

        #disable set states
        GL.glDisableClientState(GL.GL_VERTEX_ARRAY)
        GL.glDisableClientState(GL.GL_TEXTURE_COORD_ARRAY)
        GL.glDisable(GL.GL_TEXTURE_2D)

        GL.glEndList()

    def setTex(self,value):
        self._texName = value
        createTexture(value, id=self.texID, pixFormat=GL.GL_RGB, stim=self, res=self.texRes)
    def setMask(self,value):        
        """Users shouldn't use this method
        """
        self._maskName = value
        res = self.texRes#resolution of texture - 128 is bearable
        step = 1.0/res
        rad = numpy.arange(0,1+step,step)
        if type(self._maskName) == numpy.ndarray:
            #handle a numpy array
            intensity = 255*maskName.astype(float)
            res = len(intensity)
            fromFile=0
        elif type(self._maskName) == list:
            #handle a numpy array
            intensity = 255*numpy.array(maskName, 'f')
            res = len(intensity)
            fromFile=0
        elif self._maskName is "circle":
            intensity = 255.0*(rad<=1)
            fromFile=0
        elif self._maskName is "gauss":
            sigma = 1/3.0;
            intensity = 255.0*numpy.exp( -rad**2.0 / (2.0*sigma**2.0) )#3sd.s by the edge of the stimulus
            fromFile=0
        elif self._maskName is "radRamp":#a radial ramp
            intensity = 255.0-255.0*rad
            intensity = numpy.where(rad<1, intensity, 0)#half wave rectify
            fromFile=0
        elif self._maskName in [None,"none"]:
            res=4
            intensity = 255.0*numpy.ones(res,float)
            fromFile=0
        else:#might be a filename of a tiff
            try:
                im = Image.open(self._maskName)
                im = im.transpose(Image.FLIP_TOP_BOTTOM)
                im = im.resize([max(im.size), max(im.size)],Image.BILINEAR)#make it square
            except IOError, (details):
                log.error("couldn't load mask...%s: %s" %(value,details))
                return
            res = im.size[0]
            im = im.convert("L")#force to intensity (in case it was rgb)
            intensity = numpy.asarray(im)

        data = intensity.astype(numpy.uint8)
        mask = data.tostring()#serialise

        #do the openGL binding
        if self.interpolate: smoothing=GL.GL_LINEAR
        else: smoothing=GL.GL_NEAREST
        GL.glBindTexture(GL.GL_TEXTURE_1D, self.maskID)
        GL.glTexImage1D(GL.GL_TEXTURE_1D, 0, GL.GL_ALPHA,
                        res, 0,
                        GL.GL_ALPHA, GL.GL_UNSIGNED_BYTE, mask)
        GL.glTexParameteri(GL.GL_TEXTURE_1D,GL.GL_TEXTURE_WRAP_S,GL.GL_REPEAT) #makes the texture map wrap (this is actually default anyway)
        GL.glTexParameteri(GL.GL_TEXTURE_1D,GL.GL_TEXTURE_MAG_FILTER,smoothing)     #linear smoothing if texture is stretched
        GL.glTexParameteri(GL.GL_TEXTURE_1D,GL.GL_TEXTURE_MIN_FILTER,smoothing)
        GL.glTexEnvi(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_MODULATE)
        GL.glEnable(GL.GL_TEXTURE_1D)

        self.needUpdate=True
        


class ElementArrayStim:
    """
    This stimulus class defines a field of elements whose behaviour can be independently
    controlled. Suitable for creating 'global form' stimuli or more detailed random dot
    stimuli. 
    This stimulus can draw thousands of elements without dropping a frame, but in order
    to achieve this performance, uses several OpenGL extensions only available on modern
    graphics cards (supporting OpenGL2.0). See the ElementArray demo.
    """
    def __init__(self,
                 win,
                 units = None,
                 fieldPos = (0.0,0.0),
                 fieldSize = (1.0,1.0),
                 fieldShape = 'circle',
                 nElements = 100,
                 sizes = 2.0,
                 xys = None,
                 rgbs = [1.0,1.0,1.0],
                 opacities = 1.0,
                 depths = 0,
                 oris = 0,
                 sfs=1.0,
                 contrs = 1,
                 phases=0,
                 elementTex='sin',
                 elementMask='gauss',
                 texRes=48):
        
        """
        **Arguments:**
        
            - **win:** a Window() object required - the stimulus must know where to draw itself!
                            
            - **units:**
                + None (use the current units of the Window)
                + **or** 'norm' (normalised: window voes from -1:1 in each direction)
                + **or**  'cm','pix','deg' (but these real-world units need you to give sufficient info about your monitor, see below)
                  
            - **fieldPos:** The centre of the array of elements
            
            - **fieldSize:** The size of the array of elements (this will be overridden by setting explicit xy positions for the elements)
                        
            - **fieldShape:** The shape of the array (circle or 
            
                        
                    
                """
        self.win = win        
        if units in [None, "", []]:
            self.units = win.units
        else:
            self.units = units
        self.fieldPos = fieldPos
        self.fieldSize = fieldSize
        self.fieldShape = fieldShape
        self.nElements = nElements
        #info for each element
        self.sizes = sizes
        self.rgbs=rgbs
        self.xys= xys
        self.opacities = opacities
        self.oris = oris
        self.contrs = contrs
        self.phases = phases
        self.needVertexUpdate=True
        self.needColorUpdate=True
        self._useShaders=True
        self.interpolate=True
        
        if self.win.winType != 'pyglet':
            raise TypeError('ElementArray requires a pyglet context')
        
        if depths==0:
            self.depth = win._defDepth
            win._defDepth -= 0.0001# -ve depth means closer to viewer
        else:
            self.depths=depths
        
        #Deal with input for fieldpos
        if type(fieldPos) in [tuple,list]:
            self.fieldPos = numpy.array(fieldPos,float)
        else:
            self.fieldPos = numpy.array((fieldPos,fieldPos),float)
            
        #Deal with input for fieldsize
        if type(fieldSize) in [tuple,list]:
            self.fieldSize = numpy.array(fieldSize,float)
        else:
            self.fieldSize = numpy.array((fieldSize,fieldSize),float)#make a square if only given one dimension
            
        #create textures
        self.texRes=texRes
        self.texID=GL.GLuint()
        GL.glGenTextures(1, ctypes.byref(self.texID))
        self.maskID=GL.GLuint()
        GL.glGenTextures(1, ctypes.byref(self.maskID))
        self.setMask(elementMask)
        self.setTex(elementTex)
        
        self.setContrs(contrs)
        self.setRgbs(rgbs)
        self.setOpacities(opacities)#opacities is used by setRgbs, so this needs to be early
        self.setXYs(xys)
        self.setOris(oris)  
        self.setSizes(sizes) #set sizes before sfs (sfs may need it formatted)
        self.setSfs(sfs)
        self.setPhases(phases)
                
    def setXYs(self,value=None, operation=''):
        """Set the xy values of the element centres (relative to the centre of the field).
        Values should be:            
            
            - None
            - an array/list of Nx2 coordinates.
            
        If value is None then the xy positions will be generated automatically, based
        on the fieldSize and fieldPos. In this case opacity will also be overridden
        by this function (it is used to make elements outside the field invisible.
        """
        if value==None:
            if self.fieldShape is 'sqr':
                self.xys = numpy.random.rand(self.nElements,2)*self.fieldSize - self.fieldSize/2 #initialise a random array of X,Y
                #gone outside the square
                self.xys[:,0] = ((self.xys[:,0]+self.fieldSize[0]/2) % self.fieldSize[0])-self.fieldSize[0]/2
                self.xys[:,1] = ((self.xys[:,1]+self.fieldSize[1]/2) % self.fieldSize[1])-self.fieldSize[1]/2
            elif self.fieldShape is 'circle':
                #take twice as many elements as we need (and cull the ones outside the circle)
                xys = numpy.random.rand(self.nElements*2,2)*self.fieldSize - self.fieldSize/2 #initialise a random array of X,Y
                #gone outside the square
                xys[:,0] = ((xys[:,0]+self.fieldSize[0]/2) % self.fieldSize[0])-self.fieldSize[0]/2
                xys[:,1] = ((xys[:,1]+self.fieldSize[1]/2) % self.fieldSize[1])-self.fieldSize[1]/2
                #use a circular envelope and flips dot to opposite edge if they fall
                #beyond radius.
                #NB always circular - uses fieldSize in X only
                normxy = xys/(self.fieldSize/2.0)
                dotDist = numpy.sqrt((normxy[:,0]**2.0 + normxy[:,1]**2.0))
                self.xys = xys[dotDist<1.0,:][0:self.nElements]
        else:        
            #make into an array
            if type(value) in [int, float, list, tuple]:
                value = numpy.array(value)        
            #check shape
            if value.shape != (self.nElements,2):
                raise ValueError("New value for setXYs should be either None or Nx2")
            if operation=='':
                self.xys=value    
            else: exec('self.xys'+operation+'=value')            
        self.needVertexUpdate=True
            
    def setOris(self,value,operation=''):
        """Set the orientation for each element. 
        Should either be a single value or an Nx1 array/list
        """
        #make into an array
        if type(value) in [int, float, list, tuple]:
            value = numpy.array(value)
        
        #check shape
        if value.shape in [(),(1,)]:
            value = value.repeat(self.nElements)
        elif value.shape in [(self.nElements,), (self.nElements,1)]:
            pass #is already Nx1
        else:
            raise ValueError("New value for setOris should be either Nx1 or a single value")
        if operation=='':
            self.oris=value    
        else: exec('self.oris'+operation+'=value')
        self.needVertexUpdate=True
    #----------------------------------------------------------------------
    def setSfs(self, value,operation=''):
        """Set the spatial frequency for each element. 
        Should either be:
        
          - a single value 
          - an Nx1 array/list 
          - an Nx2 array/list (spatial frequency of the element in X and Y).
          
        If the units for the stimulus are 'pix' or 'norm' then the units of sf
        are cycles per stimulus width. For units of 'deg' or 'cm' the units
        are c/cm or c/deg respectively.
          
        """
        
        #make into an array
        if type(value) in [int, float, list, tuple]:
            value = numpy.array(value)
        
        #check shape
        if value.shape in [(),(1,)]:
            value = numpy.resize(value, [self.nElements,2])
        elif value.shape in [(self.nElements,), (self.nElements,1)]:
            value.shape=(self.nElements,1)#set to be 2D
            value = value.repeat(2,1) #repeat once on dim 1
        elif value.shape == (self.nElements,2):
            pass#all is good
        else:
            raise ValueError("New value for setSfs should be either Nx1, Nx2 or a single value")
            
        if operation=='':
            self.sfs=value    
        else: exec('self.sfs'+operation+'=value')
                        
    def setOpacities(self,value,operation=''):
        """Set the opacity for each element. 
        Should either be a single value or an Nx1 array/list
        """
        #make into an array
        if type(value) in [int, float, list, tuple]:
            value = numpy.array(value)
        
        #check shape
        if value.shape in [(),(1,)]:
            value = value.repeat(self.nElements)
        elif value.shape in [(self.nElements,), (self.nElements,1)]:
            pass #is already Nx1
        else:
            raise ValueError("New value for setOpacities should be either Nx1 or a single value")
        
        if operation=='':
            self.opacities=value    
        else: exec('self.opacities'+operation+'=value')
        self.needTexCoordUpdate =True
        
    def setSizes(self,value,operation=''):
        """Set the size for each element. 
        Should either be:
        
          - a single value 
          - an Nx1 array/list 
          - an Nx2 array/list
        """
        #make into an array
        if type(value) in [int, float, list, tuple]:
            value = numpy.array(value)
        #check shape
        if value.shape in [(),(1,)]:
            value = numpy.resize(value, [self.nElements,2])
        elif value.shape in [(self.nElements,), (self.nElements,1)]:
            value.shape=(self.nElements,1)#set to be 2D
            value = value.repeat(2,1) #repeat once on dim 1
        elif value.shape == (self.nElements,2):
            pass#all is good
        else:
            raise ValueError("New value for setSizes should be either Nx1, Nx2 or a single value")
            
        if operation=='':
            self.sizes=value    
        else: exec('self.sizes'+operation+'=value')
        self.needVertexUpdate=True    
        
    def setPhases(self,value,operation=''):
        """Set the phase for each element. 
        Should either be:
        
          - a single value 
          - an Nx1 array/list 
          - an Nx2 array/list (for separate X and Y phase)
        """
        #make into an array
        if type(value) in [int, float, list, tuple]:
            value = numpy.array(value)
        
        #check shape
        if value.shape in [(),(1,)]:
            value = numpy.resize(value, [self.nElements,2])
        elif value.shape in [(self.nElements,), (self.nElements,1)]:
            value.shape=(self.nElements,1)#set to be 2D
            value = value.repeat(2,1) #repeat once on dim 1
        elif value.shape == (self.nElements,2):
            pass#all is good
        else:
            raise ValueError("New value for setPhases should be either Nx1, Nx2 or a single value")
            
        if operation=='':
            self.phases=value    
        else: exec('self.phases'+operation+'=value')
        self.needTexCoordUpdate=True
        
    def setRgbs(self,value,operation=''):
        """Set the rgb for each element. 
        Should either be:
        
          - a single value 
          - an Nx1 array/list 
          - an Nx3 array/list
        """
        #make into an array
        if type(value) in [int, float, list, tuple]:
            value = numpy.array(value)
        
        #check shape
        if value.shape in [(), (1,),(3,)]:
            value = numpy.resize(value, [self.nElements,3])
        elif value.shape in [(self.nElements,), (self.nElements,1)]:
            value.shape=(self.nElements,1)#set to be 2D
            value = value.repeat(3,1) #repeat once on dim 1
        elif value.shape == (self.nElements,3):
            pass#all is good
        else:
            raise ValueError("New value for setRgbs should be either Nx1, Nx3 or a single value")
        if operation=='':
            self.rgbs=value    
        else: exec('self.rgbs'+operation+'=value')
        self.needColorUpdate=True 
    def setContrs(self,value,operation=''):
        """Set the contrast for each element. 
        Should either be:
        
          - a single value 
          - an Nx1 array/list 
        """
        #make into an array
        if type(value) in [int, float, list, tuple]:
            value = numpy.array(value)
        #check shape
        if value.shape in [(),(1,)]:
            value = value.repeat(self.nElements)
        elif value.shape in [(self.nElements,), (self.nElements,1)]:
            pass #is already Nx1
        else:
            raise ValueError("New value for setContrs should be either Nx1 or a single value")
            
        if operation=='':
            self.contrs=value    
        else: exec('self.contrs'+operation+'=value')
        self.needColorUpdate=True 
    def setFieldPos(self,value,operation=''):
        """Set the centre of the array (X,Y)
        """
        #make into an array
        if type(value) in [int, float, list, tuple]:
            value = numpy.array(value)
        #check shape
        if value.shape != (2,):
            raise ValueError("New value for setFieldPos should be [x,y]")
        
        if operation=='':
            self.fieldPos=value
        else:
            exec('self.fieldPos'+operation+'=value')
    
    def setFieldSize(self,value,operation=''):
        """Set the size of the array on the screen (will override
        current XY positions of the elements)
        """
        #make into an array
        if type(value) in [int, float, list, tuple]:
            value = numpy.array(value)
        #check shape
        if value.shape not in [(2,),(1,)]:
            raise ValueError("New value for setFieldSize should be [x,y] or a single value")
        
        if operation=='':
            self.fieldSize=value
        else:
            exec('self.fieldSize'+operation+'=value')
        self.setXYs()#to reflect new settings, overriding individual xys 
        
    def draw(self):
        """
        Draw the stimulus in its relevant window. You must call
        this method after every MyWin.update() if you want the
        stimulus to appear on that frame and then update the screen
        again.
        """        
        if self.needVertexUpdate: 
            self.updateElementVertices()
        if self.needColorUpdate:
            self.updataElementColors()
        if self.needTexCoordUpdate:
            self.updataTextureCoords()
            
        #scale the drawing frame and get to centre of field
        GL.glPushMatrix()#push before drawing, pop after        
        GL.glPushClientAttrib(GL.GL_CLIENT_ALL_ATTRIB_BITS)#push the data for client attributes
        
        GL.glLoadIdentity()
        self.win.setScale(self.units)
        GL.glTranslatef(self.fieldPos[0],self.fieldPos[1],0.0)
               
        GL.glColorPointer(4, GL.GL_DOUBLE, 0, self._RGBAs.ctypes)
        GL.glVertexPointer(3, GL.GL_DOUBLE, 0, self._visXYZvertices.ctypes)#.data_as(ctypes.POINTER(ctypes.c_float)))

        #setup the shaderprogram        
        GL.glUseProgram(self.win._progSignedTexMask)
        GL.glUniform1i(GL.glGetUniformLocation(self.win._progSignedTexMask, "texture"), 0) #set the texture to be texture unit 0
        GL.glUniform1i(GL.glGetUniformLocation(self.win._progSignedTexMask, "mask"), 1)  # mask is texture unit 1
        
        #setup client texture coordinates first
        GL.glClientActiveTexture (GL.GL_TEXTURE0)   
        GL.glTexCoordPointer (2, GL.GL_DOUBLE, 0, self._texCoords.ctypes)  
        GL.glEnableClientState(GL.GL_TEXTURE_COORD_ARRAY)    
        GL.glClientActiveTexture (GL.GL_TEXTURE1)   
        GL.glTexCoordPointer (2, GL.GL_DOUBLE, 0, self._maskCoords.ctypes)  
        GL.glEnableClientState(GL.GL_TEXTURE_COORD_ARRAY)    
        #then bind textures
        GL.glActiveTexture (GL.GL_TEXTURE1)     
        GL.glEnable(GL.GL_TEXTURE_2D)
        GL.glBindTexture (GL.GL_TEXTURE_2D, self.maskID)
        GL.glActiveTexture (GL.GL_TEXTURE0)     
        GL.glEnable(GL.GL_TEXTURE_2D)
        GL.glBindTexture (GL.GL_TEXTURE_2D, self.texID)
        
        GL.glEnableClientState(GL.GL_VERTEX_ARRAY)
        GL.glEnableClientState(GL.GL_COLOR_ARRAY)
        GL.glDrawArrays(GL.GL_QUADS, 0, self._visXYZvertices.shape[0]*4)
        GL.glDisableClientState(GL.GL_COLOR_ARRAY)
        GL.glDisableClientState(GL.GL_VERTEX_ARRAY)
        GL.glDisableClientState(GL.GL_TEXTURE_COORD_ARRAY)
        
        GL.glPopClientAttrib()
        GL.glPopMatrix()
        
    def updateElementVertices(self):
        self._visXYZvertices=numpy.zeros([self.nElements , 4, 3],'d')
        wx = self.sizes[:,0]*numpy.cos(self.oris[:]*numpy.pi/180)/2
        wy = self.sizes[:,0]*numpy.sin(self.oris[:]*numpy.pi/180)/2
        hx = self.sizes[:,1]*numpy.sin(self.oris[:]*numpy.pi/180)/2
        hy = -self.sizes[:,1]*numpy.cos(self.oris[:]*numpy.pi/180)/2
        
        #X
        self._visXYZvertices[:,0,0] = self.xys[:,0] -wx + hx#TopL
        self._visXYZvertices[:,1,0] = self.xys[:,0] +wx + hx#TopR
        self._visXYZvertices[:,2,0] = self.xys[:,0] +wx - hx#BotR
        self._visXYZvertices[:,3,0] = self.xys[:,0] -wx - hx#BotL
        
        #Y
        self._visXYZvertices[:,0,1] = self.xys[:,1] -wy + hy
        self._visXYZvertices[:,1,1] = self.xys[:,1] +wy + hy
        self._visXYZvertices[:,2,1] = self.xys[:,1] +wy - hy
        self._visXYZvertices[:,3,1] = self.xys[:,1] -wy - hy
        
        #depth
        self._visXYZvertices[:,:,2] = numpy.arange(0,0.001*self.nElements,0.001).repeat(4).reshape(self.nElements, 4)
        
        self.needVertexUpdate=False
        
    #----------------------------------------------------------------------
    def updataElementColors(self):
        """Create a new array of self._RGBAs"""
        
        N=self.nElements
        self._RGBAs=numpy.zeros([N,4],'d')
        self._RGBAs[:,0:3] = self.rgbs[:,:] * self.contrs[:].reshape([N,1]).repeat(3,1)/2+0.5
        self._RGBAs[:,-1] = self.opacities
        self._RGBAs=self._RGBAs.reshape([N,1,4]).repeat(4,1)#repeat for the 4 vertices in the grid
        self.needColorUpdate=False
    def updataTextureCoords(self):
        """Create a new array of self._maskCoords"""
        
        N=self.nElements
        self._maskCoords=numpy.array([[0,1],[1,1],[1,0],[0,0]],'d').reshape([1,4,2])
        self._maskCoords = self._maskCoords.repeat(N,0)        
        
        #for the main texture
        if self.units in ['norm', 'pix']:#sf is dependent on size (openGL default)
            L = -self.sfs[:,0]/2 - self.phases[:,0]+0.5
            R = +self.sfs[:,0]/2 - self.phases[:,0]+0.5
            T = +self.sfs[:,1]/2 - self.phases[:,1]+0.5
            B = -self.sfs[:,1]/2 - self.phases[:,1]+0.5
        else: #we should scale to become independent of size        
            L = -self.sfs[:,0]*self.sizes[:,0]/2 - self.phases[:,0]+0.5
            R = +self.sfs[:,0]*self.sizes[:,0]/2 - self.phases[:,0]+0.5
            T = +self.sfs[:,1]*self.sizes[:,1]/2 - self.phases[:,1]+0.5
            B = -self.sfs[:,1]*self.sizes[:,1]/2 - self.phases[:,1]+0.5
            
        #self._texCoords=numpy.array([[1,1],[1,0],[0,0],[0,1]],'d').reshape([1,4,2])
        self._texCoords=numpy.concatenate([[L,T],[R,T],[R,B],[L,B]]) \
            .transpose().reshape([N,4,2]).astype('d')
        self.needTexCoordUpdate=False
        
    def setTex(self,value):
        """Change the texture (all elements have the same base texture). Avoid this 
        during time-critical points in your script. Uploading new textures to the 
        graphics card can be time-consuming.
        """
        self._texName = value
        createTexture(value, id=self.texID, pixFormat=GL.GL_RGB, stim=self, res=self.texRes)
    def setMask(self,value):    
        """Change the mask (all elements have the same mask). Avoid doing this 
        during time-critical points in your script. Uploading new textures to the 
        graphics card can be time-consuming."""    
        self._maskName = value
        createTexture(value, id=self.maskID, pixFormat=GL.GL_ALPHA, stim=self, res=self.texRes)
        
class MovieStim:
    """A stimulus class for playing movies (mpeg, avi, etc...) in 
    PsychoPy. 
    
    **examples**::
    
        mov = visual.MovieStim(myWin, 'testmovie.mpg', fliVert=False)
        print mov.duration
        print mov.format.width, mov.format.height #give the original size of the movie in pixels
        
        mov.draw() #draw the current frame (automagically determined)
        
    See MovieStim.py for demo.
    """
    def __init__(self, win,
                 filename = "",
                 units   = 'pix',
                 size    = None,
                 pos      =(0.0,0.0),
                 ori     =0.0,
                 flipVert = False,
                 flipHoriz = False,
                 opacity=1.0):
        """
        **Arguments:**

            - **win:** a Window() object required - the stimulus must know where to draw itself

            - **filename:**
                a string giving the relative or absolute path to the movie. Can be any movie that 
                AVbin can read (e.g. mpeg, DivX)
                
            - **units:**
                + None (use 'pix')
                + **or** 'norm' (normalised: window goes from -1:1)
                + **or**  'cm','pix','deg' (but these real-world units need you to give sufficient info about your monitor, see below)
                
            - **pos**:
                position of the centre of the movie, given in the units specified
                
            - **flipVert**:
                bool (True/False or 1/-1) if True then the movie will be top-bottom flipped
                
            - **flipHoriz**:
                bool (True/False or 1/-1) if True then the movie will be right-left flipped
            
            - **ori**:
                orientation of the stimulus in degrees
                
            - **size**:
                size of the stimulus in units given. If not specified then the movie will take its
                original dimensions. Thes can be interrogated by 
                
            - **opacity**:
                the movie can be made transparent by reducing this
        """
        self.win = win        
        self._movie=None # the actual pyglet media object
        self._player=pyglet.media.ManagedSoundPlayer()
        self.filename=filename
        self.duration=None
        self.loadMovie( self.filename )
        self.format=self._movie.video_format        
        self.pos=pos
        self.depth=0        
        self.pos = numpy.asarray(pos)
        self.flipVert = flipVert
        self.flipHoriz = flipHoriz
        self.opacity = opacity
        self.playing=0
        
        #size
        if size == None: self.size= numpy.array([self.format.width, self.format.height] , float)
        elif type(size) in [tuple,list]: self.size = numpy.array(size,float)
        else: self.size = numpy.array((size,size),float)
        
        self.ori = ori
        if units in [None, "", []]:
            self.units = win.units
        else:
            self.units = units
            
        #check for pyglet
        if win.winType!='pyglet': 
            log.Error('Movie stimuli can only be used with a pyglet window')
            core.quit()
                    
    def loadMovie(self, filename):
        
        self._movie = pyglet.media.load( filename, streaming=True)
        self._player.queue(self._movie)
        self.duration = self._movie.duration
        #self._player.on_eos=self.onEOS #doesn't seem to work
        
    def draw(self, win=None):
        """Draw the current frame to a particular visual.Window (or to the
        default win for this object if not specified). The current position in the
        movie will be determined automatically.
        
        This method should be called on every frame that the movie is meant to appear"""
        if not self._player.playing:
            self._player.play()
            self.playing=1
        #set the window to draw to
        if win==None: win=self.win
        win.winHandle.switch_to()
        
        #work out next default depth
        if self.depth==0:
            thisDepth = self.win._defDepth
            self.win._defDepth += _depthIncrements[self.win.winType]
        else:
            thisDepth=self.depth

        #do scaling
        #scale the viewport to the appropriate size
        self.win.setScale(self.units)
        
        frameTexture = self._player.get_texture()
        GL.glColor4f(1,1,1,self.opacity)
        GL.glPushMatrix()
        #move to centre of stimulus and rotate
        GL.glTranslatef(self.pos[0],self.pos[1],thisDepth)
        GL.glRotatef(-self.ori,0.0,0.0,1.0)
        flipBitX = 1-self.flipHoriz*2
        flipBitY = 1-self.flipVert*2
        frameTexture.blit(
                -self.size[0]/2.0*flipBitX, 
                -self.size[1]/2.0*flipBitY, 
                width=self.size[0]*flipBitX, 
                height=self.size[1]*flipBitY,
                z=thisDepth)        
        GL.glPopMatrix()
    def _onEOS(self):
        #not called, for some reason?!
        self.playing=-1
        print 'movie finished'
class TextStimGLUT:
    """DEPRECATED - please use TextStim instead - they're much nicer!"""
    def __init__(self,win,
                 text="Hello World",
                 font="GLUT_BITMAP_TIMES_ROMAN_24",
                 pos=(0.0,0.0),
                 depth=0,
                 rgb=(1.0,1.0,1.0),
                 opacity=1.0,
                 units="",
                 ori=0.0,
                 letterWidth=10.0,
                 lineWidth=1.0,
                 alignHoriz='center',
                 alignVert='center'):
        self.win = win
        if win._haveShaders: self._useShaders=True#by default, this is a good thing
        else: self._useShaders=False
        
        if len(units): self.units = units
        else: self.units = win.units
        self.pos= numpy.array(pos)
        if type(font)==str:
            self.fontName=font
            exec('self.font=GLUT.'+font)
        else:#presumably was an actual GLUT font enumerator
            self.font = font
        if self.font in [GLUT.GLUT_STROKE_ROMAN, GLUT.GLUT_STROKE_MONO_ROMAN]:
            self.strokeFont=1
        else: self.strokeFont=0
        self.text='' #just a placeholder set below
        self.setText(text) #some additional things get set with text
        self.needUpdate =1
        self.opacity= opacity
        self.contrast= 1.0
        self.alignHoriz = alignHoriz
        self.alignVert = alignVert

        if type(rgb) in [float, int]: #user may give a luminance val
            self.rgb=numpy.asarray((rgb,rgb,rgb), float)
        else:
            self.rgb = numpy.asarray(rgb, float)

        self.depth=depth

        self._listID = GL.glGenLists(1)
        #initialise textures for stimulus
        if self.win.winType=="pyglet":
            self.texID=GL.GLuint()
            GL.glGenTextures(1, ctypes.byref(self.texID))
        else:
            self._texID = GL.glGenTextures(1)
        #self._setColorTex()

        self.ori=ori
        self.lineWidth=lineWidth
        self.letterWidth=letterWidth#width of each character in pix
        GL.glEnable(GL.GL_LINE_SMOOTH)#nicer for stroke fonts

    def _set(self,attrib,val,op=''):
        #can handle single value in -> multi out (e.g. size->h,w)
        if op=='' or op==None:
            exec('self.'+attrib+'*=0') #set all values in array to 0
            exec('self.'+attrib+'+=val') #then add the value to array
        else:
            exec('self.'+attrib+op+'=val')
        self.needUpdate =1

    def set(self, attrib, val, op=''):
        """DEPRECATED
        TextStim.set() is obselete and may not be supported in future
        versions of PsychoPy. Use the specific method for each parameter instead
        (e.g. setOri(), setText()...)
        """
        self._set(attrib, val, op)

    def setOri(self,value,operation=None):
        self._set('ori', value, operation)
    def setPos(self,value,operation=None):
        self._set('pos', value, operation)
    def setRGB(self,value, operation=None):
        self._set('rgb', value, operation)
    def setOpacity(self,value,operation=None):
        self._set('opacity', value, operation)
    def setText(self,value,operation=None):
        self._set('text', value, operation)
        #also update the current length and height
        if self.strokeFont:
            if len(self.text)==1:
                self.length = GLUT.glutStrokeWidth(self.font, ord(self.text))
            else: self.length = GLUT.glutStrokeLength(self.font, self.text)
            self.height = 100.0 #roughly!
        else:
            if len(self.text)==1:
                self.length = GLUT.glutBitmapWidth(self.font, ord(self.text))
            else: self.length = GLUT.glutBitmapLength(self.font, self.text)
            if self.fontName:
                #final two digits of name give height (but overestimated!)
                self.height=float(self.fontName[-2:])-1
            else: self.height = 23.0 #(if the default 24pt font was used)
    def setDepth(self,value, operation=None):
        self._set('depth', value, operation)

    def _updateListShaders(self):
        """
        The user shouldn't need this method since it gets called
        after every call to .set() Basically it updates the OpenGL
        representation of your stimulus if some parameter of the
        stimulus changes. Call it if you change a property manually
        rather than using the .set() command
        """
        GL.glNewList(self._listID, GL.GL_COMPILE)
        GL.glPushMatrix()

        #load Null textures into multitexteureARB - they interfere with glColor
        GL.glActiveTexture(GL.GL_TEXTURE0)
        GL.glEnable(GL.GL_TEXTURE_2D)
        GL.glBindTexture(GL.GL_TEXTURE_2D, 0)
        GL.glActiveTexture(GL.GL_TEXTURE1)
        GL.glEnable(GL.GL_TEXTURE_2D)
        GL.glBindTexture(GL.GL_TEXTURE_2D, 0)

        GL.glColor4f(self.rgb[0]/2.0+0.5, self.rgb[1]/2.0+0.5, self.rgb[2]/2.0+0.5, self.opacity)

        unitScale = self.win.setScale(self.units)
        GL.glTranslatef(self.pos[0],self.pos[1],0)#NB depth is set already
        if self.strokeFont:#draw stroke font stims
            GL.glLineWidth(self.lineWidth)
            GL.glPopMatrix()#get rid of the scaling matrix for positioning
            GL.glPushMatrix()#and make a new one for drawing the characters
            #NB:scaling stroke_fonts requires that we move to the right position
            #to start drawing (setScale above) and then change the scale again
            #so that the letters are spaced correctly
            #alignment
            self.win.setScale('stroke_font', self) #special usage of setScale
            if self.alignHoriz =='center':
                #self.win.setScale('pix')
                GL.glTranslatef(-self.length/2, -self.height/2, 0.0)
            elif self.alignHoriz =='right':
                GL.glTranslatef(-self.length, -self.height, 0.0)
            else:#no need to change
                pass
            #stroke fonts rotate
            GL.glRotatef(self.ori,0.0,0.0,1.0)

            for characters in self.text:
                GLUT.glutStrokeCharacter(self.font,ord(characters))
        else:#draw bitmap font stims
            self.win.setScale('pix', self, prevScale=unitScale)
            #how much to move left
            if self.alignHoriz =='center': left = -self.length/2.0
            elif self.alignHoriz =='right':left = -self.length
            else: left = 0.0
            #how much to move bottom
            if self.alignVert =='center': bottom = -self.height/2.0
            elif self.alignVert =='top': bottom = -self.height
            else: bottom = 0.0
            #do the actual move
            GL.glRasterPos3f(left,bottom,0)

            for characters in self.text:
                GLUT.glutBitmapCharacter(self.font,ord(characters))

        GL.glPopMatrix()

        GL.glEndList()
        self.needUpdate=0

    def _updateListNoShaders(self):
        """
        The user shouldn't need this method since it gets called
        after every call to .set() Basically it updates the OpenGL
        representation of your stimulus if some parameter of the
        stimulus changes. Call it if you change a property manually
        rather than using the .set() command
        """
        GL.glNewList(self._listID, GL.GL_COMPILE)
        GL.glPushMatrix()

        #load Null textures into multitexteureARB - they interfere with glColor
        GL_multitexture.glActiveTextureARB(GL_multitexture.GL_TEXTURE0_ARB)
        GL.glEnable(GL.GL_TEXTURE_2D)
        GL.glBindTexture(GL.GL_TEXTURE_2D, 0)
        GL_multitexture.glActiveTextureARB(GL_multitexture.GL_TEXTURE1_ARB)
        GL.glEnable(GL.GL_TEXTURE_2D)
        GL.glBindTexture(GL.GL_TEXTURE_2D, 0)

        GL.glColor4f(self.rgb[0]/2.0+0.5, self.rgb[1]/2.0+0.5, self.rgb[2]/2.0+0.5, self.opacity)

        unitScale = self.win.setScale(self.units)
        GL.glTranslatef(self.pos[0],self.pos[1],0)#NB depth is set already
        if self.strokeFont:#draw stroke font stims
            GL.glLineWidth(self.lineWidth)
            GL.glPopMatrix()#get rid of the scaling matrix for positioning
            GL.glPushMatrix()#and make a new one for drawing the characters
            #NB:scaling stroke_fonts requires that we move to the right position
            #to start drawing (setScale above) and then change the scale again
            #so that the letters are spaced correctly
            #alignment
            self.win.setScale('stroke_font', self) #special usage of setScale
            if self.alignHoriz =='center':
                #self.win.setScale('pix')
                GL.glTranslatef(-self.length/2, -self.height/2, 0.0)
            elif self.alignHoriz =='right':
                GL.glTranslatef(-self.length, -self.height, 0.0)
            else:#no need to change
                pass
            #stroke fonts rotate
            GL.glRotatef(self.ori,0.0,0.0,1.0)

            for characters in self.text:
                GLUT.glutStrokeCharacter(self.font,ord(characters))
        else:#draw bitmap font stims
            self.win.setScale('pix', self, prevScale=unitScale)
            #how much to move left
            if self.alignHoriz =='center': left = -self.length/2.0
            elif self.alignHoriz =='right':left = -self.length
            else: left = 0.0
            #how much to move bottom
            if self.alignVert =='center': bottom = -self.height/2.0
            elif self.alignVert =='top': bottom = -self.height
            else: bottom = 0.0
            #do the actual move
            GL.glRasterPos3f(left,bottom,0)

            for characters in self.text:
                GLUT.glutBitmapCharacter(self.font,ord(characters))

        GL.glPopMatrix()

        GL.glEndList()
        self.needUpdate=0

    def draw(self, win=None):
        """
        Draw the stimulus in its relevant window. You must call
        this method after every MyWin.flip() if you want the
        stimulus to appear on that frame and then update the screen
        again.
        
        If win is specified then override the normal window of this stimulus.
        """
        #set the window to draw to
        if win==None: win=self.win
        if win.winType=='pyglet': win.winHandle.switch_to()

        #work out next default depth
        if self.depth==0:
            thisDepth = self.win._defDepth
            self.win._defDepth += _depthIncrements[self.win.winType]
        else:
            thisDepth=self.depth

        GL.glPushMatrix()#push before the list, pop after
        if self.needUpdate: self._updateList()
        GL.glCallList(self._listID)
        GL.glPopMatrix()#push before the list, pop after

class TextStim(_BaseVisualStim):
    """Class of text stimuli to be displayed in a **Window()**
    """
    def __init__(self, win,
                 text="Hello World",
                 font="",
                 pos=(0.0,0.0),
                 depth=0,
                 rgb=(1.0,1.0,1.0),
                 opacity=1.0,
                 units="",
                 ori=0.0,
                 height=None,
                 antialias=True,
                 bold=False,
                 italic=False,
                 alignHoriz='center',
                 alignVert='center',
                 fontFiles=[],
                 wrapWidth=None):
        """
            **Arguments:**
        
                - **win:**
                    a Window() object required - the stimulus must know where to draw itself!
        
                - **text**
                    The text to be rendered
        
                - **pos**
                    Position on the screen
                    
                - **depth**
                    Depth on the screen (if None it will be defined on .draw() to be in front of the last object drawn)
                    
                - **rgb**
                    The color of the text (ranging [-1,-1,-1] to [1,1,1])
                    
                - **opacity**
                    How transparent the object will be (0 for transparent, 1 for opaque)
        
                - **units**
                    "pix", "norm", "cm", "deg" only if you want to override the Window's default
                    
                - **ori**
                    Orientation of the text
                    
                - **height**
                    Height of the characters (including the ascent of the letter and the descent)
                    
                - **antialias**
                    boolean to allow (or not) antialiasing the text
                    
                - **bold**
                    Make the text bold (better to use a bold font name)
                    
                - **italic**
                    Make the text italic (better to use an actual italic font)
                    
                - **alignHoriz**
                    The horizontal alignment ('left', 'right' or 'center')
                    
                - **alignVert**
                    The vertical alignment ('top', 'bottom' or 'center')
                    
                - **fontFiles**
                    A list of additional files if the font is not in the standard system location (include the full path)
                    
                - **wrapWidth**
                    The width the text should run before wrapping
        """
        self.win = win
        if win._haveShaders: self._useShaders=True
        else: self._useShaders=False
        self.needUpdate =1
        self.opacity= opacity
        self.contrast= 1.0
        self.alignHoriz = alignHoriz
        self.alignVert = alignVert
        self.antialias = antialias
        self.bold=bold
        self.italic=italic
        self.text='' #NB just a placeholder - real value set below
        self.depth=depth
        self.ori=ori
        self.wrapWidth=wrapWidth
        self._pygletTextObj=None

        if len(units): self.units = units
        else: self.units = win.units
        self.pos= numpy.array(pos)

        #height in pix (needs to be done after units)
        if self.units=='cm':
            if height: self.height = height
            else: self.height = 1.0#default text height
            self.heightPix = psychopy.misc.cm2pix(self.height, win.monitor)
        elif self.units=='deg':
            if height: self.height = height
            else: self.height = 1.0#default text height
            self.heightPix = psychopy.misc.deg2pix(self.height, win.monitor)
        elif self.units=='norm':
            if height: self.height = height
            else: self.height = 0.1#default text height
            self.heightPix = self.height*win.size[1]/2
        else: #treat units as pix
            if height: self.height = height
            else: self.height = 20#default text height
            self.heightPix = self.height
        
        if self.wrapWidth ==None:
            if self.units=='norm': self.wrapWidth=1
            elif self.units=='deg': self.wrapWidth=15
            elif self.units=='cm': self.wrapWidth=15
            elif self.units=='pix': self.wrapWidth=500
        if self.units=='norm': self._wrapWidthPIX= self.wrapWidth*win.size[0]/2
        elif self.units=='deg': self._wrapWidthPIX= psychopy.misc.deg2pix(self.wrapWidth, win.monitor)
        elif self.units=='cm': self._wrapWidthPIX= psychopy.misc.cm2pix(self.wrapWidth, win.monitor)
        elif self.units=='pix': self._wrapWidthPIX=self.wrapWidth
        
        for thisFont in fontFiles:
            pyglet.font.add_file(thisFont)
        self.setFont(font)

        if type(rgb) in [float, int]: #user may give a luminance val
            self.rgb=numpy.asarray((rgb,rgb,rgb), float)
        else:
            self.rgb = numpy.asarray(rgb, float)

        #generate the texture and list holders
        self._listID = GL.glGenLists(1)
        if not self.win.winType=="pyglet":
            self._texID = GL.glGenTextures(1)
        #render the text surfaces and build drawing list
        self.setText(text) #some additional things get set with text

        self.needUpdate=True

    def setFont(self, font):
        """Set the font to be used for text rendering.
        font should be a string specifying the name of the font (in system resources)
        """
        self.fontname=None
        
        if self.win.winType=="pyglet":
            self._font = pyglet.font.load(font, int(self.heightPix), dpi=72, italic=self.italic, bold=self.bold)
            
        else:   
            if font==None or len(font)==0:
                self.fontname = pygame.font.get_default_font()
            elif font in pygame.font.get_fonts():
                self.fontname = font
            elif type(font)==str:
                #try to find a xxx.ttf file for it
                fontFilenames = glob.glob(font+'*')#check for possible matching filenames
                if len(fontFilenames)>0:
                    for thisFont in fontFilenames:
                        if thisFont[-4:] in ['.TTF', '.ttf']:
                            self.fontname = thisFont#take the first match
                            break #stop at the first one we find
                    #trhen check if we were successful
                    if self.fontname == None and font!="":
                        #we didn't find a ttf filename
                        log.warning("Found %s but it doesn't end .ttf. Using default font." %fontFilenames[0])
                        self.fontname = pygame.font.get_default_font()

            if self.fontname is not None and os.path.isfile(self.fontname):
                self._font = pygame.font.Font(self.fontname, int(self.heightPix), italic=self.italic, bold=self.bold)
            else:
                try:
                    self._font = pygame.font.SysFont(self.fontname, int(self.heightPix), italic=self.italic, bold=self.bold)
                    self.fontname = font
                    log.info('using sysFont ' + str(font))
                except:
                    self.fontname = pygame.font.get_default_font()
                    log.error("Couldn't find font %s on the system. Using %s instead!\n \
                              Font names should be written as concatenated names all in lower case.\n \
                              e.g. 'arial', 'monotypecorsiva', 'rockwellextra'..." %(font, self.fontname))
                    self._font = pygame.font.SysFont(self.fontname, int(self.heightPix), italic=self.italic, bold=self.bold)
        self.needUpdate = True

    def setText(self,value):
        """Set the text to be rendered using the current font
        """
        if self._useShaders:
            self._setTextShaders(value)
        else:
            self._setTextNoShaders(value)
    def setRGB(self,value, operation=None):
        self._set('rgb', value, operation)
        self.setText(self.text)#need to render the text again to a texture
    
    def _setTextShaders(self,value):
        """Set the text to be rendered using the current font
        """
        self.text = value
        
        if self.win.winType=="pyglet":
            self._pygletTextObj = pyglet.font.Text(self._font, self.text,
                                                       halign=self.alignHoriz, valign=self.alignVert,
                                                       color = (self.rgb[0],self.rgb[1], self.rgb[2], self.opacity),
                                                       width=self._wrapWidthPIX)#width of the frame     
#            self._pygletTextObj = pyglet.text.Label(self.text,self.fontname, int(self.heightPix),
#                                                       anchor_x=self.alignHoriz, anchor_y=self.alignVert,#the point we rotate around
#                                                       halign=self.alignHoriz,
#                                                       color = (int(127.5*self.rgb[0]+127.5),
#                                                            int(127.5*self.rgb[1]+127.5),
#                                                            int(127.5*self.rgb[2]+127.5),
#                                                            int(255*self.opacity)),
#                                                       multiline=True, width=self._wrapWidthPIX)#width of the frame           
            self.width, self.height = self._pygletTextObj.width, self._pygletTextObj.height
        else:   
            self._surf = self._font.render(value, self.antialias, [255,255,255])
            self.width, self.height = self._surf.get_size()
            if self.antialias: smoothing = GL.GL_LINEAR
            else: smoothing = GL.GL_NEAREST
            #generate the textures from pygame surface
            GL.glEnable(GL.GL_TEXTURE_2D)
            GL.glBindTexture(GL.GL_TEXTURE_2D, self._texID)#bind that name to the target            
            GL.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL.GL_RGBA,
                            self.width,self.height,0,
                            GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, pygame.image.tostring( self._surf, "RGBA",1))
            GL.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_MAG_FILTER,smoothing)    #linear smoothing if texture is stretched?
            GL.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_MIN_FILTER,smoothing)    #but nearest pixel value if it's compressed?


        self.needUpdate = True

    def _updateListShaders(self):
        """
        The user shouldn't need this method since it gets called
        after every call to .set() Basically it updates the OpenGL
        representation of your stimulus if some parameter of the
        stimulus changes. Call it if you change a property manually
        rather than using the .set() command
        """
        GL.glNewList(self._listID, GL.GL_COMPILE)
        GL.glPushMatrix()

        #setup the shaderprogram
        #no need to do texture maths so no need for programs?
        GL.glUseProgram(0)#self.win._progSignedTex)
        #GL.glUniform1i(GL.glGetUniformLocation(self.win._progSignedTex, "texture"), 0) #set the texture to be texture unit 0
        
        #coords:
        if self.alignHoriz in ['center', 'centre']: left = -self.width/2.0;    right = self.width/2.0
        elif self.alignHoriz =='right':    left = -self.width;    right = 0.0
        else: left = 0.0; right = self.width
        #how much to move bottom
        if self.alignVert in ['center', 'centre']: bottom=-self.height/2.0; top=self.height/2.0
        elif self.alignVert =='top': bottom=-self.height; top=0
        else: bottom=0.0; top=self.height
        Btex, Ttex, Ltex, Rtex = -0.01, 0.98, 0,1.0#there seems to be a rounding err in pygame font textures
        
        #unbind the mask texture regardless
        GL.glActiveTexture(GL.GL_TEXTURE1)
        GL.glEnable(GL.GL_TEXTURE_2D)
        GL.glBindTexture(GL.GL_TEXTURE_2D, 0)
        if self.win.winType=="pyglet":
            #unbind the main texture
            GL.glActiveTexture(GL.GL_TEXTURE0)
#            GL_multitexture.glActiveTextureARB(GL_multitexture.GL_TEXTURE0_ARB)
            GL.glBindTexture(GL.GL_TEXTURE_2D, 0) #the texture is specified by pyglet.font.GlyphString.draw()
            GL.glEnable(GL.GL_TEXTURE_2D)
        else:
            #bind the appropriate main texture
            GL.glActiveTexture(GL.GL_TEXTURE0)
            GL.glBindTexture(GL.GL_TEXTURE_2D, self._texID)            
            GL.glEnable(GL.GL_TEXTURE_2D)
            
        if self.win.winType=="pyglet":
            GL.glActiveTexture(GL.GL_TEXTURE0)
            GL.glEnable(GL.GL_TEXTURE_2D)
            self._pygletTextObj.draw()
        else:
            GL.glBegin(GL.GL_QUADS)                  # draw a 4 sided polygon
            # right bottom
            GL.glMultiTexCoord2f(GL.GL_TEXTURE0,Rtex, Btex)
            GL.glVertex3f(right,bottom,0)
            # left bottom
            GL.glMultiTexCoord2f(GL.GL_TEXTURE0,Ltex,Btex)
            GL.glVertex3f(left,bottom,0)
            # left top
            GL.glMultiTexCoord2f(GL.GL_TEXTURE0,Ltex,Ttex)
            GL.glVertex3f(left,top,0)
            # right top
            GL.glMultiTexCoord2f(GL.GL_TEXTURE0,Rtex,Ttex)
            GL.glVertex3f(right,top,0)
            GL.glEnd()

        GL.glDisable(GL.GL_TEXTURE_2D)
        GL.glUseProgram(0)
        GL.glPopMatrix()

        GL.glEndList()
        self.needUpdate=0

    def _setTextNoShaders(self,value,operation=None):
        """Set the text to be rendered using the current font
        """
        self.text = value
        
        if self.win.winType=="pyglet":
            self._pygletTextObj = pyglet.font.Text(self._font, self.text,
                                                       halign=self.alignHoriz, valign=self.alignVert,
                                                       color = (self.rgb[0],self.rgb[1], self.rgb[2], self.opacity)
                                                       )
            self.width, self.height = self._pygletTextObj.width, self._pygletTextObj.height
        else:   
            self._surf = self._font.render(value, self.antialias,
                                           [self.rgb[0]*127.5+127.5,
                                            self.rgb[1]*127.5+127.5,
                                            self.rgb[2]*127.5+127.5])
            self.width, self.height = self._surf.get_size()
            if self.antialias: smoothing = GL.GL_LINEAR
            else: smoothing = GL.GL_NEAREST
            #generate the textures from pygame surface
            GL.glEnable(GL.GL_TEXTURE_2D)
            GL.glBindTexture(GL.GL_TEXTURE_2D, self._texID)#bind that name to the target            
            GL.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL.GL_RGBA,
                            self.width,self.height,0,
                            GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, pygame.image.tostring( self._surf, "RGBA",1))
            GL.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_MAG_FILTER,smoothing)    #linear smoothing if texture is stretched?
            GL.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_MIN_FILTER,smoothing)    #but nearest pixel value if it's compressed?

        self.needUpdate = True

    def _updateListNoShaders(self):
        """
        The user shouldn't need this method since it gets called
        after every call to .set() Basically it updates the OpenGL
        representation of your stimulus if some parameter of the
        stimulus changes. Call it if you change a property manually
        rather than using the .set() command
        """
        GL.glNewList(self._listID, GL.GL_COMPILE)
        GL.glPushMatrix()

        #coords:
        if self.alignHoriz in ['center', 'centre']: left = -self.width/2.0;    right = self.width/2.0
        elif self.alignHoriz =='right':    left = -self.width;    right = 0.0
        else: left = 0.0; right = self.width
        #how much to move bottom
        if self.alignVert in ['center', 'centre']: bottom=-self.height/2.0; top=self.height/2.0
        elif self.alignVert =='top': bottom=-self.height; top=0
        else: bottom=0.0; top=self.height
        Btex, Ttex, Ltex, Rtex = -0.01, 0.98, 0,1.0#there seems to be a rounding err in pygame font textures

        if self.win.winType=="pyglet":
            #unbind the mask texture 
            GL.glActiveTexture(GL.GL_TEXTURE1)
            GL.glEnable(GL.GL_TEXTURE_2D)
            GL.glBindTexture(GL.GL_TEXTURE_2D, 0)
            #unbind the main texture
            GL.glActiveTexture(GL.GL_TEXTURE0)
            GL.glEnable(GL.GL_TEXTURE_2D)
            
        else:
            #bind the appropriate main texture
            GL_multitexture.glActiveTextureARB(GL_multitexture.GL_TEXTURE0_ARB)
            GL.glEnable(GL.GL_TEXTURE_2D)
            GL.glBindTexture(GL.GL_TEXTURE_2D, self._texID)
            #unbind the mask texture regardless
            GL_multitexture.glActiveTextureARB(GL_multitexture.GL_TEXTURE1_ARB)
            GL.glEnable(GL.GL_TEXTURE_2D)
            GL.glBindTexture(GL.GL_TEXTURE_2D, 0)
            
        if self.win.winType=="pyglet":
            self._pygletTextObj.draw()
        else:                
            GL.glBegin(GL.GL_QUADS)                  # draw a 4 sided polygon
            # right bottom
            GL_multitexture.glMultiTexCoord2fARB(GL_multitexture.GL_TEXTURE0_ARB,Rtex, Btex)
            GL.glVertex3f(right,bottom,0)
            # left bottom
            GL_multitexture.glMultiTexCoord2fARB(GL_multitexture.GL_TEXTURE0_ARB,Ltex,Btex)
            GL.glVertex3f(left,bottom,0)
            # left top
            GL_multitexture.glMultiTexCoord2fARB(GL_multitexture.GL_TEXTURE0_ARB,Ltex,Ttex)
            GL.glVertex3f(left,top,0)
            # right top
            GL_multitexture.glMultiTexCoord2fARB(GL_multitexture.GL_TEXTURE0_ARB,Rtex,Ttex)
            GL.glVertex3f(right,top,0)
            GL.glEnd()

        GL.glDisable(GL.GL_TEXTURE_2D)

        GL.glPopMatrix()

        GL.glEndList()
        self.needUpdate=0

    def draw(self, win=None):
        """
        Draw the stimulus in its relevant window. You must call
        this method after every MyWin.flip() if you want the
        stimulus to appear on that frame and then update the screen
        again.
        
        If win is specified then override the normal window of this stimulus.
        """
        #set the window to draw to
        if win==None: win=self.win
        if win.winType=='pyglet': win.winHandle.switch_to()
        
        #work out next default depth
        if self.depth==0:
            thisDepth = self.win._defDepth
            self.win._defDepth += _depthIncrements[self.win.winType]
        else:
            thisDepth=self.depth

        GL.glPushMatrix()

        #scale and rotate
        unitScale = self.win.setScale(self.units)
        GL.glTranslatef(self.pos[0],self.pos[1],thisDepth)#NB depth is set already
        GL.glRotatef(self.ori,0.0,0.0,1.0)
        #then scale back to pixels
        self.win.setScale('pix', None, unitScale)

        if self._useShaders: #then rgb needs to be set as glColor
            #setup color
            desiredRGB = (self.rgb*self.contrast+1)/2.0#RGB in range 0:1 and scaled for contrast
            if numpy.any(desiredRGB**2.0>1.0):
                desiredRGB=[0.6,0.6,0.4]
            GL.glColor4f(desiredRGB[0],desiredRGB[1],desiredRGB[2], self.opacity)
        else: #color is set in texture, so set glColor to white
            GL.glColor4f(1,1,1,1)

        GL.glDisable(GL.GL_DEPTH_TEST) #should text have a depth or just on top?
        #update list if necss and then call it
        if self.win.winType=='pyglet':
            
            #and align based on x anchor
            if self.alignHoriz=='right':
                GL.glTranslatef(-self.width,0,0)#NB depth is set already
            if self.alignHoriz in ['center', 'centre']:
                GL.glTranslatef(-self.width/2,0,0)#NB depth is set already
                
            #unbind the mask texture regardless
            GL.glActiveTexture(GL.GL_TEXTURE1)
            GL.glEnable(GL.GL_TEXTURE_2D)
            GL.glBindTexture(GL.GL_TEXTURE_2D, 0)
            #unbind the main texture
            GL.glActiveTexture(GL.GL_TEXTURE0)
            GL.glEnable(GL.GL_TEXTURE_2D)
            #then allow pyglet to bind and use texture during drawing
            
            self._pygletTextObj.draw()            
            GL.glDisable(GL.GL_TEXTURE_2D) 
        else: 
            #for pygame we should (and can) use a drawing list   
            if self.needUpdate: self._updateList()
            GL.glCallList(self._listID)
        GL.glEnable(GL.GL_DEPTH_TEST)                   # Enables Depth Testing
        GL.glPopMatrix()
    def setUseShaders(self, val=True):
        """Set this stimulus to use shaders if possible.
        """
        if val==True and self.win._haveShaders==False:
            logging.warn("Shaders were requested for PatchStim but aren;t available. Shaders need OpenGL 2.0+ drivers")
        if val!=self._useShaders:
            self._useShaders=val
            self.setText(self.text)  
            self.needUpdate=True
            
def makeRadialMatrix(matrixSize):
    """Generate a square matrix where each element val is
    its distance from the centre of the matrix
    """
    oneStep = 2.0/(matrixSize-1)
    xx,yy = numpy.mgrid[0:2+oneStep:oneStep, 0:2+oneStep:oneStep] -1.0 #NB need to add one step length because
    rad = numpy.sqrt(xx**2 + yy**2)
    return rad

def createTexture(tex, id, pixFormat, stim, res=128):
    """
    id is the texture ID
    pixFormat = GL.GL_ALPHA, GL.GL_RGB
    useShaders is a bool
    interpolate is a bool (determines whether texture will use GL_LINEAR or GL_NEAREST
    res is the resolution of the texture (unless a bitmap image is used)
    """
    
    """
    Create an intensity texture, ranging -1:1.0
    """
    useShaders = stim._useShaders
    interpolate = stim.interpolate
    if type(tex) == numpy.ndarray:
        #handle a numpy array
        #for now this needs to be an NxN intensity array        
        intensity = tex.astype(numpy.float32)
        if intensity.max()>1 or intensity.min()<-1:
            log.error('numpy arrays used as textures should be in the range -1(black):1(white)')
        wasLum = True
        ##is it 1D?
        if tex.shape[0]==1:
            stim._tex1D=True
            res=im.shape[1]
        elif len(tex.shape)==1 or tex.shape[1]==1:
            stim._tex1D=True
            res=tex.shape[0]
        else:
            stim._tex1D=False
            #check if it's a square power of two
            maxDim = max(tex.shape)
            powerOf2 = 2**numpy.ceil(numpy.log2(maxDim))
            if tex.shape[0]!=powerOf2 or tex.shape[1]!=powerOf2:
                log.error("Numpy array textures must be square and must be power of two (e.g. 16x16, 256x256)")      
                core.quit()
            res=tex.shape[0]
    elif tex in [None,"none", "None"]:
        res=1 #4x4 (2x2 is SUPPOSED to be fine but generates wierd colours!)
        intensity = numpy.ones([res,res],numpy.float32)
        wasLum = True
    elif tex == "sin":
        onePeriodX, onePeriodY = numpy.mgrid[0:2*pi:2*pi/res, 0:2*pi:2*pi/res]
        intensity = numpy.sin(onePeriodY-pi/2)
        wasLum = True
    elif tex == "sqr":#square wave (symmetric duty cycle)
        onePeriodX, onePeriodY = numpy.mgrid[0:2*pi:2*pi/res, 0:2*pi:2*pi/res]
        sinusoid = numpy.sin(onePeriodY-pi/2)
        intensity = numpy.where(sinusoid>0, 1, -1)
        wasLum = True
    elif tex == "sinXsin":
        onePeriodX, onePeriodY = numpy.mgrid[0:2*pi:2*pi/res, 0:2*pi:2*pi/res]
        intensity = numpy.sin(onePeriodX-pi/2)*numpy.sin(onePeriodY-pi/2)
        wasLum = True
    elif tex == "sqrXsqr":
        onePeriodX, onePeriodY = numpy.mgrid[0:2*pi:2*pi/res, 0:2*pi:2*pi/res]
        sinusoid = numpy.sin(onePeriodX-pi/2)*numpy.sin(onePeriodY-pi/2)
        intensity = numpy.where(sinusoid>0, 1, -1)
        wasLum = True
    elif tex is "circle":
        rad=makeRadialMatrix(res)
        intensity = (rad<=1)*2-1 
        fromFile=0
    elif tex is "gauss":
        rad=makeRadialMatrix(res)
        sigma = 1/3.0;
        intensity = numpy.exp( -rad**2.0 / (2.0*sigma**2.0) )*2-1 #3sd.s by the edge of the stimulus
        fromFile=0
    elif tex is "radRamp":#a radial ramp
        rad=makeRadialMatrix(res)
        intensity = 1-2*rad
        intensity = numpy.where(rad<-1, intensity, -1)#clip off the corners (circular)
        fromFile=0
    else:#might be a filename of an image
        try:
            im = Image.open(tex)
            im = im.transpose(Image.FLIP_TOP_BOTTOM)
        except:
            log.error("couldn't load tex...%s" %(tex))
            core.quit()
            raise #so that we quit

        #is it 1D?
        if im.size[0]==1 or im.size[1]==1:
            log.error("Only 2D textures are supported at the moment")
        else:
            maxDim = max(im.size)
            powerOf2 = int(2**numpy.ceil(numpy.log2(maxDim)))
            if im.size[0]!=powerOf2 or im.size[1]!=powerOf2:
                log.warning("Image '%s' was not a square power-of-two image. Linearly interpolating to be %ix%i" %(tex, powerOf2, powerOf2))
                im=im.resize([powerOf2,powerOf2],Image.BILINEAR)      
                
        #is it Luminance or RGB?
        if im.mode=='L':
            wasLum = True
            intensity= numpy.array(im).astype(numpy.float32)*2/255-1.0 #get to range -1:1
        elif pixFormat==GL.GL_ALPHA:#we have RGB and need Lum
            wasLum = True
            im = im.convert("L")#force to intensity (in case it was rgb)
            intensity= numpy.array(im).astype(numpy.float32)*2/255-1.0 #get to range -1:1            
        elif pixFormat==GL.GL_RGB:#we have RGB and keep it that way
            #texture = im.tostring("raw", "RGB", 0, -1)
            im = im.convert("RGBA")#force to rgb (in case it was CMYK or L)
            intensity = numpy.array(im).astype(numpy.float32)*2/255-1
            wasLum=False
            
    if pixFormat==GL.GL_RGB and wasLum and useShaders:
        #keep as float32 -1:1
        internalFormat = GL.GL_RGB32F_ARB
        dataType = GL.GL_FLOAT
        data = numpy.ones((intensity.shape[0],intensity.shape[1],3),numpy.float32)#initialise data array as a float
        data[:,:,0] = intensity#R
        data[:,:,1] = intensity#G
        data[:,:,2] = intensity#B
    elif pixFormat==GL.GL_RGB and wasLum:
        #scale by rgb and convert to ubyte
        internalFormat = GL.GL_RGB
        dataType = GL.GL_UNSIGNED_BYTE
        data = numpy.ones((intensity.shape[0],intensity.shape[1],3),numpy.float32)#initialise data array as a float
        data[:,:,0] = intensity*stim.rgb[0]  + stim.rgbPedestal[0]#R
        data[:,:,1] = intensity*stim.rgb[1]  + stim.rgbPedestal[1]#G
        data[:,:,2] = intensity*stim.rgb[2]  + stim.rgbPedestal[2]#B
        #convert to ubyte
        data = psychopy.misc.float_uint8(stim.contrast*data)
    elif pixFormat==GL.GL_RGB and useShaders:
        internalFormat = GL.GL_RGB32F_ARB
        dataType = GL.GL_FLOAT
        data = intensity
    elif pixFormat==GL.GL_RGB:
        internalFormat = GL.GL_RGB
        dataType = GL.GL_UNSIGNED_BYTE
        data = psychopy.misc.float_uint8(intensity)
    elif pixFormat==GL.GL_ALPHA and useShaders:
        internalFormat = GL.GL_ALPHA
        dataType = GL.GL_UNSIGNED_BYTE
        data = psychopy.misc.float_uint8(intensity)    
    elif pixFormat==GL.GL_ALPHA:
        internalFormat = GL.GL_ALPHA
        dataType = GL.GL_UNSIGNED_BYTE
        #can't use float_uint8 - do it manually
        data = numpy.around(255*stim.opacity*(0.5+0.5*intensity)).astype(numpy.uint8)
    #check for RGBA textures
    if len(intensity.shape)>2 and intensity.shape[2] == 4:
        if pixFormat==GL.GL_RGB: pixFormat=GL.GL_RGBA
        if internalFormat==GL.GL_RGB: internalFormat=GL.GL_RGBA
        elif internalFormat==GL.GL_RGB32F_ARB: internalFormat=GL.GL_RGBA32F_ARB

    texture = data.tostring()#serialise

    if interpolate: smoothing=GL.GL_LINEAR
    else: smoothing=GL.GL_NEAREST

    #bind the texture in openGL
    GL.glEnable(GL.GL_TEXTURE_2D)
    GL.glBindTexture(GL.GL_TEXTURE_2D, id)#bind that name to the target
    GL.glTexImage2D(GL.GL_TEXTURE_2D, 0, internalFormat,
                    data.shape[0],data.shape[1], 0,
                    pixFormat, dataType, texture)
    GL.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_WRAP_S,GL.GL_REPEAT) #makes the texture map wrap (this is actually default anyway)
    #important if using bits++ because GL_LINEAR
    #sometimes extrapolates to pixel vals outside range
    GL.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_MAG_FILTER,smoothing)    #linear smoothing if texture is stretched?
    GL.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_MIN_FILTER,smoothing)    #but nearest pixel value if it's compressed?
    GL.glTexEnvi(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_MODULATE)#?? do we need this - think not!
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.