Skip to content

Commit 9578e80

Browse files
committed
refactor to prepare for radial motion
1 parent 5120396 commit 9578e80

File tree

8 files changed

+118
-42
lines changed

8 files changed

+118
-42
lines changed

apertureTexture.m

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@
4040
Screen('FillOval', cfg.aperture.texture, transparent, ...
4141
CenterRectOnPoint([0 0 repmat(diameter, 1, 2)], ...
4242
xPos, yPos));
43+
44+
otherwise
45+
46+
error('unknown aperture type: %s.', cfg.aperture.type);
4347

4448
end
4549

src/dot/computeCartCoord.m

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
function cartesianCoordinates = computeCartCoord(positions, cfg)
2+
cartesianCoordinates = ...
3+
[positions(:,1) - cfg.screen.winWidth / 2, ... % x coordinate
4+
positions(:,2) + cfg.screen.winWidth / 2]; % y coordinate
5+
end
Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
1-
function direction = computeRadialMotionDirection(cfg, positions, direction)
2-
if strcmp(cfg.design.motionType, 'radial')
3-
4-
cartesianCoordinates = positions - cfg.screen.winWidth / 2;
5-
6-
[angle, ~] = cart2pol(cartesianCoordinates(:, 1), cartesianCoordinates(:, 2));
7-
angle = angle / pi * 180;
8-
9-
if direction == -666
10-
angle = angle - 180;
11-
end
12-
13-
direction = angle;
14-
1+
function angleMotion = computeRadialMotionDirection(cfg, dots)
2+
3+
cartesianCoordinates = computeCartCoord(dots.positions, cfg);
4+
5+
[angleMotion, ~] = cart2pol(cartesianCoordinates(:, 1), cartesianCoordinates(:, 2));
6+
angleMotion = angleMotion / pi * 180;
7+
8+
if dots.direction == -666
9+
angleMotion = angleMotion - 180;
1510
end
11+
1612
end
13+

src/dot/initDots.m

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,32 +18,32 @@
1818
% TODO
1919
% bound direction between 0 and 360 ??
2020

21-
direction = thisEvent.direction(1);
21+
dots.direction = thisEvent.direction(1);
2222

2323
speedPixPerFrame = thisEvent.speed(1);
2424

2525
lifeTime = cfg.dot.lifeTime;
2626

2727
% decide which dots are signal dots (1) and those are noise dots (0)
28-
isSignal = rand(cfg.dot.number, 1) < cfg.dot.coherence;
28+
dots.isSignal = rand(cfg.dot.number, 1) < cfg.dot.coherence;
2929

3030
% for static dots
31-
if direction == -1
31+
if dots.direction == -1
3232
speedPixPerFrame = 0;
3333
lifeTime = Inf;
34-
isSignal = true(cfg.dot.number, 1);
34+
dots.isSignal = true(cfg.dot.number, 1);
3535
end
3636

3737
%% Set an array of dot positions [xposition, yposition]
3838
% These can never be bigger than 1 or lower than 0
3939
% [0,0] is the top / left of the square
4040
% [1,1] is the bottom / right of the square
41-
positions = rand(cfg.dot.number, 2) * cfg.screen.winWidth;
41+
dots.positions = rand(cfg.dot.number, 2) * cfg.screen.winWidth;
4242

4343
%% Set vertical and horizontal speed for all dots
44-
directionAllDots = setDotDirection(cfg, positions, direction, isSignal);
44+
dots = setDotDirection(cfg, dots);
4545

46-
[horVector, vertVector] = decomposeMotion(directionAllDots);
46+
[horVector, vertVector] = decomposeMotion(dots.directionAllDots);
4747
speeds = [horVector, vertVector];
4848

4949
% we were working with unit vectors. we now switch to pixels
@@ -61,20 +61,8 @@
6161
dots.lifeTime = lifeTime;
6262
dots.time = time;
6363
dots.speeds = speeds;
64-
dots.isSignal = isSignal;
65-
dots.positions = positions;
66-
end
67-
68-
function directionAllDots = setDotDirection(cfg, positions, direction, isSignal)
69-
70-
directionAllDots = nan(cfg.dot.number, 1);
64+
dots.speedPixPerFrame = speedPixPerFrame;
7165

72-
direction = computeRadialMotionDirection(cfg, positions, direction);
66+
end
7367

74-
% Coherent dots
75-
directionAllDots(isSignal) = direction;
76-
% Random direction for the non coherent dots
77-
directionAllDots(~isSignal) = rand(sum(~isSignal), 1) * 360;
78-
directionAllDots = rem(directionAllDots, 360);
7968

