VisionEgg.Gratings

Package VisionEgg :: Module Gratings |
[frames] | no frames]

# Source Code for Module VisionEgg.Gratings

      1  # The Vision Egg: Gratings
      2  #
      3  # Copyright (C) 2001-2003 Andrew Straw.
      4  # Copyright (C) 2005,2008 California Institute of Technology
      5  #
      6  # URL: <http://www.visionegg.org/>
      7  #
      8  # Distributed under the terms of the GNU Lesser General Public License
      9  # (LGPL). See LICENSE.TXT that came with this file.
     10
     11  """
     12  Grating stimuli.
     13
     14  """
     15
     16  ####################################################################
     17  #
     18  #        Import all the necessary packages
     19  #
     20  ####################################################################
     21
     22  import logging                              # available in Python 2.3
     23
     24  import VisionEgg
     25  import VisionEgg.Core
     26  import VisionEgg.Textures
     27  import VisionEgg.ParameterTypes as ve_types
     28  import numpy
     29  import math, types, string
     30  import VisionEgg.GL as gl # get all OpenGL stuff in one namespace
     31  import _vegl
     32
     33 -def _get_type_info( bitdepth ):
     34      """Private helper function to calculate type info based on bit depth"""
     35      if bitdepth == 8:
     36          gl_type = gl.GL_UNSIGNED_BYTE
     37          numpy_dtype = numpy.uint8
     38          max_int_val = float((2**8)-1)
     39      elif bitdepth == 12:
     40          gl_type = gl.GL_SHORT
     41          numpy_dtype = numpy.int16
     42          max_int_val = float((2**15)-1)
     43      elif bitdepth == 16:
     44          gl_type = gl.GL_INT
     45          numpy_dtype = numpy.int32
     46          max_int_val = float((2.**31.)-1) # do as float to avoid overflow
     47      else:
     48          raise ValueError("supported bitdepths are 8, 12, and 16.")
     49      return gl_type, numpy_dtype, max_int_val
     50
     51 -class LuminanceGratingCommon(VisionEgg.Core.Stimulus):
     52      """Base class with common code to all ways of drawing luminance gratings.
     53
     54      Parameters
     55      ==========
     56      bit_depth -- precision with which grating is calculated and sent to OpenGL (UnsignedInteger)
     57                   Default: 8
     58      """
     59
     60      parameters_and_defaults = VisionEgg.ParameterDefinition({
     61          'bit_depth':(8,
     62                       ve_types.UnsignedInteger,
     63                       'precision with which grating is calculated and sent to OpenGL'),
     64          })
     65
     66      __slots__ = (
     67          'gl_internal_format',
     68          'format',
     69          'gl_type',
     70          'numpy_dtype',
     71          'max_int_val',
     72          'cached_bit_depth',
     73          )
     74
     75 -    def calculate_bit_depth_dependencies(self):
     76          """Calculate a number of parameters dependent on bit depth."""
     77          bit_depth_warning = False
     78          p = self.parameters # shorthand
     79
     80          red_bits = gl.glGetIntegerv( gl.GL_RED_BITS )
     81          green_bits = gl.glGetIntegerv( gl.GL_GREEN_BITS )
     82          blue_bits = gl.glGetIntegerv( gl.GL_BLUE_BITS )
     83          min_bits = min( (red_bits,green_bits,blue_bits) )
     84          if min_bits < p.bit_depth:
     85              logger = logging.getLogger('VisionEgg.Gratings')
     86              logger.warning("Requested bit depth of %d in "
     87                             "LuminanceGratingCommon, which is "
     88                             "greater than your current OpenGL context "
     89                             "supports (%d)."% (p.bit_depth,min_bits))
     90          self.gl_internal_format = gl.GL_LUMINANCE
     91          self.format = gl.GL_LUMINANCE
     92          self.gl_type, self.numpy_dtype, self.max_int_val = _get_type_info( p.bit_depth )
     93          self.cached_bit_depth = p.bit_depth
     94
     95 -class AlphaGratingCommon(VisionEgg.Core.Stimulus):
     96      """Base class with common code to all ways of drawing gratings in alpha.
     97
     98      This class is currently not used by any other classes.
     99
    100      Parameters
    101      ==========
    102      bit_depth -- precision with which grating is calculated and sent to OpenGL (UnsignedInteger)
    103                   Default: 8
    104      """
    105
    106      parameters_and_defaults = VisionEgg.ParameterDefinition({
    107          'bit_depth':(8,
    108                       ve_types.UnsignedInteger,
    109                       'precision with which grating is calculated and sent to OpenGL'),
    110          })
    111
    112      __slots__ = (
    113          'gl_internal_format',
    114          'format',
    115          'gl_type',
    116          'numpy_dtype',
    117          'max_int_val',
    118          'cached_bit_depth',
    119          )
    120
    121 -    def calculate_bit_depth_dependencies(self):
    122          """Calculate a number of parameters dependent on bit depth."""
    123          p = self.parameters # shorthand
    124          alpha_bit_depth = gl.glGetIntegerv( gl.GL_ALPHA_BITS )
    125          if alpha_bit_depth < p.bit_depth:
    126              logger = logging.getLogger('VisionEgg.Gratings')
    127              logger.warning("Requested bit depth of %d, which is "
    128                             "greater than your current OpenGL context "
    129                             "supports (%d)."% (p.bit_depth,min_bits))
    130          self.gl_internal_format = gl.GL_ALPHA
    131          self.format = gl.GL_ALPHA
    132          self.gl_type, self.numpy_dtype, self.max_int_val = _get_type_info( p.bit_depth )
    133          self.cached_bit_depth = p.bit_depth
    134
    135 -class SinGrating2D(LuminanceGratingCommon):
    136      """Sine wave grating stimulus
    137
    138      This is a general-purpose, realtime sine-wave luminace grating
    139      generator. To acheive an arbitrary orientation, this class rotates
    140      a textured quad.  To draw a grating with sides that always remain
    141      horizontal and vertical, draw a large grating in a small viewport.
    142      (The viewport will clip anything beyond its edges.)
    143
    144      Parameters
    145      ==========
    146      anchor                      -- specifies how position parameter is interpreted (String)
    147                                     Default: center
    148      bit_depth                   -- precision with which grating is calculated and sent to OpenGL (UnsignedInteger)
    149                                     Inherited from LuminanceGratingCommon
    150                                     Default: 8
    151      color1                      -- (AnyOf(Sequence3 of Real or Sequence4 of Real))
    152                                     Default: (1.0, 1.0, 1.0)
    153      color2                      -- optional color with which to perform interpolation with color1 in RGB space (AnyOf(Sequence3 of Real or Sequence4 of Real))
    154                                     Default: (determined at runtime)
    155      contrast                    -- (Real)
    156                                     Default: 1.0
    157      depth                       -- (Real)
    158                                     Default: (determined at runtime)
    159      ignore_time                 -- (Boolean)
    160                                     Default: False
    161      mask                        -- optional masking function (Instance of <class 'VisionEgg.Textures.Mask2D'>)
    162                                     Default: (determined at runtime)
    163      max_alpha                   -- (Real)
    164                                     Default: 1.0
    165      num_samples                 -- (UnsignedInteger)
    166                                     Default: 512
    167      on                          -- draw stimulus? (Boolean)
    168                                     Default: True
    169      orientation                 -- (Real)
    170                                     Default: 0.0
    171      pedestal                    -- (Real)
    172                                     Default: 0.5
    173      phase_at_t0                 -- (Real)
    174                                     Default: 0.0
    175      position                    -- (units: eye coordinates) (Sequence2 of Real)
    176                                     Default: (320.0, 240.0)
    177      recalculate_phase_tolerance -- (Real)
    178                                     Default: (determined at runtime)
    179      size                        -- defines coordinate size of grating (in eye coordinates) (Sequence2 of Real)
    180                                     Default: (640.0, 480.0)
    181      spatial_freq                -- frequency defined relative to coordinates defined in size parameter (units: cycles/eye_coord_unit) (Real)
    182                                     Default: 0.0078125
    183      t0_time_sec_absolute        -- (Real)
    184                                     Default: (determined at runtime)
    185      temporal_freq_hz            -- (Real)
    186                                     Default: 5.0
    187      """
    188
    189      parameters_and_defaults = VisionEgg.ParameterDefinition({
    190          'on':(True,
    191                ve_types.Boolean,
    192                "draw stimulus?"),
    193          'mask':(None, # allows window onto otherwise (tilted) rectangular grating
    194                  ve_types.Instance(VisionEgg.Textures.Mask2D),
    195                  "optional masking function"),
    196          'contrast':(1.0,
    197                      ve_types.Real),
    198          'pedestal':(0.5,
    199                      ve_types.Real),
    200          'position':((320.0,240.0), # in eye coordinates
    201                      ve_types.Sequence2(ve_types.Real),
    202                      "(units: eye coordinates)"),
    203          'anchor':('center',
    204                    ve_types.String,
    205                    "specifies how position parameter is interpreted"),
    206          'depth':(None, # if not None, turns on depth testing and allows for occlusion
    207                   ve_types.Real),
    208          'size':((640.0,480.0),
    209                  ve_types.Sequence2(ve_types.Real),
    210                  "defines coordinate size of grating (in eye coordinates)",
    211                  ),
    212          'spatial_freq':(1.0/128.0, # cycles/eye coord units
    213                          ve_types.Real,
    214                          "frequency defined relative to coordinates defined in size parameter (units: cycles/eye_coord_unit)",
    215                          ),
    216          'temporal_freq_hz':(5.0, # hz
    217                              ve_types.Real),
    218          't0_time_sec_absolute':(None, # Will be assigned during first call to draw()
    219                                  ve_types.Real),
    220          'ignore_time':(False, # ignore temporal frequency variable - allow control purely with phase_at_t0
    221                         ve_types.Boolean),
    222          'phase_at_t0':(0.0, # degrees [0.0-360.0]
    223                         ve_types.Real),
    224          'orientation':(0.0, # 0=right, 90=up
    225                         ve_types.Real),
    226          'num_samples':(512, # number of spatial samples, should be a power of 2
    227                         ve_types.UnsignedInteger),
    228          'max_alpha':(1.0, # controls "opacity": 1.0 = completely opaque, 0.0 = completely transparent
    229                       ve_types.Real),
    230          'color1':((1.0, 1.0, 1.0), # alpha is ignored (if given) -- use max_alpha parameter
    231                    ve_types.AnyOf(ve_types.Sequence3(ve_types.Real),
    232                                   ve_types.Sequence4(ve_types.Real))),
    233          'color2':(None, # perform interpolation with color1 in RGB space.
    234                    ve_types.AnyOf(ve_types.Sequence3(ve_types.Real),
    235                                   ve_types.Sequence4(ve_types.Real)),
    236                    "optional color with which to perform interpolation with color1 in RGB space"),
    237          'recalculate_phase_tolerance':(None, # only recalculate texture when phase is changed by more than this amount, None for always recalculate. (Saves time.)
    238                                         ve_types.Real),
    239          })
    240
    241      __slots__ = (
    242          '_texture_object_id',
    243          '_last_phase',
    244          )
    245
    246 -    def __init__(self,**kw):
    247          LuminanceGratingCommon.__init__(self,**kw)
    248
    249          p = self.parameters # shorthand
    250
    251          self._texture_object_id = gl.glGenTextures(1)
    252          if p.mask:
    253              gl.glActiveTextureARB(gl.GL_TEXTURE0_ARB)
    254          gl.glBindTexture(gl.GL_TEXTURE_1D,self._texture_object_id)
    255
    256          # Do error-checking on texture to make sure it will load
    257          max_dim = gl.glGetIntegerv(gl.GL_MAX_TEXTURE_SIZE)
    258          if p.num_samples > max_dim:
    259              raise NumSamplesTooLargeError("Grating num_samples too large for video system.\nOpenGL reports maximum size of %d"%(max_dim,))
    260
    261          self.calculate_bit_depth_dependencies()
    262
    263          w = p.size[0]
    264          inc = w/float(p.num_samples)
    265          phase = 0.0 # this data won't get used - don't care about phase
    266          self._last_phase = phase
    267          floating_point_sin = numpy.sin(2.0*math.pi*p.spatial_freq*numpy.arange(0.0,w,inc,dtype=numpy.float)+(phase/180.0*math.pi))*0.5*p.contrast+p.pedestal
    268          floating_point_sin = numpy.clip(floating_point_sin,0.0,1.0) # allow square wave generation if contrast > 1
    269          texel_data = (floating_point_sin*self.max_int_val).astype(self.numpy_dtype).tostring()
    270
    271          # Because the MAX_TEXTURE_SIZE method is insensitive to the current
    272          # state of the video system, another check must be done using
    273          # "proxy textures".
    274          gl.glTexImage1D(gl.GL_PROXY_TEXTURE_1D,            # target
    275                          0,                                 # level
    276                          self.gl_internal_format,           # video RAM internal format
    277                          p.num_samples,                     # width
    278                          0,                                 # border
    279                          self.format,                       # format of texel data
    280                          self.gl_type,                      # type of texel data
    281                          texel_data)                        # texel data (irrelevant for proxy)
    282          if gl.glGetTexLevelParameteriv(gl.GL_PROXY_TEXTURE_1D, # Need PyOpenGL >= 2.0
    283                                         0,
    284                                         gl.GL_TEXTURE_WIDTH) == 0:
    285              raise NumSamplesTooLargeError("Grating num_samples is too wide for your video system!")
    286
    287          # If we got here, it worked and we can load the texture for real.
    288          gl.glTexImage1D(gl.GL_TEXTURE_1D,                  # target
    289                          0,                                 # level
    290                          self.gl_internal_format,           # video RAM internal format
    291                          p.num_samples,                     # width
    292                          0,                                 # border
    293                          self.format,                       # format of texel data
    294                          self.gl_type,                      # type of texel data
    295                          texel_data)                        # texel data
    296
    297          # Set texture object defaults
    298          gl.glTexParameteri(gl.GL_TEXTURE_1D,gl.GL_TEXTURE_WRAP_S,gl.GL_CLAMP_TO_EDGE)
    299          gl.glTexParameteri(gl.GL_TEXTURE_1D,gl.GL_TEXTURE_WRAP_T,gl.GL_CLAMP_TO_EDGE)
    300          gl.glTexParameteri(gl.GL_TEXTURE_1D,gl.GL_TEXTURE_MAG_FILTER,gl.GL_LINEAR)
    301          gl.glTexParameteri(gl.GL_TEXTURE_1D,gl.GL_TEXTURE_MIN_FILTER,gl.GL_LINEAR)
    302
    303          if p.color2 is not None:
    304              if VisionEgg.Core.gl_renderer == 'ATi Rage 128 Pro OpenGL Engine' and VisionEgg.Core.gl_version == '1.1 ATI-1.2.22':
    305                  logger = logging.getLogger('VisionEgg.Gratings')
    306                  logger.warning("Your video card and driver have known "
    307                                 "bugs which prevent them from rendering "
    308                                 "color gratings properly.")
    309
    310 -    def __del__(self):
    311          gl.glDeleteTextures( [self._texture_object_id] )
    312
    313 -    def draw(self):
    314          p = self.parameters # shorthand
    315          if p.on:
    316              # calculate center
    317              center = VisionEgg._get_center(p.position,p.anchor,p.size)
    318              if p.mask:
    319                  gl.glActiveTextureARB(gl.GL_TEXTURE0_ARB)
    320              gl.glBindTexture(gl.GL_TEXTURE_1D,self._texture_object_id)
    321
    322              gl.glEnable(gl.GL_TEXTURE_1D)
    323              gl.glDisable(gl.GL_TEXTURE_2D)
    324              if p.bit_depth != self.cached_bit_depth:
    325                  self.calculate_bit_depth_dependencies()
    326
    327              # Clear the modeview matrix
    328              gl.glMatrixMode(gl.GL_MODELVIEW)
    329              gl.glPushMatrix()
    330
    331              # Rotate about the center of the texture
    332              gl.glTranslate(center[0],
    333                             center[1],
    334                             0)
    335              gl.glRotate(p.orientation,0,0,1)
    336
    337              if p.depth is None:
    338                  gl.glDisable(gl.GL_DEPTH_TEST)
    339                  depth = 0.0
    340              else:
    341                  gl.glEnable(gl.GL_DEPTH_TEST)
    342                  depth = p.depth
    343
    344              # allow max_alpha value to control blending
    345              gl.glEnable( gl.GL_BLEND )
    346              gl.glBlendFunc( gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA )
    347
    348              if p.color2:
    349                  gl.glTexEnvi(gl.GL_TEXTURE_ENV, gl.GL_TEXTURE_ENV_MODE, gl.GL_BLEND)
    350                  gl.glTexEnvfv(gl.GL_TEXTURE_ENV, gl.GL_TEXTURE_ENV_COLOR, p.color2)
    351                  ## alpha is ignored because the texture base internal format is luminance
    352              else:
    353                  gl.glTexEnvi(gl.GL_TEXTURE_ENV, gl.GL_TEXTURE_ENV_MODE, gl.GL_MODULATE)
    354
    355              if p.t0_time_sec_absolute is None and not p.ignore_time:
    356                  p.t0_time_sec_absolute = VisionEgg.time_func()
    357
    358              w = p.size[0]
    359              inc = w/float(p.num_samples)
    360              if p.ignore_time:
    361                  phase = p.phase_at_t0
    362              else:
    363                  t_var = VisionEgg.time_func() - p.t0_time_sec_absolute
    364                  phase = t_var*p.temporal_freq_hz*-360.0 + p.phase_at_t0
    365              if p.recalculate_phase_tolerance is None or abs(self._last_phase - phase) > p.recalculate_phase_tolerance:
    366                  self._last_phase = phase # we're re-drawing the phase at this angle
    367                  floating_point_sin = numpy.sin(2.0*math.pi*p.spatial_freq*numpy.arange(0.0,w,inc,dtype=numpy.float)+(phase/180.0*math.pi))*0.5*p.contrast+p.pedestal
    368                  floating_point_sin = numpy.clip(floating_point_sin,0.0,1.0) # allow square wave generation if contrast > 1
    369                  texel_data = (floating_point_sin*self.max_int_val).astype(self.numpy_dtype)
    370                  # PyOpenGL 2.0.1.09 has a bug, so use our own wrapper
    371                  _vegl.veglTexSubImage1D(gl.GL_TEXTURE_1D, # target
    372                                          0,                # level
    373                                          0,                # x offset
    374                                          p.num_samples,    # width
    375                                          self.format,      # format of new texel data
    376                                          self.gl_type,     # type of new texel data
    377                                          texel_data)       # new texel data
    378                  if 0:
    379                      compare_array = numpy.empty(texel_data.shape,dtype=texel_data.dtype)
    380                      pixels = _vegl.veglGetTexImage(gl.GL_TEXTURE_1D, # target
    381                                                     0, # level
    382                                                     self.format, # format
    383                                                     self.gl_type, # type
    384                                                     compare_array)
    385                      assert numpy.allclose( compare_array, texel_data )
    386
    387              h_w = p.size[0]/2.0
    388              h_h = p.size[1]/2.0
    389
    390              l = -h_w
    391              r = h_w
    392              b = -h_h
    393              t = h_h
    394
    395              # in the case of only color1,
    396              # the texel data multiplies color1 to produce a color
    397
    398              # with color2,
    399              # the texel data linearly interpolates between color1 and color2
    400
    401              gl.glColor4f(p.color1[0],p.color1[1],p.color1[2],p.max_alpha)
    402
    403              if p.mask:
    404                  p.mask.draw_masked_quad(0.0,1.0,0.0,1.0, # l,r,b,t for texture coordinates
    405                                          l,r,b,t, # l,r,b,t in eye coordinates
    406                                          depth ) # also in eye coordinates
    407              else:
    408                  # draw unmasked quad
    409                  gl.glBegin(gl.GL_QUADS)
    410
    411                  gl.glTexCoord2f(0.0,0.0)
    412                  gl.glVertex3f(l,b,depth)
    413
    414                  gl.glTexCoord2f(1.0,0.0)
    415                  gl.glVertex3f(r,b,depth)
    416
    417                  gl.glTexCoord2f(1.0,1.0)
    418                  gl.glVertex3f(r,t,depth)
    419
    420                  gl.glTexCoord2f(0.0,1.0)
    421                  gl.glVertex3f(l,t,depth)
    422                  gl.glEnd() # GL_QUADS
    423
    424              gl.glDisable(gl.GL_TEXTURE_1D)
    425              gl.glPopMatrix()
    426
    427 -class SinGrating3D(LuminanceGratingCommon):
    428      """Sine wave grating stimulus texture mapped onto quad in 3D
    429
    430      This is a general-purpose, realtime sine-wave luminace grating
    431      generator. This 3D version doesn't support an orientation
    432      parameter.  This could be implemented, but for now should be done
    433      by orienting the quad in 3D.
    434
    435      Parameters
    436      ==========
    437      bit_depth                   -- precision with which grating is calculated and sent to OpenGL (UnsignedInteger)
    438                                     Inherited from LuminanceGratingCommon
    439                                     Default: 8
    440      color1                      -- (AnyOf(Sequence3 of Real or Sequence4 of Real))
    441                                     Default: (1.0, 1.0, 1.0)
    442      color2                      -- optional color with which to perform interpolation with color1 in RGB space (AnyOf(Sequence3 of Real or Sequence4 of Real))
    443                                     Default: (determined at runtime)
    444      contrast                    -- (Real)
    445                                     Default: 1.0
    446      depth                       -- (Real)
    447                                     Default: (determined at runtime)
    448      depth_test                  -- perform depth test? (Boolean)
    449                                     Default: True
    450      ignore_time                 -- (Boolean)
    451                                     Default: False
    452      lowerleft                   -- vertex position (units: eye coordinates) (AnyOf(Sequence3 of Real or Sequence4 of Real))
    453                                     Default: (0.0, 0.0, -1.0)
    454      lowerright                  -- vertex position (units: eye coordinates) (AnyOf(Sequence3 of Real or Sequence4 of Real))
    455                                     Default: (1.0, 0.0, -1.0)
    456      mask                        -- optional masking function (Instance of <class 'VisionEgg.Textures.Mask2D'>)
    457                                     Default: (determined at runtime)
    458      max_alpha                   -- (Real)
    459                                     Default: 1.0
    460      num_samples                 -- (UnsignedInteger)
    461                                     Default: 512
    462      on                          -- draw stimulus? (Boolean)
    463                                     Default: True
    464      pedestal                    -- (Real)
    465                                     Default: 0.5
    466      phase_at_t0                 -- (Real)
    467                                     Default: 0.0
    468      recalculate_phase_tolerance -- (Real)
    469                                     Default: (determined at runtime)
    470      size                        -- defines coordinate size of grating (in eye coordinates) (Sequence2 of Real)
    471                                     Default: (1.0, 1.0)
    472      spatial_freq                -- frequency defined relative to coordinates defined in size parameter (units; cycles/eye_coord_unit) (Real)
    473                                     Default: 4.0
    474      t0_time_sec_absolute        -- (Real)
    475                                     Default: (determined at runtime)
    476      temporal_freq_hz            -- (Real)
    477                                     Default: 5.0
    478      upperleft                   -- vertex position (units: eye coordinates) (AnyOf(Sequence3 of Real or Sequence4 of Real))
    479                                     Default: (0.0, 1.0, -1.0)
    480      upperright                  -- vertex position (units: eye coordinates) (AnyOf(Sequence3 of Real or Sequence4 of Real))
    481                                     Default: (1.0, 1.0, -1.0)
    482      """
    483
    484      parameters_and_defaults = VisionEgg.ParameterDefinition({
    485          'on':(True,
    486                ve_types.Boolean,
    487                "draw stimulus?"),
    488          'mask':(None, # allows window onto otherwise (tilted) rectangular grating
    489                  ve_types.Instance(VisionEgg.Textures.Mask2D),
    490                  "optional masking function"),
    491          'contrast':(1.0,
    492                      ve_types.Real),
    493          'pedestal':(0.5,
    494                      ve_types.Real),
    495          'depth':(None, # if not None, turns on depth testing and allows for occlusion
    496                   ve_types.Real),
    497          'size':((1.0,1.0), # in eye coordinates
    498                  ve_types.Sequence2(ve_types.Real),
    499                  "defines coordinate size of grating (in eye coordinates)"),
    500          'spatial_freq':(4.0, # cycles/eye coord units
    501                          ve_types.Real,
    502                          "frequency defined relative to coordinates defined in size parameter (units; cycles/eye_coord_unit)"),
    503          'temporal_freq_hz':(5.0, # hz
    504                              ve_types.Real),
    505          't0_time_sec_absolute':(None, # Will be assigned during first call to draw()
    506                                  ve_types.Real),
    507          'ignore_time':(False, # ignore temporal frequency variable - allow control purely with phase_at_t0
    508                         ve_types.Boolean),
    509          'phase_at_t0':(0.0, # degrees [0.0-360.0]
    510                         ve_types.Real),
    511          'num_samples':(512, # number of spatial samples, should be a power of 2
    512                         ve_types.UnsignedInteger),
    513          'max_alpha':(1.0, # controls "opacity": 1.0 = completely opaque, 0.0 = completely transparent
    514                       ve_types.Real),
    515          'color1':((1.0, 1.0, 1.0), # alpha is ignored (if given) -- use max_alpha parameter
    516                    ve_types.AnyOf(ve_types.Sequence3(ve_types.Real),
    517                                   ve_types.Sequence4(ve_types.Real))),
    518          'color2':(None, # perform interpolation with color1 in RGB space.
    519                    ve_types.AnyOf(ve_types.Sequence3(ve_types.Real),
    520                                   ve_types.Sequence4(ve_types.Real)),
    521                    "optional color with which to perform interpolation with color1 in RGB space"),
    522          'recalculate_phase_tolerance':(None, # only recalculate texture when phase is changed by more than this amount, None for always recalculate. (Saves time.)
    523                                         ve_types.Real),
    524          'depth_test':(True,
    525                        ve_types.Boolean,
    526                        "perform depth test?"),
    527          'lowerleft':((0.0,0.0,-1.0), # in eye coordinates
    528                       ve_types.AnyOf(ve_types.Sequence3(ve_types.Real),
    529                                      ve_types.Sequence4(ve_types.Real)),
    530                       "vertex position (units: eye coordinates)"),
    531          'lowerright':((1.0,0.0,-1.0), # in eye coordinates
    532                        ve_types.AnyOf(ve_types.Sequence3(ve_types.Real),
    533                                       ve_types.Sequence4(ve_types.Real)),
    534                        "vertex position (units: eye coordinates)"),
    535          'upperleft':((0.0,1.0,-1.0), # in eye coordinates
    536                       ve_types.AnyOf(ve_types.Sequence3(ve_types.Real),
    537                                      ve_types.Sequence4(ve_types.Real)),
    538                       "vertex position (units: eye coordinates)"),
    539          'upperright':((1.0,1.0,-1.0), # in eye coordinates
    540                        ve_types.AnyOf(ve_types.Sequence3(ve_types.Real),
    541                                       ve_types.Sequence4(ve_types.Real)),
    542                        "vertex position (units: eye coordinates)"),
    543          })
    544
    545      __slots__ = (
    546          '_texture_object_id',
    547          '_last_phase',
    548          )
    549
    550 -    def __init__(self,**kw):
    551          LuminanceGratingCommon.__init__(self,**kw)
    552
    553          p = self.parameters # shorthand
    554
    555          self._texture_object_id = gl.glGenTextures(1)
    556          if p.mask:
    557              gl.glActiveTextureARB(gl.GL_TEXTURE0_ARB)
    558          gl.glBindTexture(gl.GL_TEXTURE_1D,self._texture_object_id)
    559
    560          # Do error-checking on texture to make sure it will load
    561          max_dim = gl.glGetIntegerv(gl.GL_MAX_TEXTURE_SIZE)
    562          if p.num_samples > max_dim:
    563              raise NumSamplesTooLargeError("Grating num_samples too large for video system.\nOpenGL reports maximum size of %d"%(max_dim,))
    564
    565          self.calculate_bit_depth_dependencies()
    566
    567          w = p.size[0]
    568          inc = w/float(p.num_samples)
    569          phase = 0.0 # this data won't get used - don't care about phase
    570          self._last_phase = phase
    571          floating_point_sin = numpy.sin(2.0*math.pi*p.spatial_freq*numpy.arange(0.0,w,inc,dtype=numpy.float)+(phase/180.0*math.pi))*0.5*p.contrast+p.pedestal
    572          floating_point_sin = numpy.clip(floating_point_sin,0.0,1.0) # allow square wave generation if contrast > 1
    573          texel_data = (floating_point_sin*self.max_int_val).astype(self.numpy_dtype).tostring()
    574
    575          # Because the MAX_TEXTURE_SIZE method is insensitive to the current
    576          # state of the video system, another check must be done using
    577          # "proxy textures".
    578          gl.glTexImage1D(gl.GL_PROXY_TEXTURE_1D,            # target
    579                          0,                                 # level
    580                          self.gl_internal_format,           # video RAM internal format
    581                          p.num_samples,                     # width
    582                          0,                                 # border
    583                          self.format,                       # format of texel data
    584                          self.gl_type,                      # type of texel data
    585                          texel_data)                        # texel data (irrelevant for proxy)
    586          if gl.glGetTexLevelParameteriv(gl.GL_PROXY_TEXTURE_1D, # Need PyOpenGL >= 2.0
    587                                         0,
    588                                         gl.GL_TEXTURE_WIDTH) == 0:
    589              raise NumSamplesTooLargeError("Grating num_samples is too wide for your video system!")
    590
    591          # If we got here, it worked and we can load the texture for real.
    592          gl.glTexImage1D(gl.GL_TEXTURE_1D,                  # target
    593                          0,                                 # level
    594                          self.gl_internal_format,           # video RAM internal format
    595                          p.num_samples,                     # width
    596                          0,                                 # border
    597                          self.format,                       # format of texel data
    598                          self.gl_type,                      # type of texel data
    599                          texel_data)                        # texel data
    600
    601          # Set texture object defaults
    602          gl.glTexParameteri(gl.GL_TEXTURE_1D,gl.GL_TEXTURE_WRAP_S,gl.GL_CLAMP_TO_EDGE)
    603          gl.glTexParameteri(gl.GL_TEXTURE_1D,gl.GL_TEXTURE_WRAP_T,gl.GL_CLAMP_TO_EDGE)
    604          gl.glTexParameteri(gl.GL_TEXTURE_1D,gl.GL_TEXTURE_MAG_FILTER,gl.GL_LINEAR)
    605          gl.glTexParameteri(gl.GL_TEXTURE_1D,gl.GL_TEXTURE_MIN_FILTER,gl.GL_LINEAR)
    606
    607          if p.color2 is not None:
    608              if VisionEgg.Core.gl_renderer == 'ATi Rage 128 Pro OpenGL Engine' and VisionEgg.Core.gl_version == '1.1 ATI-1.2.22':
    609                  logger = logging.getLogger('VisionEgg.Gratings')
    610                  logger.warning("Your video card and driver have known "
    611                                 "bugs which prevent them from rendering "
    612                                 "color gratings properly.")
    613
    614 -    def __del__(self):
    615          gl.glDeleteTextures( [self._texture_object_id] )
    616
    617 -    def draw(self):
    618          p = self.parameters # shorthand
    619          if p.on:
    620              if p.mask:
    621                  gl.glActiveTextureARB(gl.GL_TEXTURE0_ARB)
    622              if p.depth_test:
    623                  gl.glEnable(gl.GL_DEPTH_TEST)
    624              else:
    625                  gl.glDisable(gl.GL_DEPTH_TEST)
    626              gl.glBindTexture(gl.GL_TEXTURE_1D,self._texture_object_id)
    627              gl.glEnable(gl.GL_TEXTURE_1D)
    628              gl.glDisable(gl.GL_TEXTURE_2D)
    629              if p.bit_depth != self.cached_bit_depth:
    630                  self.calculate_bit_depth_dependencies()
    631
    632              # allow max_alpha value to control blending
    633              gl.glEnable( gl.GL_BLEND )
    634              gl.glBlendFunc( gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA )
    635
    636              if p.color2:
    637                  gl.glTexEnvi(gl.GL_TEXTURE_ENV, gl.GL_TEXTURE_ENV_MODE, gl.GL_BLEND)
    638                  gl.glTexEnvfv(gl.GL_TEXTURE_ENV, gl.GL_TEXTURE_ENV_COLOR, p.color2)
    639                  ## alpha is ignored because the texture base internal format is luminance
    640              else:
    641                  gl.glTexEnvi(gl.GL_TEXTURE_ENV, gl.GL_TEXTURE_ENV_MODE, gl.GL_MODULATE)
    642
    643              if p.t0_time_sec_absolute is None and not p.ignore_time:
    644                  p.t0_time_sec_absolute = VisionEgg.time_func()
    645
    646              w = p.size[0]
    647              inc = w/float(p.num_samples)
    648              if p.ignore_time:
    649                  phase = p.phase_at_t0
    650              else:
    651                  t_var = VisionEgg.time_func() - p.t0_time_sec_absolute
    652                  phase = t_var*p.temporal_freq_hz*-360.0 + p.phase_at_t0
    653              if p.recalculate_phase_tolerance is None or abs(self._last_phase - phase) > p.recalculate_phase_tolerance:
    654                  self._last_phase = phase # we're re-drawing the phase at this angle
    655                  floating_point_sin = numpy.sin(2.0*math.pi*p.spatial_freq*numpy.arange(0.0,w,inc,dtype=numpy.float)+(phase/180.0*math.pi))*0.5*p.contrast+p.pedestal
    656                  floating_point_sin = numpy.clip(floating_point_sin,0.0,1.0) # allow square wave generation if contrast > 1
    657                  texel_data = (floating_point_sin*self.max_int_val).astype(self.numpy_dtype).tostring()
    658
    659                  gl.glTexSubImage1D(gl.GL_TEXTURE_1D, # target
    660                                     0,                # level
    661                                     0,                # x offset
    662                                     p.num_samples,    # width
    663                                     self.format,      # format of new texel data
    664                                     self.gl_type,     # type of new texel data
    665                                     texel_data)       # new texel data
    666
    667              # in the case of only color1,
    668              # the texel data multiplies color1 to produce a color
    669
    670              # with color2,
    671              # the texel data linearly interpolates between color1 and color2
    672
    673              gl.glColor4f(p.color1[0],p.color1[1],p.color1[2],p.max_alpha)
    674
    675              if p.mask:
    676                  p.mask.draw_masked_quad_3d(0.0,1.0,0.0,1.0, # for texture coordinates
    677                                             p.lowerleft,p.lowerright,p.upperright,p.upperleft)
    678              else:
    679                  # draw unmasked quad
    680                  gl.glBegin(gl.GL_QUADS)
    681
    682                  gl.glTexCoord2f(0.0,0.0)
    683                  gl.glVertex3f(*p.lowerleft)
    684
    685                  gl.glTexCoord2f(1.0,0.0)
    686                  gl.glVertex3f(*p.lowerright)
    687
    688                  gl.glTexCoord2f(1.0,1.0)
    689                  gl.glVertex3f(*p.upperright)
    690
    691                  gl.glTexCoord2f(0.0,1.0)
    692                  gl.glVertex3f(*p.upperleft)
    693                  gl.glEnd() # GL_QUADS
    694
    695              gl.glDisable(gl.GL_TEXTURE_1D)
    696
    697 -class NumSamplesTooLargeError( RuntimeError ):
    698      pass
    699