-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathdots_limitedlifetime.m
120 lines (98 loc) · 5.6 KB
/
dots_limitedlifetime.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
% This code reproduces the analyses in the paper
% Urai AE, Braun A, Donner THD (2016) Pupil-linked arousal is driven
% by decision uncertainty and alters serial choice bias.
%
% Permission is hereby granted, free of charge, to any person obtaining a
% copy of this software and associated documentation files (the "Software"),
% to deal in the Software without restriction, including without limitation
% the rights to use, copy, modify, merge, publish, distribute, sublicense,
% and/or sell copies of the Software, and to permit persons to whom the
% Software is furnished to do so, subject to the following conditions:
%
% The above copyright notice and this permission notice shall be included
% in all copies or substantial portions of the Software.
% If you use the Software for your own research, cite the paper.
%
% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
% OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
% DEALINGS IN THE SOFTWARE.
%
% Anne Urai, 2016
function [stimuli] = dots_limitedlifetime(setup, window, dots)
% Generates a dot patch according to a limited lifetime
% algorithm, see:
% Pilly, P. K., & Seitz, A. R. (2009).
% What a difference a parameter makes: a psychophysical comparison
% of random dot motion algorithms. Vision Research, 49(13), 1599-612.
% IMPORTANT direction of motion is clockwise from vertical, so not the unit circle!
% --------------------------------------------------------------------------
NVAR = dots.nvar; % interleaved sequence
LIFETIME = dots.lifetime;
NDOTS = dots.nDots; % 774, 3097
DIRECTION = dots.direction;
COH = dots.coherence;
SPEED = dots.speed; % downward, as a function of speed (in degrees per second) and framerate)
NFR = ceil(setup.nframes/NVAR);
RADIUS = dots.radius;
INNER = dots.innerspace; % inner circle doesn't contain dots
for var = 1:NVAR, %create separate variants
% generate random starting points within a circular aperture
rad = RADIUS*sqrt(rand(NDOTS,1));
theta = 2*pi*rand(NDOTS,1);
pos = [rad.*cos(theta) rad.*sin(theta)]; % convert to cartesian coordinates, x/y
% assign each dot a scalar that indicates how long it has been
% 'alive' (that is, a signal dot)
% Each dot will have a integer value 'life' which is how many frames the
% dot has been going. The starting 'life' of each dot will be a random
% number between 0 and dots.lifetime-1 so that they don't all 'die' on the
% same frame:
life = ceil(rand(1,NDOTS)*LIFETIME);
for frameNum = 1:NFR,
% define the noise dots
noisedots = round(NDOTS*(1-COH)); %nr of noisedots based on the coherence
t1 = rand(NDOTS,1);
[t1,t2] = sort(t1);
noiseindex = t2(1:noisedots); %random subset of dots
% define signal dots
signalindex = t2(noisedots+1:end); %the dots that are signal
% move signal dots with a certain speed in the right direction
pos(signalindex,:) = [SPEED.*cos(DIRECTION*pi/180)/window.frameRate+pos(signalindex,1), ...
SPEED.*sin(DIRECTION*pi/180)/window.frameRate+pos(signalindex,2)]; %convert to cartesian coordinates
% replot the noisedots somewhere in the aperture
rad = RADIUS*sqrt(rand(noisedots,1));
theta = 2*pi*rand(noisedots,1);
[pos(noiseindex, 1), pos(noiseindex,2)] = pol2cart(theta, rad);
% increment the 'life' of each dot
life = life+1;
% find the 'dead' dots
deadindex = mod(life,LIFETIME)==0;
deaddots = length(find(deadindex==1));
% replace the positions of the dead dots to a random location
rad = RADIUS*sqrt(rand(deaddots,1));
theta = 2*pi*rand(deaddots,1);
[pos(deadindex, 1), pos(deadindex,2)] = pol2cart(theta, rad);
% find the dots that have left the aperture
outindex = find(sqrt((pos(:,1).^2)+(pos(:,2).^2)) >= RADIUS); %index dots that are outside the aperture
% wrap them around in the direction where they came from
[theta, rad] = cart2pol(pos(outindex, 1), pos(outindex,2));
theta = theta + pi; %move to the other side of the circle
rad = RADIUS*ones(length(rad),1);
[pos(outindex, 1), pos(outindex,2)] = pol2cart(theta, rad);
% find the dots that are too close to the fixation
innerindex = find(sqrt((pos(:,1).^2)+(pos(:,2).^2)) <= INNER);
[theta, rad] = cart2pol(pos(innerindex, 1), pos(innerindex, 2));
rad = INNER + (RADIUS - INNER)*sqrt(rand(length(innerindex),1)); %random radius
theta = theta + pi;
[pos(innerindex, 1), pos(innerindex,2)] = pol2cart(theta, rad);
% save the positions per variant
temp(var,frameNum, :, :) = pos';
end
end
stimuli = squeeze(reshape(temp, 1, NFR*NVAR, 2, NDOTS));
stimuli = stimuli(1:setup.nframes, :, :);
end