80-
end

src/dot/reseedDots.m

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,39 @@
11
function dots = reseedDots(dots, cfg)
2-
2+
3+
fixationWidthPix = 0;
4+
if isfield(cfg.fixation, 'widthPix')
5+
fixationWidthPix = cfg.fixation.widthPix;
6+
end
7+
8+
cartesianCoordinates = computeCartCoord(dots.positions, cfg);
9+
[~, radius] = cart2pol(cartesianCoordinates(:, 1), cartesianCoordinates(:, 2));
10+
311
% Create a logical vector to detect any dot that has:
412
% - an xy position inferior to 0
513
% - an xy position superior to winWidth
614
% - has exceeded its liftime
15+
% - is on the fixation cross
716
% - has been been picked to be killed
17+
818
N = any([ ...
919
dots.positions > cfg.screen.winWidth, ...
1020
dots.positions < 0, ...
1121
dots.time > dots.lifeTime, ...
12-
rand(cfg.dot.number, 1) < cfg.dot.proportionKilledPerFrame], 2) ;
22+
radius - cfg.dot.sizePix < fixationWidthPix / 2, ...
23+
rand(cfg.dot.number, 1) < cfg.dot.proportionKilledPerFrame, ...
24+
], 2) ;
1325

1426
% If there is any such dot we relocate it to a new random position
1527
% and change its lifetime to 1
1628
if any(N)
29+
1730
dots.positions(N, :) = rand(sum(N), 2) * cfg.screen.winWidth;
31+
32+
dots = setDotDirection(cfg, dots);
33+
[horVector, vertVector] = decomposeMotion(dots.directionAllDots);
34+
dots.speeds = [horVector, vertVector] * dots.speedPixPerFrame;
35+
1836
dots.time(N, 1) = 1;
1937
end
20-
21-
end
38+
39+
end

src/dot/setDotDirection.m

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
function dots = setDotDirection(cfg, dots)
2+
3+
directionAllDots = nan(cfg.dot.number, 1);
4+
5+
% Coherent dots
6+
directionAllDots(dots.isSignal) = dots.direction;
7+
8+
if strcmp(cfg.design.motionType, 'radial')
9+
angleMotion = computeRadialMotionDirection(cfg, dots);
10+
directionAllDots(dots.isSignal) = angleMotion;
11+
end
12+
13+
% Random direction for the non coherent dots
14+
directionAllDots(~dots.isSignal) = rand(sum(~dots.isSignal), 1) * 360;
15+
directionAllDots = rem(directionAllDots, 360);
16+
17+
dots.directionAllDots = directionAllDots;
18+
19+
end

src/dot/updateDots.m

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@
99

1010
error(errorStruct);
1111
end
12-
13-
dots = reseedDots(dots, cfg);
14-
12+
1513
% Add one frame to the dot lifetime to each dot
1614
dots.time = dots.time + 1;
1715

16+
dots = reseedDots(dots, cfg);
17+
1818
end

tests/test_reseedDots.m

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
function test_suite = test_reseedDots %#ok<*STOUT>
2+
try % assignment of 'localfunctions' is necessary in Matlab >= 2016
3+
test_functions = localfunctions(); %#ok<*NASGU>
4+
catch % no problem; early Matlab versions can use initTestSuite fine
5+
end
6+
initTestSuite;
7+
end
8+
9+
function test_reseedDotsBasic()
10+
11+
dots.lifeTime = 100;
12+
cfg.screen.winWidth = 2000;
13+
cfg.dot.number = 5;
14+
cfg.dot.sizePix = 20;
15+
cfg.dot.proportionKilledPerFrame = 0;
16+
17+
cfg.fixation.widthPix = 20;
18+
19+
dots.positions = [ ...
20+
694, 100; % OK
21+
490, 2043; % out of frame
22+
-104, 392; % out of frame
23+
492, 402; % OK
24+
1000, 1000; % on the fixation cross
25+
];
26+
27+
dots.time = [...
28+
6; ... OK
29+
4 ; ... OK
30+
56; ... OK
31+
300; ... % exceeded its life time
32+
50]; % OK
33+
34+
dots = reseedDots(dots, cfg);
35+
36+
reseeded = [...
37+
6;
38+
1;
39+
1;
40+
1;
41+
1];
42+
43+
assertEqual(reseeded, dots.time)
44+
45+
end

0 commit comments

Comments
 (0)