Graphic adapters (BoxGraphic, CircleGraphic, PieGraphic, PolygonGraphic, TextGraphic, ImageGraphic, MovieGraphic, SineGrating, RandomDotMotion and GraphicContainer) are special in that they do not have a Success or stop condition. They simply pass the Success or stop status of the child adapter to the parent. (In contrast, other stimulus adapters, such as AudioSound and Stimulator, succeed when the stimulation is completed.) It allows the graphic adapters to be inserted almost anywhere in a chain. See the following codes.
box = BoxGraphic(eye_);
box.List = { [1 1 1],
|
fix = SingleTarget(eye_);
fix.Target = [0 0];
fix.Threshold = 3;
box = BoxGraphic(fix);
box.List = { [1 1 1],
|
fix = SingleTarget(eye_);
fix.Target = [0 0];
fix.Threshold = 3;
wth = WaitThenHold(fix);
wth.WaitTime = 5000;
wth.HoldTime = 500;
box = BoxGraphic(wth);
box.List = { [1 1 1],
|
In these three examples, BoxGraphic is inserted in a different part of the chain but they all work. However, inserting graphic adapters in the middle of a chain makes it difficult to reuse the adapters and decreases the code readability. The better practice is to separate the behavior monitoring and the stimulus presentation as below. In that way, the same graphic adapter can be recycled in another chain.
box = BoxGraphic(null_); % Use NullTracker to free BoxGraphic from behavior tracking
box.List = { [1 1 1], [1 0 0], 1, [0 0] };
fix = SingleTarget(eye_); % Only SingleTarget and WaitThenHold are associated with EyeTracker
fix.Target = box; % Use the graphic adapter as a target
fix.Threshold = 3;
wth = WaitThenHold(fix);
wth.WaitTime = 5000;
wth.HoldTime = 500;
scene = create_scene(wth);
run_scene(scene);
One graphic adapter can contain multiple objects.
box = BoxGraphic(null_);
box.List = { [1 1 1], [1 0 0], 1, [5 5]; ... % This adapter has 4 squares.
[1 1 1], [0 1 0], 1, [-5 5]; ...
[1 1 1], [0 0 1], 1, [-5 -5]; ...
[1 1 1], [1 1 0], 1, [5 -5]; ...
};
In this case, the length of each adapter property is increased, according to the number of objects, and the property of each object can be manipulated independently.
>> box.Enable
ans =
4×1 logical array
1
1
1
1
|
>> box.Position
ans =
5 5
-5 5
-5 -5
5 -5
|
>> box.Scale
ans =
1 1
1 1
1 1
1 1
|
>> box.Zorder
ans =
0
0
0
0
|
>> box.Enable([2 4]) = false; % Hide the 2nd and 4th squares
>> box.Scale(3,:) = [2 1]; % Increase the width of the 3rd box twice
A multi-object adapter can be used to indicate multiple targets.
box = BoxGraphic(null_); % 4 squares
box.List = { [1 1 1], [1 0 0], 1, [5 5]; [1 1 1], [0 1 0], 1, [-5 5]; ...
[1 1 1], [0 0 1], 1, [-5 -5]; [1 1 1], [1 1 0], 1, [5 -5] };
mul = MultiTarget(eye_);
mul.Target = box; % The same as mul.setTarget(box);
mul.Threshold = 3;
mul.WaitTime = 5000;
mul.HoldTime = 500;
scene = create_scene(mul);
run_scene(scene);
All adapters that have the Target property also have the setTarget() method. The optional index argument of setTarget() allows choosing a subset of objects in an adapter.
mul.setTarget(box,[1 3]); % Use only the 1st and 3rd boxes
Use GraphicContainer to compose complex graphics from multiple adapters. Note that the add() method of GraphicContainer also has the optional index argument.
box = BoxGraphic(null_); box.List = { [1 1 1], [1 0 0], 1, [5 5] };
crc = CircleGraphic(null_); crc.List = { [1 1 1], [0 1 0], 1, [-5 5] };
pie = PieGraphic(null_); pie.List = { [1 0 0], [1 1 0], 1, [-5 -5], 45, 270 };
img = ImageGraphic(null_); img.List = { 'A.bmp' , [0 0]; 'B.bmp' , [5 -5] };
gc = GraphicContainer(box); % Aggregate all
gc.add(crc);
gc.add(pie);
gc.add(img,2); % Add only B.bmp
In the scene framework, adapters are linked as a chain to do complex jobs (for example, acquiring eye positions + checking eye fixation). In addition, multiple chains can be combined into a macro chain with aggregator adapters such as AndAdapter, OrAdapter, AllContinue and AnyContinue. When creating a macro chain, it is even more critical to think through what is the condition that the chain has to meet to finish the scene.
The code snippet below is an example of a macro chain composed of two child chains: one that checks button press (colored in red) and one that monitors eye positions (in blue). SingleButton and SingleTarget succeed when the button #1 is pressed and the eye is on (-5, 5), respectively, and OrAdapter succeeds when any child chain succeeds. Therefore, the scene ends when either button press or eye fixation is acquired.
btn = SingleButton(button_); % Child chain 1: ButtonTracker + SingleButton
btn.Button = 1;
fix = SingleTarget(eye_); % Child chain 2: EyeTracker + SingleTarget
fix.Target = [-5 5];
fix.Threshold = 3;
or = OrAdapter(btn); % Aggregator: Child chain 1 + Child chain 2
or.add(fix);
scene = create_scene(or);
run_scene(scene);
Since the above scene does not have a timer component, it waits for user input indefinitely. We can add WaitThenHold to the second chain, to make the scene finish after a certain period, like the following.
btn = SingleButton(button_);
btn.Button = 1;
fix = SingleTarget(eye_); % Child chain 2: EyeTracker + SingleTarget + WaitThenHold
fix.Target = [-5 5];
fix.Threshold = 3;
wth = WaitThenHold(fix);
wth.WaitTime = 3000; % Wait for eye fixation up to 3 s
wth.HoldTime = 0;
or = OrAdapter(btn);
or.add(wth);
scene = create_scene(or);
run_scene(scene);
It may look perfect now, but this scene will not end even if there is no user input for 3 s. It is because WaitThenHold may stop with or without a success (i.e., with or without eye fixation) but stopping without a success does not satisfy OrAdapter.
To make the scene work as we intended, we need to use a different aggregator that watches the stop states of child chains, instead of the success states.
btn = SingleButton(button_);
btn.Button = 1;
fix = SingleTarget(eye_);
fix.Target = [-5 5];
fix.Threshold = 3;
wth = WaitThenHold(fix);
wth.WaitTime = 3000;
wth.HoldTime = 0;
ac = AllContinue(btn); % New aggregator
ac.add(wth);
scene = create_scene(ac);
run_scene(scene);
AllContinue stops when any of its child chains stops, regardless of their success states. Therefore the scene will end whether WaitThenHold is finished by eye fixation or by time out. AndAdapter and OrAdapter monitor the success states of their child chains; AllContinue and AnyContinue, the stop states.
As shown in these examples, it should be considered carefully whether to use the success state or the stop state to finish a scene when designing a complex macro chain. The success and stop conditions of each adapter are explained in the timing script function manual. Also check out other special aggregators, such as Concurrent and Sequential, in the manual.