Skip to content

Commit f2f8870

Browse files
committed
update dot algo
1 parent 3f84d26 commit f2f8870

File tree

6 files changed

+108
-101
lines changed

6 files changed

+108
-101
lines changed

setParameters.m

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,17 +50,21 @@
5050
cfg.numEventsPerBlock = 12;
5151
cfg.eventDuration = 1; % second
5252

53-
% speed in visual angles
54-
cfg.dot.speed = 1;
53+
% speed in visual angles / second
54+
cfg.dot.speed = 15;
5555
% Coherence Level (0-1)
56-
cfg.dot.coh = .7;
56+
cfg.dot.coherence = 1;
5757
% nb dots per visual angle square.
58-
cfg.dot.density = 5;
58+
cfg.dot.density = .25;
5959

6060
% Dot life time in seconds
61-
cfg.dot.lifeTime = 1;
61+
cfg.dot.lifeTime = 10;
62+
63+
% proportion of dots killed per frame
64+
cfg.dot.proportionKilledPerFrame = .05;
65+
6266
% Dot Size (dot width) in visual angles.
63-
cfg.dot.size = 0.1;
67+
cfg.dot.size = 1;
6468
cfg.dot.color = cfg.color.white;
6569

6670
% Diameter/length of side of aperture in Visual angles

subfun/doDotMo.m

Lines changed: 11 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -6,110 +6,34 @@
66
%
77
% Input:
88
% - cfg: PTB/machine configurations returned by setParameters and initPTB
9-
% - expParameters: parameters returned by setParameters
10-
% - logFile: structure that stores the experiment logfile to be saved
119
%
1210
% Output:
1311
% -
1412
%
15-
% The dots are drawn on a square that contains the round aperture, then any
16-
% dots outside of the aperture is turned into a NaN so effectively the
17-
% actual number of dots on the screen at any given time is not the one that you input but a
18-
% smaller number (nDots / Area of aperture) on average.
13+
% The dots are drawn on a square with a width equals to the width of the
14+
% screen
15+
% We then draw an aperture on top to hide the certain dots.
1916

2017
%% Get parameters
21-
direction = thisEvent.direction(1);
22-
isTarget = thisEvent.target(1);
2318

24-
25-
dotLifeTime = cfg.dot.lifeTime;
26-
27-
targetDuration = cfg.target.duration;
28-
29-
% thisEvent = deg2Pix('speed', thisEvent, cfg);
30-
% dotSpeedPix = logFile.iEventSpeedPix;
31-
32-
coh = cfg.dot.coh;
33-
speed = thisEvent.speed(1);
34-
% Check if it is a static or motion block
35-
if direction == -1
36-
37-
speed = 0;
38-
coh = 1;
39-
40-
dotLifeTime = cfg.eventDuration;
41-
end
42-
43-
%% initialize variables
44-
45-
% Set an array of dot positions [xposition, yposition]
46-
% These can never be bigger than 1 or lower than 0
47-
% [0,0] is the top / left of the square
48-
% [1,1] is the bottom / right of the square
49-
dotPositions = rand(cfg.dot.number, 2);
50-
51-
% Set a N x 2 matrix that gives jump size in pixels
52-
% pix/sec * sec/frame = pix / frame
53-
dxdy = repmat( ...
54-
speed * 10 / (cfg.aperture.width * 10) * (3 / cfg.screen.monitorRefresh) * ...
55-
[cos(pi * direction / 180.0) -sin(pi * direction / 180.0)], cfg.dot.number, 1);
56-
57-
% dxdy = repmat(...
58-
% dotSpeedPix / Cfg.ifi ...
59-
% * (cos(pi*direction/180) - sin(pi*direction/180)), ...
60-
% ndots, 1);
61-
62-
% Create a ones vector to update to dotlife time of each dot
63-
dotTime = ones(size(dotPositions, 1), 1);
64-
65-
% Covert the dotLifeTime from seconds to frames
66-
dotLifeTime = ceil(dotLifeTime / cfg.screen.ifi);
19+
dots = initializeDots(cfg, thisEvent);
6720

6821
% Set for how many frames this event will last
6922
framesLeft = floor(cfg.eventDuration / cfg.screen.ifi);
70-
23+
7124
%% Start the dots presentation
7225
vbl = Screen('Flip', cfg.screen.win);
7326
onset = vbl;
7427

