Repository navigation
Expand file tree
/
Copy pathFractControllerBalmMotion.j
More file actions
121 lines (104 loc) · 4.39 KB
/
Copy pathFractControllerBalmMotion.j
File metadata and controls
121 lines (104 loc) · 4.39 KB
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
/*
This file is part of FrACT10, a vision test battery.
© 2021 Michael Bach, bach@uni-freiburg.de, <https://michaelbach.de>
2025-03-05 created class "FractControllerBalmMotion" inheriting from "FractController"
*/
@import "FractControllerBalm.j"
@import "SoundManager.j"
@implementation FractControllerBalmMotion: FractControllerBalm {
float motionOffset;
float radiusInPix;
float speedInPixPerSec;
float dotCenterDist;
float motionJumpBack;
BOOL isMoving;
id animationRequestID;
id animationTimeStamp;
int dotgridNY;
int dotgridNX;
id dotsX;
id dotsY;
}
- (void) runStart { //console.info("FractControllerBalmMotion>runStart");
nAlternatives = 4; nTrials = [Settings nTrials04];
animationRequestID = 0;
radiusInPix = 0.5 * [MiscSpace pixelFromDegree: [Settings balmMotionDiameterInDeg]];
if (radiusInPix > 0.1 * Math.min(viewWidth, viewHeight)) {
[self alertProblemOfDiameter: [Settings balmMotionDiameterInDeg]];
}
speedInPixPerSec = [MiscSpace pixelFromDegree: [Settings balmSpeedInDegPerSec]];
dotCenterDist = radiusInPix * 4;
dotgridNX = Math.ceil((3 * viewWidth) / dotCenterDist); //create x/y arrays for the dots
dotgridNY = Math.ceil((3 * viewHeight) / dotCenterDist);
dotsX = [dotgridNX]; //allocate the dots position arrays
for (let i = 0; i < dotgridNX; i++) dotsX[i] = [dotgridNY];
dotsY = [dotgridNX];
for (let i = 0; i < dotgridNX; i++) dotsY[i] = [dotgridNY];
[super runStart];
}
- (void) trialStart {
[super trialStart];
const xsPerScreen = Math.ceil(dotgridNX / 3), ysPerScreen = Math.ceil(dotgridNY / 3);
const jitter = dotCenterDist / 2;
for (let x = 0; x < dotgridNX; x++) {
for (let y = 0; y < dotgridNY; y++) {
dotsX[x][y] = (x - xsPerScreen) * dotCenterDist + jitter * (Math.random() - 0.5);
dotsY[x][y] = (y - ysPerScreen) * dotCenterDist + jitter * (Math.random() - 0.5);
}
}
}
- (void) drawAllDotsDx: (float) dx dy: (float) dy {
const er = extentInPix + radiusInPix;
for (let x = 0; x < dotgridNX; x++) {
for (let y = 0; y < dotgridNY; y++) {
const dotX = dotsX[x][y] + dx, dotY = dotsY[x][y] + dy;
const inside = (dotX > -er) && (dotX < er) && (dotY > -er) && (dotY < er);
if (inside) [optotypes fillCircleAtX: dotX y: dotY radius: radiusInPix];
}
}
}
- (void) drawStimulusInRect: (CGRect) dirtyRect forView: (FractView) fractView { //console.info("FractControllerBalmMotion>drawStimulusInRect, state: ", state);
trialInfoString = [self composeTrialInfoString];
[self prepareDrawing];
switch(state) {
case kStateDrawBack:
[self drawAllDotsDx: 0 dy: 0];
break;
case kStateDrawFore://console.info("kStateDrawFore");
if (!isMoving) { //detect first time
[[SoundManager sharedManager] playSound: kSoundTrialStart];
isMoving = YES; motionOffset = 0; animationTimeStamp = -1;
discardKeyEntries = NO; //now allow responding
}
let dx, dy;
switch ([alternativesGenerator currentAlternative]) {
case 0: dx = motionOffset; dy = 0; break;
case 2: dx = 0; dy = -motionOffset; break;
case 4: dx = - motionOffset; dy = 0; break;
case 6: dx = 0; dy = motionOffset; break;
}
[self drawAllDotsDx: dx dy: dy];
animationRequestID = window.requestAnimationFrame(function(timeStamp) {
//console.info("frameAnimation", timeStamp)
if (isMoving) {
if (animationTimeStamp < 0) animationTimeStamp = timeStamp;
const deltaTSecs = (timeStamp - animationTimeStamp) / 1000;
animationTimeStamp = timeStamp;
motionOffset += speedInPixPerSec * deltaTSecs;
if (motionOffset > viewWidthHalf) motionOffset -= viewWidthHalf;
[fractView display];
}
})
break;
default: break;
}
CGContextRestoreGState(cgc);
CGContextSetFillColor(cgc, gColorBack);
[super drawStimulusInRect: dirtyRect forView: fractView];
}
- (void) trialEnd { //console.info("FractControllerBalmMotion>trialEnd");
isMoving = NO;
window.cancelAnimationFrame(animationRequestID);
[super trialEnd];
}
@end