Trial event figure maker
This function makes a picture like this:
I wrote this because I was too lazy to do it by hand :) You set it up using a task_specs structure illustrated as the default in the m-file. The stimulus displays would probably require most extra work of course.
As always, remember to save the figure in a suitable format (e.g. tiff) if you plan on ever having it printed!
function task_figure(varargin)
% function task_figure(task_specs, angle)
%
% task_specs.times [ms]
% task_specs.names
% task_specs.interval_names
% task_specs.stim
% task_specs.plotting.eventArrowLength;
% task_specs.plotting.screenSize;
% task_specs.plotting.plotside;
%
% Thomas Edward Gladwin, June 2006. thomasgladwin@hotmail.com
%
if length(varargin) >= 1,
task_specs = varargin{1};
else,
task_specs = [];
end;
if isempty(task_specs),
task_specs.times = [0 1500 3500 4250 5500];
task_specs.names{1} = 'Cue';
task_specs.names{2} = 'Stimulus';
task_specs.names{3} = 'End of trial';
task_specs.names{4} = 'Next trial';
task_specs.names{5} = [];
task_specs.interval_names{1} = 'Preparation interval';
task_specs.interval_names{2} = 'Response interval';
task_specs.interval_names{3} = 'Inter-trial interval';
task_specs.interval_names{4} = [];
task_specs.stim{1} = {'*' 'R'};
task_specs.stim{2} = {'L' 'R'};
task_specs.stim{3} = {' '};
task_specs.stim{4} = {'*' '*'};
task_specs.stim{5} = [];
task_specs.plotting.eventArrowLength = 0.1;
task_specs.plotting.screenSize = 0.1;
task_specs.plotting.plotside = 600;
end;
if length(varargin) >= 2,
angle = varargin{2};
else
angle = pi / 8;
end;
conditionNames = fieldnames(task_specs);
t0 = task_specs.times(1);
t_end = task_specs.times(end);
t_buffer = 0; %0.1 * (t_end - t0);
t0 = t0 - t_buffer;
t_end = t_end + t_buffer;
timeLineX = [t0 t_end * cos(angle)];
timeLineY = [t0 t_end * sin(angle)];
timeLineL = sqrt(diff(timeLineX) .^ 2 + diff(timeLineY) .^ 2);
figure(1); clf; hold on;
minx = timeLineX(1) - 0.1 * abs(diff(timeLineX));
maxx = timeLineX(2) + 0.1 * abs(diff(timeLineX));
miny = timeLineY(1) - 1.2 * task_specs.plotting.screenSize * timeLineL;
maxy = timeLineY(2) + 1.6 * task_specs.plotting.eventArrowLength * timeLineL;
XLim(sort([minx maxx]));
YLim(sort([miny maxy]));
set(gca, 'XTick', []);
set(gca, 'YTick', []);
fprintf('Now stretch to beautifulness\n');
dx = diff(xlim);
dy = diff(ylim);
pos = get(gcf, 'Position');
if dx > dy,
pos = [25 25 task_specs.plotting.plotside task_specs.plotting.plotside * dy / dx];
else,
pos = [25 25 task_specs.plotting.plotside * dx / dy task_specs.plotting.plotside];
end;
set(gcf, 'Position', pos);
time_line = plot(timeLineX, timeLineY, 'k-'); set(time_line, 'LineWidth', 2);
arrow(timeLineX, timeLineY, 0.1, pi/8, [0 0 0], '-', 2);
% Events
for iScreen = 1:length(task_specs.names),
screenX = timeLineX(1) + diff(timeLineX) * (task_specs.times(iScreen) + t_buffer) / (t_end + t_buffer);
screenY = timeLineY(1) + diff(timeLineY) * (task_specs.times(iScreen) + t_buffer)/ (t_end + t_buffer);
if ~isempty(task_specs.names{iScreen}),
x = [screenX screenX]
y = [screenY screenY + task_specs.plotting.eventArrowLength * timeLineL]
arrow(x, y, 0.2, pi/8, [0 0 0], '-', 1);
% once for extent...
t = text(x(2), y(2), task_specs.names{iScreen});
ext = get(t, 'Extent');
xshift = ext(3) / 2;
yshift = ext(4) / 2;
set(t, 'Visible', 'off');
t = text(x(2) - xshift, y(2) + yshift, task_specs.names{iScreen});
end;
end;
% interval names
for iScreen = 1:length(task_specs.interval_names)
screenX1 = timeLineX(1) + diff(timeLineX) * (task_specs.times(iScreen) + t_buffer) / (t_end + t_buffer);
screenY1 = timeLineY(1) + diff(timeLineY) * (task_specs.times(iScreen) + t_buffer)/ (t_end + t_buffer);
screenX2 = timeLineX(1) + diff(timeLineX) * (task_specs.times(iScreen + 1) + t_buffer) / (t_end + t_buffer);
screenY2 = timeLineY(1) + diff(timeLineY) * (task_specs.times(iScreen + 1) + t_buffer)/ (t_end + t_buffer);
screenX = mean([screenX1 screenX2]);
screenY = mean([screenY1 screenY2]);
if ~isempty(task_specs.interval_names{iScreen}),
screenSize0 = task_specs.plotting.screenSize * timeLineL;
intArrowLength = 1.5 * screenSize0;
x = [screenX screenX + cos(pi / 4) * intArrowLength];
y = [screenY screenY - sin(pi / 4) * intArrowLength];
arrow(x, y, 0.2, pi/8, [0 0 0], '-', 1);
% once for extent...
t = text(x(2), y(2), task_specs.interval_names{iScreen});
ext = get(t, 'Extent');
xshift = ext(3) / length(task_specs.interval_names{iScreen});
yshift = ext(4) / length(task_specs.interval_names{iScreen});
set(t, 'Visible', 'off');
t = text(x(2) + xshift, y(2) - yshift, task_specs.interval_names{iScreen});
end;
end;
% screens
for iScreen = length(task_specs.names):(-1):1,
screenX = timeLineX(1) + diff(timeLineX) * (task_specs.times(iScreen) + t_buffer) / (t_end + t_buffer);
screenY = timeLineY(1) + diff(timeLineY) * (task_specs.times(iScreen) + t_buffer)/ (t_end + t_buffer);
if ~isempty(task_specs.stim{iScreen}),
screenSize0 = task_specs.plotting.screenSize * timeLineL;
pos = [screenX - screenSize0 / 2 screenY - screenSize0 screenSize0 screenSize0];
verticesX = [pos(1); pos(1) + pos(3); pos(1) + pos(3); pos(1)];
verticesY = [pos(2); pos(2); pos(2) + pos(4); pos(2) + pos(4)];
fill(verticesX, verticesY, [1 1 1]);
rect0 = rectangle('Position', pos);
nStim = length(task_specs.stim{iScreen});
for iStim = 1:nStim,
x0 = pos(1) + pos(3) / 2;
dStim = 0.8 * pos(4) / nStim;
y0 = (pos(2) + 0.9 * pos(4)) - dStim * iStim;
% once for extent...
t = text(x0, y0, task_specs.stim{iScreen}{iStim});
ext = get(t, 'Extent');
xshift = ext(3) / 2;
yshift = ext(4) / 2;
set(t, 'Visible', 'off');
t = text(x0 - xshift, y0 + yshift, task_specs.stim{iScreen}{iStim});
end;
end;
end;
function arrow(varargin)
% function arrow(x0, y0, r, angle, arrowheadsize, arrowheadangle, colorvec, linestyle, linewidth)
% function arrow(x_comp, y_comp, arrowheadsize, arrowheadangle, colorvec, linestyle, linewidth)
%
% x_comp and y_comp must contain two values.
% Trailing plotting parameters may be ommitted. Any plotting parameter can be specified as [] for defaults.
axes0 = gca;
if length(varargin{1}) == 1,
x0 = varargin{1};
y0 = varargin{2};
r = varargin{3};
angle = varargin{4};
x_comp = [x0 x0 + r * cos(angle)];
y_comp = [x0 x0 + r * sin(angle)];
plotParIndex = 5;
else,
x_comp = varargin{1};
y_comp = varargin{2};
r = sqrt(diff(x_comp) .^ 2 + diff(y_comp) .^ 2);
angle = atan2(diff(y_comp), diff(x_comp));
plotParIndex = 3;
end;
if length(varargin) >= plotParIndex,
headSize = varargin{plotParIndex} * r;
if isempty(headSize ),
headSize = 0.1 * r;
end;
else,
headSize = 0.1 * r;
end;
if length(varargin) >= plotParIndex + 1,
headAngle = varargin{plotParIndex + 1};
if isempty(headAngle),
headAngle = pi / 8;
end;
else,
headAngle = pi / 8;
end;
if length(varargin) >= plotParIndex + 2,
color = varargin{plotParIndex + 2};
if isempty(color),
color = [0 0 0];
end;
else,
color = [0 0 0];
end;
if length(varargin) >= plotParIndex + 3,
linestyle = varargin{plotParIndex + 3};
if isempty(linestyle),
linestyle = '-';
end;
else,
linestyle = '-';
end;
if length(varargin) >= plotParIndex + 4,
linewidth = varargin{plotParIndex + 4};
if isempty(linewidth),
linewidth = 0.1;
end;
else,
linewidth = 0.1;
end;
holding = get(axes0, 'NextPlot');
if strcmp(holding, 'replace') == 1,
cla;
end;
%set(axes0, 'NextPlot', 'add');
% The main line
drawLine(x_comp, y_comp, color, linestyle, linewidth);
% The head: draw on origin on a lying arrow, then rotate and translate.
headPoint = getHeadPoint(angle, headSize, headAngle, x_comp, y_comp);
drawLine([x_comp(2) headPoint(1)], [y_comp(2) headPoint(2)], color, linestyle, linewidth);
headPoint = getHeadPoint(angle, headSize, -headAngle, x_comp, y_comp);
drawLine([x_comp(2) headPoint(1)], [y_comp(2) headPoint(2)], color, linestyle, linewidth);
% whitespace if necessary
xlim1 = [min(x_comp - headSize) max(x_comp + headSize)];
ylim1 = [min(y_comp - headSize) max(y_comp + headSize)];
xlim0 = get(gca, 'XLim');
ylim0 = get(gca, 'YLim');
xlim2 = [min([xlim0(1); xlim1(1)]) max([xlim0(2); xlim1(2)])];
ylim2 = [min([ylim0(1); ylim1(1)]) max([ylim0(2); ylim1(2)])];
set(gca, 'XLim', xlim2);
set(gca, 'YLim', ylim2);
%set(axes0, 'NextPlot', holding);
function line0 = drawLine(x_comp, y_comp, color, linestyle, linewidth);
line0 = line(x_comp, y_comp);
set(line0, 'Color', color);
set(line0, 'LineStyle', linestyle);
set(line0, 'LineWidth', linewidth);
function headPoint = getHeadPoint(angle, headSize, headAngle, x_comp, y_comp)
headPoint = headSize * [-cos(headAngle); sin(headAngle)];
rotation_matrix = [cos(angle) -sin(angle); sin(angle) cos(angle)];
headPoint = rotation_matrix * headPoint + [x_comp(2); y_comp(2)];