7528
while framesLeft
7629

77-
% L are the dots that will be moved
78-
L = rand(cfg.dot.number, 1) < coh;
79-
80-
% Move the selected dots
81-
dotPositions(L, :) = dotPositions(L, :) + dxdy(L, :);
82-
83-
% If not 100% coherence, we get new random locations for the other dots
84-
if sum(~L) > 0
85-
dotPositions(~L, :) = rand(sum(~L), 2);
86-
end
87-
88-
% Create a logical vector to detect any dot that has:
89-
% - an xy position inferior to 0
90-
% - an xy position superior to 1
91-
% - has exceeded its liftime
92-
N = any([dotPositions > 1, dotPositions < 0, dotTime > dotLifeTime], 2) ;
30+
[dots] = updateDots(dots, cfg);
9331

94-
% If there is any such dot we relocate it to a new random position
95-
% and change its lifetime to 1
96-
if any(N)
97-
dotPositions(N, :) = rand(sum(N), 2);
98-
dotTime(N, 1) = 1;
99-
end
100-
101-
%% Convert the dot position to pixels
102-
% We expand that square so that its side is equal to the whole
103-
% screen width.
104-
% With no aperture the whole screen is filled with dots.
105-
dotPositionsPix = floor(dotPositions * cfg.screen.winRect(3));
106-
107-
% This assumes that zero is at the top left, but we want it to be
32+
%% Center the dots
33+
% We assumed that zero is at the top left, but we want it to be
10834
% in the center, so shift the dots up and left, which just means
109-
% adding half of the aperture size to both the x and y direction.
110-
dotPositionsPix = (dotPositionsPix - cfg.screen.winRect(3) / 2)';
111-
112-
thisEvent.dot.positions = dotPositionsPix;
35+
% adding half of the screen width in pixel to both the x and y direction.
36+
thisEvent.dot.positions = (dots.positions - cfg.screen.winWidth / 2)';
11337

11438
%% make textures
11539
dotTexture('make', cfg, thisEvent);
@@ -125,7 +49,7 @@
12549
% If this frame shows a target we change the color
12650
thisFixation.fixation = cfg.fixation;
12751
thisFixation.screen = cfg.screen;
128-
if GetSecs < (onset + targetDuration) && isTarget == 1
52+
if thisEvent.target(1) && GetSecs < (onset + cfg.target.duration)
12953
thisFixation.fixation.color = cfg.fixation.colorTarget;
13054
end
13155
drawFixation(thisFixation);
@@ -139,9 +63,6 @@
13963
% Check for end of loop
14064
framesLeft = framesLeft - 1;
14165

142-
% Add one frame to the dot lifetime to each dot
143-
dotTime = dotTime + 1;
144-
14566
end
14667

14768
%% Erase last dots
@@ -155,4 +76,3 @@
15576
duration = vbl - onset;
15677

15778
end
158-

subfun/expDesign.m

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@
3838

3939
% Set directions for static and motion condition
4040
motionDirections = [0 90 180 270];
41-
staticDirections = [-1 -1 -1 -1];
41+
staticDirections = [0 90 180 270];
42+
% staticDirections = [-1 -1 -1 -1];
4243

4344
%% Check inputs
4445

@@ -59,7 +60,7 @@
5960
% Get the parameters
6061
names = cfg.names;
6162
numRepetitions = cfg.numRepetitions;
62-
speedEvent = cfg.dot.speed;
63+
dotsSpeed = cfg.dot.speedPixPerFrame;
6364
numEventsPerBlock = cfg.numEventsPerBlock;
6465
maxNumFixTargPerBlock = cfg.target.maxNbPerBlock;
6566

@@ -93,7 +94,7 @@
9394

9495
cfg.design.blockNames = cell(nrBlocks, 1);
9596
cfg.design.directions = zeros(nrBlocks, numEventsPerBlock);
96-
cfg.design.speeds = ones(nrBlocks, numEventsPerBlock) * speedEvent;
97+
cfg.design.speeds = ones(nrBlocks, numEventsPerBlock) * dotsSpeed;
9798
cfg.design.fixationTargets = zeros(nrBlocks, numEventsPerBlock);
9899

99100
for iMotionBlock = 1:numRepetitions

