-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathGRFStimuli.m
More file actions
567 lines (474 loc) · 19.2 KB
/
GRFStimuli.m
File metadata and controls
567 lines (474 loc) · 19.2 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
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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
/*
GRFStimuli.m
Stimulus generation for GaborRFMap
March 29, 2003 JHRM
*/
#import "GRF.h"
#import "GaborRFMap.h"
#import "GRFStimuli.h"
#import "UtilityFunctions.h"
#define kDefaultDisplayIndex 1 // Index of stim display when more than one display
#define kMainDisplayIndex 0 // Index of main stimulus display
#define kPixelDepthBits 32 // Depth of pixels in stimulus window
#define stimWindowSizePix 250 // Height and width of stim window on main display
#define kTargetBlue 0.0
#define kTargetGreen 1.0
#define kMidGray 0.5
#define kPI (atan(1) * 4)
#define kTargetRed 1.0
#define kDegPerRad 57.295779513
#define kAdjusted(color, contrast) (kMidGray + (color - kMidGray) / 100.0 * contrast)
NSString *stimulusMonitorID = @"GaborRFMap Stimulus";
@implementation GRFStimuli
- (void) dealloc;
{
[[task monitorController] removeMonitorWithID:stimulusMonitorID];
[taskStimList release];
[mapStimList0 release];
[mapStimList1 release];
[fixSpot release];
[targetSpot release];
[gabors release];
[super dealloc];
}
- (void)doFixSettings;
{
[fixSpot runSettingsDialog];
}
- (void)doGabor0Settings;
{
[[self taskGabor] runSettingsDialog];
}
- (void)dumpStimList;
{
StimDesc stimDesc;
long index;
NSLog(@"\ncIndex stim0Type stim1Type stimOnFrame stimOffFrame SF");
for (index = 0; index < [taskStimList count]; index++) {
[[taskStimList objectAtIndex:index] getValue:&stimDesc];
NSLog(@"%4ld:\t%d\t %ld %ld %.2f", index, stimDesc.stimType, stimDesc.stimOnFrame, stimDesc.stimOffFrame,
stimDesc.spatialFreqCPD);
NSLog(@"stim is %s", (stimDesc.stimType == kValidStim) ? "valid" :
((stimDesc.stimType == kTargetStim) ? "target" : "other"));
}
NSLog(@"\n");
}
- (void)erase;
{
[[task stimWindow] lock];
glClearColor(kMidGray, kMidGray, kMidGray, 0);
glClear(GL_COLOR_BUFFER_BIT);
[[NSOpenGLContext currentContext] flushBuffer];
[[task stimWindow] unlock];
}
- (id)init;
{
float frameRateHz = [[task stimWindow] frameRateHz];
if (!(self = [super init])) {
return nil;
}
monitor = [[[LLIntervalMonitor alloc] initWithID:stimulusMonitorID
description:@"Stimulus frame intervals"] autorelease];
[[task monitorController] addMonitor:monitor];
[monitor setTargetIntervalMS:1000.0 / frameRateHz];
taskStimList = [[NSMutableArray alloc] init];
mapStimList0 = [[NSMutableArray alloc] init];
mapStimList1 = [[NSMutableArray alloc] init];
// Create and initialize the visual stimuli
gabors = [[NSArray arrayWithObjects:[self initGabor:YES],
[self initGabor:NO], [self initGabor:NO], nil] retain];
[[gabors objectAtIndex:kMapGabor0] setAchromatic:YES];
[[gabors objectAtIndex:kMapGabor1] setAchromatic:YES];
fixSpot = [[LLFixTarget alloc] init];
[fixSpot bindValuesToKeysWithPrefix:@"GRFFix"];
targetSpot = [[LLFixTarget alloc] init];
//[targetSpot bindValuesToKeysWithPrefix:@"GRFFix"];
return self;
}
- (LLGabor *)initGabor:(BOOL)bindTemporalFreq;
{
static long counter = 0;
LLGabor *gabor;
gabor = [[LLGabor alloc] init]; // Create a gabor stimulus
[gabor setDisplays:[[task stimWindow] displays] displayIndex:[[task stimWindow] displayIndex]];
if (bindTemporalFreq) {
[gabor removeKeysFromBinding:[NSArray arrayWithObjects:LLGaborDirectionDegKey,
LLGaborTemporalPhaseDegKey, LLGaborContrastKey, LLGaborSpatialPhaseDegKey, nil]];
}
else {
[gabor removeKeysFromBinding:[NSArray arrayWithObjects:LLGaborDirectionDegKey, LLGaborTemporalPhaseDegKey,
LLGaborContrastKey, LLGaborSpatialPhaseDegKey, LLGaborTemporalFreqHzKey, nil]];
}
[gabor bindValuesToKeysWithPrefix:[NSString stringWithFormat:@"GRF%ld", counter++]];
return gabor;
}
/*
makeStimList()
Make stimulus lists for one trial. Three lists are made: one for the task gabor, and one each for the
mapping gabors at the two locations. Each list is constructed as an NSMutableArry of StimDesc or StimDesc
structures.
Task Stim List: The target in the specified targetIndex position (0 based counting).
Mapping Stim List: The list is constructed so that each stimulus type appears n times before any appears (n+1).
Details of the construction, as well as monitoring how many stimuli and blocks have been completed are handled
by mapStimTable.
*/
- (void)makeStimLists:(TrialDesc *)pTrial;
{
long targetIndex;
long stim, nextStimOnFrame, lastStimOffFrame = 0;
long stimDurFrames, interDurFrames, stimJitterPC, interJitterPC, stimJitterFrames, interJitterFrames;
long stimDurBase, interDurBase;
float frameRateHz;
StimDesc stimDesc;
LLGabor *taskGabor = [self taskGabor];
trial = *pTrial;
[taskStimList removeAllObjects];
targetIndex = MIN(pTrial->targetIndex, pTrial->numStim);
// Now we make a second pass through the list adding the stimulus times. We also insert
// the target stimulus (if this isn't a catch trial) and set the invalid stimuli to kNull
// if this is an instruction trial.
frameRateHz = [[task stimWindow] frameRateHz];
stimJitterPC = [[task defaults] integerForKey:GRFStimJitterPCKey];
interJitterPC = [[task defaults] integerForKey:GRFInterstimJitterPCKey];
stimDurFrames = ceil([[task defaults] integerForKey:GRFStimDurationMSKey] / 1000.0 * frameRateHz);
interDurFrames = ceil([[task defaults] integerForKey:GRFInterstimMSKey] / 1000.0 * frameRateHz);
stimJitterFrames = round(stimDurFrames / 100.0 * stimJitterPC);
interJitterFrames = round(interDurFrames / 100.0 * interJitterPC);
stimDurBase = stimDurFrames - stimJitterFrames;
interDurBase = interDurFrames - interJitterFrames;
/*
// randomize
if ([[task defaults] boolForKey:GRFRandTaskGaborDirectionKey]) {
[taskGabor setDirectionDeg:rand() % 180];
}
*/
pTrial->targetOnTimeMS = 0;
for (stim = nextStimOnFrame = 0; stim < pTrial->numStim; stim++) {
// Set the default values
stimDesc.gaborIndex = kTaskGabor;
stimDesc.sequenceIndex = stim;
stimDesc.stimType = kValidStim;
stimDesc.contrastPC = 100.0*[taskGabor contrast];
stimDesc.temporalFreqHz = [taskGabor temporalFreqHz];
stimDesc.azimuthDeg = [taskGabor azimuthDeg];
stimDesc.elevationDeg = [taskGabor elevationDeg];
stimDesc.sigmaDeg = [taskGabor sigmaDeg];
stimDesc.spatialFreqCPD = [taskGabor spatialFreqCPD];
stimDesc.directionDeg = [taskGabor directionDeg];
stimDesc.radiusDeg = [taskGabor radiusDeg];
stimDesc.temporalModulation = [taskGabor temporalModulation];
// If it's not a catch trial and we're in a target spot, set the target
if (!pTrial->catchTrial) {
if ((stimDesc.sequenceIndex == targetIndex) ||
(stimDesc.sequenceIndex > targetIndex && [[task defaults] boolForKey:GRFChangeRemainKey])) {
stimDesc.stimType = kTargetStim;
stimDesc.directionDeg += pTrial->orientationChangeDeg;
}
}
// Load the information about the on and off frames
stimDesc.stimOnFrame = nextStimOnFrame;
if (stimJitterFrames > 0) {
stimDesc.stimOffFrame = stimDesc.stimOnFrame +
MAX(1, stimDurBase + (rand() % (2 * stimJitterFrames + 1)));
}
else {
stimDesc.stimOffFrame = stimDesc.stimOnFrame + MAX(1, stimDurFrames);
}
lastStimOffFrame = stimDesc.stimOffFrame;
if (interJitterFrames > 0) {
nextStimOnFrame = stimDesc.stimOffFrame +
MAX(1, interDurBase + (rand() % (2 * interJitterFrames + 1)));
}
else {
nextStimOnFrame = stimDesc.stimOffFrame + MAX(0, interDurFrames);
}
// Set to null if HideTaskGaborKey is set
if ([[task defaults] boolForKey:GRFHideTaskGaborKey])
stimDesc.stimType = kNullStim;
// Put the stimulus descriptor into the list
[taskStimList addObject:[NSValue valueWithBytes:&stimDesc objCType:@encode(StimDesc)]];
// Save the estimated target on time
if (stimDesc.stimType == kTargetStim) {
pTrial->targetOnTimeMS = stimDesc.stimOnFrame / frameRateHz * 1000.0; // this is a theoretical value
}
}
// [self dumpStimList];
// The task stim list is done, now we need to get the mapping stim lists
[[(GaborRFMap*)task mapStimTable0] makeMapStimList:mapStimList0 index:0 lastFrame:lastStimOffFrame pTrial:pTrial];
[[(GaborRFMap*)task mapStimTable1] makeMapStimList:mapStimList1 index:1 lastFrame:lastStimOffFrame pTrial:pTrial];
}
- (void)loadGabor:(LLGabor *)gabor withStimDesc:(StimDesc *)pSD;
{
if (pSD->spatialFreqCPD == 0) { // Change made by Incheol and Kaushik to get gaussians
[gabor directSetSpatialPhaseDeg:90.0];
}
[gabor directSetSigmaDeg:pSD->sigmaDeg]; // *** Should be directSetSigmaDeg
[gabor directSetRadiusDeg:pSD->radiusDeg];
[gabor directSetAzimuthDeg:pSD->azimuthDeg elevationDeg:pSD->elevationDeg];
[gabor directSetSpatialFreqCPD:pSD->spatialFreqCPD];
[gabor directSetDirectionDeg:pSD->directionDeg];
[gabor directSetContrast:pSD->contrastPC / 100.0];
[gabor directSetTemporalFreqHz:pSD->temporalFreqHz];
if (pSD->temporalFreqHz == [[task stimWindow] frameRateHz]/2) { // [Vinay] - added this to adjust the temporal phase when the temporal frequency = (refresh rate)/2 (the maximum level allowed) so that each frame doesn't capture the zero level but the max and min levels every alternate frame
[gabor directSetTemporalPhaseDeg:90.0];
}
[gabor setTemporalModulation:pSD->temporalModulation];
}
- (void)clearStimLists:(TrialDesc *)pTrial
{
// tally stim lists first?
[mapStimList0 removeAllObjects];
[mapStimList1 removeAllObjects];
}
- (LLGabor *)mappingGabor0;
{
return [gabors objectAtIndex:kMapGabor0];
}
- (LLGabor *)mappingGabor1;
{
return [gabors objectAtIndex:kMapGabor1];
}
- (LLIntervalMonitor *)monitor;
{
return monitor;
}
- (void)presentStimSequence;
{
long index, trialFrame, taskGaborFrame;
NSArray *stimLists;
StimDesc stimDescs[kGabors], *pSD;
long stimIndices[kGabors];
long stimOffFrames[kGabors];
long gaborFrames[kGabors];
LLGabor *theGabor;
NSAutoreleasePool *threadPool;
BOOL listDone = NO;
// long stimCounter = 0;
BOOL useSingleITC18;
threadPool = [[NSAutoreleasePool alloc] init]; // create a threadPool for this thread
[LLSystemUtil setThreadPriorityPeriodMS:1.0 computationFraction:0.250 constraintFraction:1.0];
stimLists = [[NSArray arrayWithObjects:taskStimList, mapStimList0, mapStimList1, nil] retain];
// Set up the stimulus calibration, including the offset then present the stimulus sequence
[[task stimWindow] lock];
[[task stimWindow] setScaleOffsetDeg:[[task eyeCalibrator] offsetDeg]];
[[task stimWindow] scaleDisplay];
// Set up the gabors
[gabors makeObjectsPerformSelector:@selector(store)];
for (index = 0; index < kGabors; index++) {
stimIndices[index] = 0;
gaborFrames[index] = 0;
[[[stimLists objectAtIndex:index] objectAtIndex:0] getValue:&stimDescs[index]];
[self loadGabor:[gabors objectAtIndex:index] withStimDesc:&stimDescs[index]];
stimOffFrames[index] = stimDescs[index].stimOffFrame;
}
// Set up the targetSpot if needed
/*
if ([[task defaults] boolForKey:GRFAlphaTargetDetectionTaskKey]) {
[targetSpot setState:YES];
NSColor *targetColor = [[fixSpot foreColor]retain];
[targetSpot setForeColor:[targetColor colorWithAlphaComponent:[[task defaults] floatForKey:GRFTargetAlphaKey]]];
[targetSpot setOuterRadiusDeg:[[task defaults]floatForKey:GRFTargetRadiusKey]];
[targetSpot setShape:kLLCircle];
[targetColor release];
}
*/
targetOnFrame = -1;
for (trialFrame = taskGaborFrame = 0; !listDone && !abortStimuli; trialFrame++) {
glClear(GL_COLOR_BUFFER_BIT);
for (index = 0; index < kGabors; index++) {
if (trialFrame >= stimDescs[index].stimOnFrame && trialFrame < stimDescs[index].stimOffFrame) {
if (stimDescs[index].stimType != kNullStim) {
theGabor = [gabors objectAtIndex:index];
[theGabor directSetFrame:[NSNumber numberWithLong:gaborFrames[index]]]; // advance for temporal modulation
[theGabor draw];
/*
if (!trial.catchTrial && index == kTaskGabor && stimDescs[index].stimType == kTargetStim) {
[targetSpot setAzimuthDeg:stimDescs[index].azimuthDeg elevationDeg:stimDescs[index].elevationDeg];
[targetSpot draw];
}
*/
gaborFrames[index]++;
}
}
}
[fixSpot draw];
[[NSOpenGLContext currentContext] flushBuffer];
glFinish();
if (trialFrame == 0) {
[monitor reset];
}
else {
[monitor recordEvent];
}
// Update Gabors as needed
for (index = 0; index < kGabors; index++) {
pSD = &stimDescs[index];
// If this is the frame after the last draw of a stimulus, post an event declaring it off. We have to do this first,
// because the off of one stimulus may occur on the same frame as the on of the next
useSingleITC18 = [[task defaults] boolForKey:GRFUseSingleITC18Key];
if (trialFrame == stimOffFrames[index]) {
[[task dataDoc] putEvent:@"stimulusOff" withData:&index];
[[task dataDoc] putEvent:@"stimulusOffTime"];
if (!useSingleITC18) {
[digitalOut outputEvent:kStimulusOffDigitOutCode withData:index];
}
if (++stimIndices[index] >= [[stimLists objectAtIndex:index] count]) { // no more entries in list
listDone = YES;
}
}
// If this is the first frame of a Gabor, post an event describing it
if (trialFrame == pSD->stimOnFrame) {
[[task dataDoc] putEvent:@"stimulusOn" withData:&index];
[[task dataDoc] putEvent:@"stimulusOnTime"];
[[task dataDoc] putEvent:@"stimulus" withData:pSD];
if (!useSingleITC18) {
[digitalOut outputEvent:kStimulusOnDigitOutCode withData:index];
}
// put the digital events
if (index == kTaskGabor) {
[digitalOut outputEventName:@"taskGabor" withData:(long)(pSD->stimType)];
}
else {
if (pSD->stimType != kNullStim) {
if (index == kMapGabor0)
[digitalOut outputEventName:@"mapping0" withData:(long)(pSD->stimType)];
if (index == kMapGabor1)
[digitalOut outputEventName:@"mapping1" withData:(long)(pSD->stimType)];
}
}
// Other prperties of the Gabor
if (index == kMapGabor0 && pSD->stimType != kNullStim && !([[task defaults] boolForKey:GRFHideLeftDigitalKey])) {
//NSLog(@"Sending left digital codes...");
[digitalOut outputEventName:@"contrast" withData:(long)(10*(pSD->contrastPC))];
[digitalOut outputEventName:@"temporalFreq" withData:(long)(10*(pSD->temporalFreqHz))];
[digitalOut outputEventName:@"azimuth" withData:(long)(100*(pSD->azimuthDeg))];
[digitalOut outputEventName:@"elevation" withData:(long)(100*(pSD->elevationDeg))];
[digitalOut outputEventName:@"orientation" withData:(long)((pSD->directionDeg))];
[digitalOut outputEventName:@"spatialFreq" withData:(long)(100*(pSD->spatialFreqCPD))];
[digitalOut outputEventName:@"radius" withData:(long)(100*(pSD->radiusDeg))];
[digitalOut outputEventName:@"sigma" withData:(long)(100*(pSD->sigmaDeg))];
}
if (index == kMapGabor1 && pSD->stimType != kNullStim && !([[task defaults] boolForKey:GRFHideRightDigitalKey])) {
//NSLog(@"Sending right digital codes...");
[digitalOut outputEventName:@"contrast" withData:(long)(10*(pSD->contrastPC))];
[digitalOut outputEventName:@"temporalFreq" withData:(long)(10*(pSD->temporalFreqHz))];
[digitalOut outputEventName:@"azimuth" withData:(long)(100*(pSD->azimuthDeg))];
[digitalOut outputEventName:@"elevation" withData:(long)(100*(pSD->elevationDeg))];
[digitalOut outputEventName:@"orientation" withData:(long)((pSD->directionDeg))];
[digitalOut outputEventName:@"spatialFreq" withData:(long)(100*(pSD->spatialFreqCPD))];
[digitalOut outputEventName:@"radius" withData:(long)(100*(pSD->radiusDeg))];
[digitalOut outputEventName:@"sigma" withData:(long)(100*(pSD->sigmaDeg))];
}
if (pSD->stimType == kTargetStim) {
targetPresented = YES;
targetOnFrame = trialFrame;
if (!useSingleITC18) {
[digitalOut outputEvent:kTargetOnDigitOutCode withData:(kTargetOnDigitOutCode+1)];
}
}
stimOffFrames[index] = stimDescs[index].stimOffFrame; // previous done by now, save time for this one
}
// If we've drawn the current stimulus for the last time, load the Gabor with the next stimulus settings
if (trialFrame == stimOffFrames[index] - 1) {
if ((stimIndices[index] + 1) < [[stimLists objectAtIndex:index] count]) { // check there are more
[[[stimLists objectAtIndex:index] objectAtIndex:(stimIndices[index] + 1)] getValue:&stimDescs[index]];
[self loadGabor:[gabors objectAtIndex:index] withStimDesc:&stimDescs[index]];
gaborFrames[index] = 0;
}
}
}
}
// If there was no target (catch trial), we nevertheless need to set a valid targetOnFrame time (now)
targetOnFrame = (targetOnFrame < 0) ? trialFrame : targetOnFrame;
// Clear the display and leave the back buffer cleared
glClear(GL_COLOR_BUFFER_BIT);
[[NSOpenGLContext currentContext] flushBuffer];
glFinish();
[[task stimWindow] unlock];
// The temporal counterphase might have changed some settings. We restore these here.
[gabors makeObjectsPerformSelector:@selector(restore)];
stimulusOn = abortStimuli = NO;
[stimLists release];
[threadPool release];
}
- (void)setFixSpot:(BOOL)state;
{
[fixSpot setState:state];
if (state) {
if (!stimulusOn) {
[[task stimWindow] lock];
[[task stimWindow] setScaleOffsetDeg:[[task eyeCalibrator] offsetDeg]];
[[task stimWindow] scaleDisplay];
glClear(GL_COLOR_BUFFER_BIT);
[fixSpot draw];
[[NSOpenGLContext currentContext] flushBuffer];
[[task stimWindow] unlock];
}
}
}
// Shuffle the stimulus sequence by repeated passed along the list and paired substitution
- (void)shuffleStimListFrom:(short)start count:(short)count;
{
long rep, reps, stim, index, temp, indices[kMaxOriChanges];
NSArray *block;
reps = 5;
for (stim = 0; stim < count; stim++) { // load the array of indices
indices[stim] = stim;
}
for (rep = 0; rep < reps; rep++) { // shuffle the array of indices
for (stim = 0; stim < count; stim++) {
index = rand() % count;
temp = indices[index];
indices[index] = indices[stim];
indices[stim] = temp;
}
}
block = [taskStimList subarrayWithRange:NSMakeRange(start, count)];
for (index = 0; index < count; index++) {
[taskStimList replaceObjectAtIndex:(start + index) withObject:[block objectAtIndex:indices[index]]];
}
}
- (void)startStimSequence;
{
if (stimulusOn) {
return;
}
stimulusOn = YES;
targetPresented = NO;
[NSThread detachNewThreadSelector:@selector(presentStimSequence) toTarget:self
withObject:nil];
}
- (BOOL)stimulusOn;
{
return stimulusOn;
}
// Stop on-going stimulation and clear the display
- (void)stopAllStimuli;
{
if (stimulusOn) {
abortStimuli = YES;
while (stimulusOn) {};
}
else {
[stimuli setFixSpot:NO];
[self erase];
}
}
- (void)tallyStimLists:(long)count
{
[[(GaborRFMap *)task mapStimTable0] tallyStimList:mapStimList0 count:count];
[[(GaborRFMap *)task mapStimTable1] tallyStimList:mapStimList1 count:count];
}
- (long)targetOnFrame;
{
return targetOnFrame;
}
- (BOOL)targetPresented;
{
return targetPresented;
}
- (LLGabor *)taskGabor;
{
return [gabors objectAtIndex:kTaskGabor];
}
@end