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

# Source Code for Module VisionEgg.Dots

      1  # The Vision Egg: Dots
      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  Random dot stimuli.
     13
     14  """
     15
     16  ####################################################################
     17  #
     18  #        Import all the necessary packages
     19  #
     20  ####################################################################
     21
     22  import logging
     23
     24  import VisionEgg
     25  import VisionEgg.Core
     26  import VisionEgg.ParameterTypes as ve_types
     27
     28  import numpy.oldnumeric as Numeric, numpy.oldnumeric.random_array as RandomArray
     29  import math, types, string
     30
     31  import VisionEgg.GL as gl # get all OpenGL stuff in one namespace
     32
     33  ### C version of draw_dots() isn't (yet) as fast as Python version:
     34  ##import VisionEgg._draw_in_c
     35  ##draw_dots = VisionEgg._draw_in_c.draw_dots # draw in C for speed
     36
     37 -def draw_dots(xs,ys,zs):
     38      """Python method for drawing dots.  May be replaced by a faster C version."""
     39      if not (len(xs) == len(ys) == len(zs)):
     40          raise ValueError("All input arguments must be same length")
     41      gl.glBegin(gl.GL_POINTS)
     42      for i in xrange(len(xs)):
     43          gl.glVertex3f(xs[i],ys[i],zs[i])
     44      gl.glEnd()
     45
     46 -class DotArea2D(VisionEgg.Core.Stimulus):
     47      """Random dots of constant velocity
     48
     49      Every dot has the same velocity. Some fraction of the dots all
     50      move in the one direction, while the rest move in random
     51      directions. Dots wrap around edges. Each dot has a lifespan.
     52
     53      This is just one example of the endless variations on drawing random dots.
     54
     55      Parameters
     56      ==========
     57      anchor                  -- (String)
     58                                 Default: center
     59      anti_aliasing           -- (Boolean)
     60                                 Default: True
     61      color                   -- (AnyOf(Sequence3 of Real or Sequence4 of Real))
     62                                 Default: (1.0, 1.0, 1.0)
     63      depth                   -- (Real)
     64                                 Default: (determined at runtime)
     65      dot_lifespan_sec        -- (Real)
     66                                 Default: 5.0
     67      dot_size                -- (Real)
     68                                 Default: 4.0
     69      on                      -- (Boolean)
     70                                 Default: True
     71      position                -- (Sequence2 of Real)
     72                                 Default: (320.0, 240.0)
     73      signal_direction_deg    -- (Real)
     74                                 Default: 90.0
     75      signal_fraction         -- (Real)
     76                                 Default: 0.5
     77      size                    -- (Sequence2 of Real)
     78                                 Default: (300.0, 300.0)
     79      velocity_pixels_per_sec -- (Real)
     80                                 Default: 10.0
     81
     82      Constant Parameters
     83      ===================
     84      num_dots -- (UnsignedInteger)
     85                  Default: 100
     86      """
     87
     88      parameters_and_defaults = {
     89          'on' : ( True,
     90                   ve_types.Boolean ),
     91          'position' : ( ( 320.0, 240.0 ), # in eye coordinates
     92                         ve_types.Sequence2(ve_types.Real) ),
     93          'anchor' : ('center',
     94                      ve_types.String),
     95          'size' :   ( ( 300.0, 300.0 ), # in eye coordinates
     96                       ve_types.Sequence2(ve_types.Real) ),
     97          'signal_fraction' : ( 0.5,
     98                                ve_types.Real ),
     99          'signal_direction_deg' : ( 90.0,
    100                                     ve_types.Real ),
    101          'velocity_pixels_per_sec' : ( 10.0,
    102                                        ve_types.Real ),
    103          'dot_lifespan_sec' : ( 5.0,
    104                                 ve_types.Real ),
    105          'color' : ((1.0,1.0,1.0),
    106                     ve_types.AnyOf(ve_types.Sequence3(ve_types.Real),
    107                                    ve_types.Sequence4(ve_types.Real))),
    108          'dot_size' : (4.0, # pixels
    109                        ve_types.Real),
    110          'anti_aliasing' : ( True,
    111                              ve_types.Boolean ),
    112          'depth' : ( None, # set for depth testing
    113                      ve_types.Real ),
    114          'center' : (None,  # DEPRECATED -- don't use
    115                      ve_types.Sequence2(ve_types.Real),
    116                      "",
    117                      VisionEgg.ParameterDefinition.DEPRECATED),
    118          }
    119
    120      constant_parameters_and_defaults = {
    121          'num_dots' : ( 100,
    122                         ve_types.UnsignedInteger ),
    123          }
    124
    125      __slots__ = (
    126          'x_positions',
    127          'y_positions',
    128          'random_directions_radians',
    129          'last_time_sec',
    130          'start_times_sec',
    131          '_gave_alpha_warning',
    132          )
    133
    134 -    def __init__(self, **kw):
    135          VisionEgg.Core.Stimulus.__init__(self,**kw)
    136          # store positions normalized between 0 and 1 so that re-sizing is ok
    137          num_dots = self.constant_parameters.num_dots # shorthand
    138          self.x_positions = RandomArray.uniform(0.0,1.0,(num_dots,))
    139          self.y_positions = RandomArray.uniform(0.0,1.0,(num_dots,))
    140          self.random_directions_radians = RandomArray.uniform(0.0,2*math.pi,(num_dots,))
    141          self.last_time_sec = VisionEgg.time_func()
    142          self.start_times_sec = None # setup variable, assign later
    143          self._gave_alpha_warning = 0
    144
    145 -    def draw(self):
    146          # XXX This method is not speed-optimized. I just wrote it to
    147          # get the job done. (Nonetheless, it seems faster than the C
    148          # version commented out above.)
    149
    150          p = self.parameters # shorthand
    151          if p.center is not None:
    152              if not hasattr(VisionEgg.config,"_GAVE_CENTER_DEPRECATION"):
    153                  logger = logging.getLogger('VisionEgg.Dots')
    154                  logger.warning("Specifying DotArea2D by deprecated "
    155                                 "'center' parameter deprecated.  Use "
    156                                 "'position' parameter instead.  (Allows "
    157                                 "use of 'anchor' parameter to set to "
    158                                 "other values.)")
    159                  VisionEgg.config._GAVE_CENTER_DEPRECATION = 1
    160              p.anchor = 'center'
    161              p.position = p.center[0], p.center[1] # copy values (don't copy ref to tuple)
    162          if p.on:
    163              # calculate center
    164              center = VisionEgg._get_center(p.position,p.anchor,p.size)
    165
    166              if p.anti_aliasing:
    167                  if len(p.color) == 4 and not self._gave_alpha_warning:
    168                      if p.color[3] != 1.0:
    169                          logger = logging.getLogger('VisionEgg.Dots')
    170                          logger.warning("The parameter anti_aliasing is "
    171                                         "set to true in the DotArea2D "
    172                                         "stimulus class, but the color "
    173                                         "parameter specifies an alpha "
    174                                         "value other than 1.0.  To "
    175                                         "acheive the best anti-aliasing, "
    176                                         "ensure that the alpha value for "
    177                                         "the color parameter is 1.0.")
    178                          self._gave_alpha_warning = 1
    179                  gl.glEnable( gl.GL_POINT_SMOOTH )
    180                  # allow max_alpha value to control blending
    181                  gl.glEnable( gl.GL_BLEND )
    182                  gl.glBlendFunc( gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA )
    183              else:
    184                  gl.glDisable( gl.GL_BLEND )
    185
    186              now_sec = VisionEgg.time_func()
    187              if self.start_times_sec is not None:
    188                  # compute extinct dots and generate new positions
    189                  replace_indices = Numeric.nonzero( Numeric.greater( now_sec - self.start_times_sec, p.dot_lifespan_sec) )
    190                  Numeric.put( self.start_times_sec, replace_indices, now_sec )
    191
    192                  new_x_positions = RandomArray.uniform(0.0,1.0,
    193                                                        (len(replace_indices),))
    194                  Numeric.put( self.x_positions, replace_indices, new_x_positions )
    195
    196                  new_y_positions = RandomArray.uniform(0.0,1.0,
    197                                                        (len(replace_indices),))
    198                  Numeric.put( self.y_positions, replace_indices, new_y_positions )
    199
    200                  new_random_directions_radians = RandomArray.uniform(0.0,2*math.pi,
    201                                                                      (len(replace_indices),))
    202                  Numeric.put( self.random_directions_radians, replace_indices, new_random_directions_radians )
    203              else:
    204                  # initialize dot extinction values to random (uniform) distribution
    205                  self.start_times_sec = RandomArray.uniform( now_sec - p.dot_lifespan_sec, now_sec,
    206                                                              (self.constant_parameters.num_dots,))
    207
    208              signal_num_dots = int(round(p.signal_fraction * self.constant_parameters.num_dots))
    209              time_delta_sec = now_sec - self.last_time_sec
    210              self.last_time_sec = now_sec # reset for next loop
    211              x_increment_normalized =  math.cos(p.signal_direction_deg/180.0*math.pi) * p.velocity_pixels_per_sec / p.size[0] * time_delta_sec
    212              y_increment_normalized = -math.sin(p.signal_direction_deg/180.0*math.pi) * p.velocity_pixels_per_sec / p.size[1] * time_delta_sec
    213              self.x_positions[:signal_num_dots] += x_increment_normalized
    214              self.y_positions[:signal_num_dots] += y_increment_normalized
    215
    216              num_random_dots = self.constant_parameters.num_dots - signal_num_dots
    217              random_x_increment_normalized =  Numeric.cos(self.random_directions_radians[signal_num_dots:]) * p.velocity_pixels_per_sec / p.size[0] * time_delta_sec
    218              random_y_increment_normalized = -Numeric.sin(self.random_directions_radians[signal_num_dots:]) * p.velocity_pixels_per_sec / p.size[1] * time_delta_sec
    219              self.x_positions[signal_num_dots:] += random_x_increment_normalized
    220              self.y_positions[signal_num_dots:] += random_y_increment_normalized
    221
    222              self.x_positions = Numeric.fmod( self.x_positions, 1.0 ) # wrap
    223              self.y_positions = Numeric.fmod( self.y_positions, 1.0 )
    224
    225              self.x_positions = Numeric.fmod( self.x_positions+1, 1.0 ) # wrap again for values < 1
    226              self.y_positions = Numeric.fmod( self.y_positions+1, 1.0 )
    227
    228              xs = (self.x_positions - 0.5) * p.size[0] + center[0]
    229              ys = (self.y_positions - 0.5) * p.size[1] + center[1]
    230
    231              if len(p.color)==3:
    232                  gl.glColor3f(*p.color)
    233              elif len(p.color)==4:
    234                  gl.glColor4f(*p.color)
    235              gl.glPointSize(p.dot_size)
    236
    237              # Clear the modeview matrix
    238              gl.glMatrixMode(gl.GL_MODELVIEW)
    239              gl.glPushMatrix()
    240
    241              gl.glDisable(gl.GL_TEXTURE_2D)
    242
    243              if p.depth is None:
    244                  depth = 0.0
    245              else:
    246                  gl.glEnable(gl.GL_DEPTH_TEST)
    247                  depth = p.depth
    248              zs = (depth,)*len(xs) # make N tuple with repeat value of depth
    249              draw_dots(xs,ys,zs)
    250              if p.anti_aliasing:
    251                  gl.glDisable( gl.GL_POINT_SMOOTH ) # turn off
    252              gl.glPopMatrix()
    253