subfun/initializeDots.m

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
function [dots] = initializeDots(cfg, thisEvent)
2+
3+
direction = thisEvent.direction(1);
4+
5+
dots.lifeTime = cfg.dot.lifeTime;
6+
7+
speedPixPerFrame = thisEvent.speed(1);
8+
9+
% decide which dots are signal dots (1) and those are noise dots (0)
10+
dots.isSignal = rand(cfg.dot.number, 1) < cfg.dot.coherence;
11+
12+
% for static dots
13+
if direction == -1
14+
speedPixPerFrame = 0;
15+
dots.lifeTime = cfg.eventDuration;
16+
dots.isSignal = ones(cfg.dot.number, 1);
17+
end
18+
19+
% Convert from seconds to frames
20+
dots.lifeTime = ceil(dots.lifeTime / cfg.screen.ifi);
21+
22+
% Set an array of dot positions [xposition, yposition]
23+
% These can never be bigger than 1 or lower than 0
24+
% [0,0] is the top / left of the square
25+
% [1,1] is the bottom / right of the square
26+
dots.positions = rand(cfg.dot.number, 2) * cfg.screen.winWidth;
27+
28+
% Set a N x 2 matrix that speed in X and Y
29+
dots.speeds = nan(cfg.dot.number, 2);
30+
31+
% Coherent dots
32+
[horVector, vertVector] = decompMotion(direction);
33+
dots.speeds(dots.isSignal,:) = ...
34+
repmat([horVector, vertVector], sum(dots.isSignal), 1);
35+
36+
% If not 100% coherence, we get new random direction for the other dots
37+
direction = rand(sum(~dots.isSignal), 1) * 360;
38+
[horVector, vertVector] = decompMotion(direction);
39+
dots.speeds(~dots.isSignal, :) = [horVector, vertVector];
40+
41+
% So far we were working wiht unit vectors convert that speed in pixels per
42+
% frame
43+
dots.speeds = dots.speeds * speedPixPerFrame;
44+
45+
% Create a vector to update to dotlife time of each dot
46+
% Not all set to one so the dots will die at different times
47+
dots.time = floor(rand(cfg.dot.number, 1) * cfg.eventDuration / cfg.screen.ifi);
48+
49+
end
50+

subfun/updateDots.m

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
function [dots] = updateDots(dots, cfg)
2+
3+
% Move the selected dots
4+
dots.positions = dots.positions + dots.speeds;
5+
6+
% Create a logical vector to detect any dot that has:
7+
% - an xy position inferior to 0
8+
% - an x position superior to winWidth
9+
% - an x position superior to winHeight
10+
% - has exceeded its liftime
11+
N = any([...
12+
dots.positions > cfg.screen.winWidth, ...
13+
dots.positions < 0, ...
14+
dots.time > dots.lifeTime, ...
15+
rand(cfg.dot.number,1) < cfg.dot.proportionKilledPerFrame ], 2) ;
16+
17+
% If there is any such dot we relocate it to a new random position
18+
% and change its lifetime to 1
19+
if any(N)
20+
dots.positions(N, :) = rand(sum(N), 2) * cfg.screen.winWidth;
21+
dots.time(N, 1) = 1;
22+
end
23+
24+
% Add one frame to the dot lifetime to each dot
25+
dots.time = dots.time + 1;
26+
end

visualLocTanslational.m

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,16 @@
3535

3636
% Convert some values from degrees to pixels
3737
cfg.dot = degToPix('size', cfg.dot, cfg);
38+
cfg.dot = degToPix('speed', cfg.dot, cfg);
39+
40+
% Get dot speeds in pixels per frame
41+
cfg.dot.speedPixPerFrame = cfg.dot.speedPix / cfg.screen.monitorRefresh;
42+
43+
cfg.aperture = degToPix('xPos', cfg.aperture, cfg);
3844

3945
% dots are displayed on a square with a length in visual angle equal to the
4046
% field of view
41-
cfg.dot.number = cfg.dot.density * cfg.screen.FOV^2;
47+
cfg.dot.number = round(cfg.dot.density * (cfg.screen.winWidth / cfg.screen.ppd)^2);
4248

4349
[el] = eyeTracker('Calibration', cfg);
4450

0 commit comments

Comments
 (0)