Permalink
Browse files

Bug fix for dots.py

The bugs are:

1. Dots that go out of bounds are replaced at a random location. When coherence is high this results in a shift in dot density toward the edge where most dots are going out of bounds. When coherence is low it causes a shift way from all borders. This is because the dots are disappearing at edges to keep the distribution of dots uniform they should be reborn at the opposite edge.

2. When noise dots are in walk mode the dot directions are not updated often enough such that as the coherence changes some noise dots become signal dots but retain the ransom direction they had when they were noise.

3. Dot locations are not refreshed between trials. If dot lifetime is long (esp infinite) one can see clusters of dots consistent from trial to trail. This accentuates the bug in (1) when signal dots reverse direction between trials as the accumulated dots on one side of the field then all charge back across the field together. 



I’ve fixed these by. See dots.py attached
  • Loading branch information...
schofiaj committed Jan 9, 2017
1 parent efba0d6 commit db6f53d50a07556fcc0e7a2de486efb341054d42
Showing with 40 additions and 6 deletions.
  1. +40 −6 psychopy/visual/dot.py
@@ -12,6 +12,14 @@
# other calls to pyglet or pyglet submodules, otherwise it may not get picked
# up by the pyglet GL engine and have no effect.
# Shaders will work but require OpenGL2.0 drivers AND PyOpenGL3.0+
# Bugfix by Andrew Schofield.
# Replaces out of bounds but still live dots at opposite edge of aperture instead of randomly within the field. This stops the concentration of dots at one side of field when lifetime is long.
# Update the dot direction immediately for 'walk' as otherwise when the coherence varies some signal dots will inherit the random directions of previous walking dots.
# Provide a visible wrapper function to refresh all the dot locations so that the whole field can be more easily refreshed between trials.
import pyglet
pyglet.options['debug_gl'] = False
import ctypes
@@ -278,7 +286,9 @@ def coherence(self, coherence):
# for 'direction' method we need to update the direction of the number
# of signal dots immediately, but for other methods it will be done
# during updateXY
if self.noiseDots in ['direction', 'position']:
#:::::::::::::::::::: AJS Actually you need to do this for 'walk' also otherwise
#would be signal dots adopt random directions when the become sinal dots in later trails
if self.noiseDots in ['direction', 'position','walk']:
self._dotsDir = numpy.random.rand(self.nDots) * 2 * pi
self._dotsDir[self._signalDots] = self.dir * pi / 180
@@ -389,7 +399,11 @@ def _newDotsXY(self, nDots):
return new[inCircle, :][:nDots, :] * 0.5
else:
return numpy.random.uniform(-0.5, 0.5, [nDots, 2])
def refreshDots(self):
"""Callable user function to choose a new set of dots"""
self._verticesBase = self._dotsXY = self._newDotsXY(self.nDots)
def _update_dotsXY(self):
"""The user shouldn't call this - its gets done within draw().
"""
@@ -446,21 +460,41 @@ def _update_dotsXY(self):
# handle boundaries of the field
if self.fieldShape in (None, 'square', 'sqr'):
dead0 = (numpy.abs(self._verticesBase[:, 0]) > 0.5)
dead1 = (numpy.abs(self._verticesBase[:, 1]) > 0.5)
dead = dead + dead0 + dead1
#dead0 = (numpy.abs(self._verticesBase[:, 0]) > 0.5)
#dead1 = (numpy.abs(self._verticesBase[:, 1]) > 0.5)
#dead = dead + dead0 + dead1
out0 = (numpy.abs(self._verticesBase[:, 0]) > 0.5)
out1 = (numpy.abs(self._verticesBase[:, 1]) > 0.5)
outofbounds = out0 + out1
elif self.fieldShape == 'circle':
#outofbounds=None
# transform to a normalised circle (radius = 1 all around)
# then to polar coords to check
# the normalised XY position (where radius should be < 1)
normXY = self._verticesBase / 0.5
# add out-of-bounds to those that need replacing
dead = dead + (numpy.hypot(normXY[:, 0], normXY[:, 1]) > 1)
#dead+= (numpy.hypot(normXY[:, 0], normXY[:, 1]) > 1)
outofbounds = (numpy.hypot(normXY[:, 0], normXY[:, 1]) > 1)
# update any dead dots
if sum(dead):
self._verticesBase[dead, :] = self._newDotsXY(sum(dead))
#self._verticesBase[dead, :] = -self._verticesBase[dead,:]
# Reposition any dots that have gone out of bounds. Net effect is to place dot one step inside the boundary on the other side of the aperture.
if sum(outofbounds):
#wind the dots back one step and store as tempary values
if self.noiseDots == 'position':
tempvert0=self._verticesBase[sd,0]-self.speed * cosDots
tempvert1=self._verticesBase[sd,1]-self.speed * sinDots
else:
tempvert0=self._verticesBase[:,0]-self.speed * cosDots
tempvert1=self._verticesBase[:,1]-self.speed * sinDots
#reflect the position of the dots about the origine of the dot field
self._verticesBase[outofbounds, 0] = -tempvert0[outofbounds]
self._verticesBase[outofbounds, 1] = -tempvert1[outofbounds]
# update the pixel XY coordinates in pixels (using _BaseVisual class)
self._updateVertices()

0 comments on commit db6f53d

Please sign in to comment.