-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathAnimatedCalibrationDisplay.m
148 lines (136 loc) · 6.2 KB
/
AnimatedCalibrationDisplay.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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
classdef AnimatedCalibrationDisplay < handle
properties (Access=private, Constant)
calStateEnum = struct('undefined',0, 'moving',1, 'shrinking',2 ,'waiting',3);
end
properties (Access=private)
calState;
currentPoint;
lastPoint;
moveStartT;
shrinkStartT;
oscillStartT;
moveDuration;
scrSize;
end
properties
doShrink = true;
shrinkTime = 0.5;
doMove = true;
moveTime = 1; % for whole screen distance, duration will be proportionally shorter when dot moves less than whole screen distance
doOscillate = true;
oscillatePeriod = 1.5;
fixBackSizeMax = 50;
fixBackSizeMaxOsc = 35;
fixBackSizeMin = 15;
fixFrontSize = 5;
fixBackColor = 0;
fixFrontColor = 255;
bgColor = 127;
end
methods
function obj = AnimatedCalibrationDisplay()
obj.setCleanState();
% get size of screen. NB: i'm assuming only one screen is
% attached (more than one is usually a bad idea for timing on
% Windows!)
obj.scrSize = Screen('Rect',0); obj.scrSize(1:2) = [];
end
function setCleanState(obj)
obj.calState = obj.calStateEnum.undefined;
obj.currentPoint= nan(1,3);
obj.lastPoint= nan(1,3);
end
function qAllowAcceptKey = doDraw(obj,wpnt,currentPoint,pos,~)
% if called with nan as first input, this is a signal that
% calibration/validation is done, and cleanup can occur if
% wanted
if isnan(wpnt)
obj.setCleanState();
return;
end
% check point changed
curT = GetSecs; % instead of using time directly, you could use the last input to this function to animate based on call sequence number to this function
if any(obj.currentPoint(2:3)~=pos)
if obj.doMove && ~isnan(obj.currentPoint(1))
obj.calState = obj.calStateEnum.moving;
obj.moveStartT = curT;
% dot should move at constant speed regardless of
% distance to cover, moveTime contains time to move
% over width of whole screen. Adjust time to proportion
% of screen covered by current move
dist = hypot(obj.currentPoint(2)-pos(1),obj.currentPoint(3)-pos(2));
obj.moveDuration = obj.moveTime*dist/obj.scrSize(1);
elseif obj.doShrink
obj.calState = obj.calStateEnum.shrinking;
obj.shrinkStartT = curT;
else
obj.calState = obj.calStateEnum.waiting;
obj.oscillStartT = curT;
end
obj.lastPoint = obj.currentPoint;
obj.currentPoint = [currentPoint pos];
end
% check state transition
if obj.calState==obj.calStateEnum.moving && (curT-obj.moveStartT)>obj.moveDuration
if obj.doShrink
obj.calState = obj.calStateEnum.shrinking;
obj.shrinkStartT = curT;
else
obj.calState = obj.calStateEnum.waiting;
obj.oscillStartT = curT;
end
elseif obj.calState==obj.calStateEnum.shrinking && (curT-obj.shrinkStartT)>obj.shrinkTime
obj.calState = obj.calStateEnum.waiting;
obj.oscillStartT = curT;
end
% determine current point position
if obj.calState==obj.calStateEnum.moving
frac = (curT-obj.moveStartT)/obj.moveDuration;
curPos = obj.lastPoint(2:3).*(1-frac) + obj.currentPoint(2:3).*frac;
else
curPos = obj.currentPoint(2:3);
end
% determine current point size
if obj.calState==obj.calStateEnum.moving
sz = [obj.fixBackSizeMax obj.fixFrontSize];
elseif obj.calState==obj.calStateEnum.shrinking
dSize = obj.fixBackSizeMax-obj.fixBackSizeMin;
frac = 1 - (curT-obj.shrinkStartT)/obj.shrinkTime;
sz = [obj.fixBackSizeMin + frac.*dSize obj.fixFrontSize];
else
if obj.doOscillate
dSize = obj.fixBackSizeMaxOsc-obj.fixBackSizeMin;
phase = cos((curT-obj.oscillStartT)/obj.oscillatePeriod*2*pi);
if obj.doShrink
frac = 1-(phase/2+.5); % start small
else
frac = phase/2+.5; % start big
end
sz = [obj.fixBackSizeMin + frac.*dSize obj.fixFrontSize];
else
sz = [obj.fixBackSizeMin obj.fixFrontSize];
end
end
% determine if we're ready to accept the user pressing the
% accept calibration point button. User should not be able to
% press it if point is not yet at the final position
qAllowAcceptKey = obj.calState~=obj.calStateEnum.moving;
% draw
Screen('FillRect',wpnt,obj.bgColor);
obj.drawAFixPoint(wpnt,curPos,sz);
end
end
methods (Access = private, Hidden)
function drawAFixPoint(obj,wpnt,pos,sz)
% draws Thaler et al. 2012's ABC fixation point
for p=1:size(pos,1)
rectH = CenterRectOnPointd([0 0 sz ], pos(p,1), pos(p,2));
rectV = CenterRectOnPointd([0 0 fliplr(sz)], pos(p,1), pos(p,2));
Screen('gluDisk', wpnt,obj. fixBackColor, pos(p,1), pos(p,2), sz(1)/2);
Screen('FillRect',wpnt,obj.fixFrontColor, rectH);
Screen('FillRect',wpnt,obj.fixFrontColor, rectV);
Screen('gluDisk', wpnt,obj. fixBackColor, pos(p,1), pos(p,2), sz(2)/2);
end
end
end
end