function handles = scope(name, data, xData)
% display data channels in a scrollable window
% scope(chanData)
% scope(name, chanData)
% scope(chanData, timeData)
% scope(name, chanData, timeData)
% Use the left and right arrows on the keyboard to pan left and right (when
% zoomed) and the up and down arrows to increase and decrease the
% horizontal zoom. Press 'ctrl' key for small scrolls. Pressing 'a' will
% autoscale the horizontal axis. Similarly, 'A' will autoscale the vertical
% axis and pressing shift in conjunction with the up and down arrows will
% zoom the vertical axis of the currently selected axis.
%
% example:
% scopeHandles = scope([sin(0.1:0.1:20*pi); cos(0.1:0.1:20*pi)]);
% scopeHandles = scope('Waves', [sin(0.1:0.1:20*pi); cos(0.1:0.1:20*pi)], 0.1:0.1:20*pi);
% left click and drag on the axes to zoom in, right click to zoom out.
% clicking on the display will set or unset the cursors.
% the text box of the bottom left corner is the zoom factor
% parse input
switch nargin
case 1
data = name;
name = 'Scope';
xData = 1:length(data);
case 2
if ischar(name)
xData = 1:length(data);
else
xData = data;
data = name;
name = 'Scope';
end
case 3
% do nothing
otherwise
disp('Improper input')
end
if size(data, 1) < size(data, 2)
data = data';
end
if size(data) == 1
noData = 1;
data = ones(1,data);
else
noData = 0;
end
%initialize plotting window
set(0,'Units','normal');
figure('NumberTitle','off',...
'Name', name,...
'menu', 'none',...
'Units','normal',...
'Position',[0 .025 1 .95],...
'Visible', 'on',...
'UserData', -100,...
'windowButtonDownFcn', @mouseDownScope,...
'WindowButtonMotionFcn', @movePointers,...
'windowButtonUpFcn', @mouseUpScope,...
'keyPressFcn', @keyPress);
clear control_info;
uicontrol('Style','slider','Units','normal','Position', [0 0 1 .015], 'value', 0, 'sliderStep', [1 1/0], 'callback', @traceScroll);
uicontrol('Style','edit','Units','normal','Position',[0 .018 .02 .02], 'string', '1', 'callback', @traceScroll);
for index = 1:size(data, 2);
handles(index) = axes('Position', [.05 .05 + (index - 1) / (size(data, 2) / .95) .95 .95 / size(data, 2)], 'nextplot', 'add');
startLine = line([1 1], [0 1], 'color', [0 0 0], 'userData', 0);
stopLine = line([0 0], [0 1], 'color', [0 0 0]);
lineText = text(1, 1, '0', 'color', [0 0 0], 'VerticalAlignment', 'bottom', 'linestyle', 'none');
text(0, 1, '0', 'visible', 'off', 'VerticalAlignment', 'bottom', 'color', [0 0 0], 'linestyle', 'none')
if index > 1
set(gca, 'xticklabel', '', 'xtick', []);
end
if ~noData
line(xData, data(:, size(data, 2) + 1 - index));
if max(data(:, size(data, 2) + 1 - index)) > min(data(:, size(data, 2) + 1 - index))
set(gca, 'ylim', [min(data(:, size(data, 2) + 1 - index)) max(data(:, size(data, 2) + 1 - index))]);
else
set(gca, 'ylim', [data(1, size(data, 2) + 1 - index) - 1 data(1, size(data, 2) + 1 - index) + 1]);
end
set(gca, 'xlim', [min(xData) max(xData)], 'userData', lineText);
set(startLine, 'yData', get(gca, 'ylim'));
set(stopLine, 'yData', get(gca, 'ylim'));
end
end
set(gcf, 'userData', 0);
setappdata(gcf, 'xlim', [min(xData) max(xData)]);
function mouseDownScope(varargin)
pointerLoc = get(gcf, 'CurrentPoint');
imageLoc = get(gca, 'Position');
if pointerLoc(1) > imageLoc(1) && pointerLoc(1) < imageLoc(1) + imageLoc(3) && pointerLoc(2) > .05
switch get(gcf, 'SelectionType')
case 'normal' %left mouse button clicked
kids = get(gcf, 'children');
firstKid = get(kids(1), 'children');
if get(firstKid(length(firstKid)), 'userData') == 0
for index = 1:length(kids) - 2
kidLines = get(kids(index), 'children');
set(kidLines(length(kidLines)), 'color', [1 0 0]);
end
set(firstKid(length(firstKid)), 'userData', 1);
else
for index = 1:length(kids) - 2
kidLines = get(kids(index), 'children');
set(kidLines(length(kidLines)), 'color', [0 0 0]);
set(kidLines(length(kidLines) - 1), 'xData', [min(get(kids(index), 'xlim')) min(get(kids(index), 'xlim'))]);
set(kidLines(length(kidLines) - 3), 'visible', 'off')
end
set(firstKid(length(firstKid)), 'userData', 0);
end
case 'extend' %middle mouse button clicked
case 'alt' %right mouse button clicked
case 'open' %double click
end
elseif pointerLoc(1) <= imageLoc(1) && pointerLoc(2) > 0.05
kids = get(gcf, 'children');
whichAxis = fix((pointerLoc(2) - .05) / .95 * (length(kids) - 2)) + 1;
switch get(gcf, 'SelectionType')
case 'normal' %left mouse button clicked
set(gcf, 'userData', [whichAxis pointerLoc(2)]);
uicontrol('units', 'normal', 'Style', 'text', 'String', '', 'backgroundColor', [0 0 1], 'Position', [0, pointerLoc(2), .01, 0.0001]);
case 'extend' %middle mouse button clicked
case 'alt' %right mouse button clicked
case 'open' %double click
end
elseif pointerLoc(1) > imageLoc(1) && pointerLoc(1) < imageLoc(1) + imageLoc(3) && pointerLoc(2) <= .05
%kids = get(gcf, 'children');
switch get(gcf, 'SelectionType')
case 'normal' %left mouse button clicked
set(gcf, 'userData', [-1 pointerLoc(1)]);
uicontrol('units', 'normal', 'Style', 'text', 'String', '', 'backgroundColor', [0 0 1], 'Position', [pointerLoc(1), .015, 0.0001, .01]);
case 'extend' %middle mouse button clicked
case 'alt' %right mouse button clicked
case 'open' %double click
end
end
function movePointers(varargin)
if get(gcf, 'userData') ~= -100
pointerLoc = get(gcf, 'CurrentPoint');
imageLoc = get(gca, 'Position');
if length(get(gcf, 'userData')) > 1
info = get(gcf, 'userData');
blueRect = get(gcf, 'children');
blueRect = blueRect(1);
if info(1) == -1
if pointerLoc(1) > info(2)
set(blueRect, 'position', [info(2), .015, pointerLoc(1) - info(2), .01]);
elseif pointerLoc(1) < info(2)
set(blueRect, 'position', [pointerLoc(1), .015, info(2) - pointerLoc(1), .01]);
end
else
if pointerLoc(2) > info(2)
set(blueRect, 'position', [0, info(2), .01, pointerLoc(2) - info(2)]);
elseif pointerLoc(2) < info(2)
set(blueRect, 'position', [0, pointerLoc(2), .01, info(2) - pointerLoc(2)]);
end
end
elseif pointerLoc(1) > imageLoc(1) && pointerLoc(1) < imageLoc(1) + imageLoc(3) && pointerLoc(2) > .05
set(gcf, 'pointer', 'crosshair');
kids = get(gcf, 'children');
firstKid = get(kids(1), 'children');
whichAxis = fix((pointerLoc(2) - .05) / .95 * (length(kids) - 2)) + 1;
axisKids = get(kids(length(kids) - 1 - whichAxis), 'children');
xCoord = (pointerLoc(1) - .05) / .95 * diff(get(gca,'Xlim')) + min(get(gca,'Xlim'));
xData = get(axisKids(end - 4), 'xData');
[junk whereX] = min(abs(xCoord - xData));
% yCoord = min(abs(yCoords(xCoord) - round(((pointerLoc(2) - .05 - (whichAxis - 1) * (.95 / (length(kids) - 2))) / (.95 / (length(kids) - 2))) * diff(get(kids(length(kids) - 1 - whichAxis),'ylim')) + min(get(kids(length(kids) - 1 - whichAxis),'ylim')))));
if get(firstKid(length(firstKid)), 'userData') == 0
for index = 1:length(kids) - 2
kidLines = get(kids(index), 'children');
set(kidLines(length(kidLines)), 'xData', [xData(whereX) xData(whereX)], 'yData', get(kids(index), 'ylim'));
yData = get(kidLines(length(kidLines) - 4), 'yData');
yBounds = get(kids(index), 'ylim');
if abs(yBounds(1) - yData(whereX)) > abs(yBounds(2) - yData(whereX))
whereY = yBounds(1) + .2 * diff(yBounds);
else
whereY = yBounds(2) - .2 * diff(yBounds);
end
set(kidLines(length(kidLines) - 2), 'position', [xData(whereX) whereY], 'string', [' \bf' num2str(xData(whereX)) ', ' num2str(yData(whereX))]);
end
else
for index = 1:length(kids) - 2
kidLines = get(kids(index), 'children');
set(kidLines(length(kidLines) - 1), 'xData', [xData(whereX) xData(whereX)], 'yData', get(kids(index), 'ylim'));
yData = get(kidLines(length(kidLines) - 4), 'yData');
firstPos = get(kidLines(length(kidLines) - 2), 'position');
yBounds = get(kids(index), 'ylim');
if abs(yBounds(1) - yData(whereX)) > abs(yBounds(2) - yData(whereX))
whereY = yBounds(1) + .1 * diff(yBounds);
else
whereY = yBounds(2) - .1 * diff(yBounds);
end
set(kidLines(length(kidLines) - 3), 'position', [xData(whereX) whereY], 'string', [' \bf \Delta' num2str(xData(whereX)- firstPos(1)) ', \Delta' num2str(yData(whereX) - yData(1 + round(firstPos(1) / diff(xData(1:2)))))], 'visible', 'on');
end
end
elseif pointerLoc(1) <= imageLoc(1) && pointerLoc(2) > 0.05
set(gcf, 'pointer', 'top');
elseif pointerLoc(1) > imageLoc(1) && pointerLoc(1) < imageLoc(1) + imageLoc(3) && pointerLoc(2) <= .05
set(gcf, 'pointer', 'right');
end
if pointerLoc(2) < .015
set(gcf, 'pointer', 'arrow');
end
end
function mouseUpScope(varargin)
pointerLoc = get(gcf, 'CurrentPoint');
imageLoc = get(gca, 'Position');
if length(get(gcf, 'userData')) > 1
info = get(gcf, 'userData');
set(gcf, 'userData', 0);
blueRect = get(gcf, 'children');
delete(blueRect(1));
kids = get(gcf, 'children');
firstKid = get(kids(1), 'children');
kidData = get(firstKid(end - 4), 'xData');
if info(1) == -1
if info(2) ~= pointerLoc(1)
myPoint = (pointerLoc(1) - .05) / .95 * diff(get(kids(1),'Xlim')) + min(get(kids(1),'Xlim'));
if myPoint < min(kidData)
myPoint = min(kidData);
end
if myPoint > max(kidData)
myPoint = max(kidData);
end
set(kids(1:length(kids) - 2), 'xlim', sort([(info(2) - .05) / .95 * diff(get(kids(1),'Xlim')) + min(get(kids(1),'Xlim')) myPoint]));
set(kids(length(kids) - 1), 'string', num2str((max(kidData) - min(kidData)) / diff(get(kids(1), 'Xlim')),'%-1.1f'));
xBounds = get(kids(1), 'xlim');
zoomFactor = str2double(get(kids(length(kids) - 1), 'string'));
newStep = 1 / zoomFactor / (1- 1 / zoomFactor);
if newStep > 10
set(kids(length(kids)), 'sliderStep', [1 newStep]);
else
set(kids(length(kids)), 'sliderStep', [newStep / 10 newStep]);
end
set(kids(length(kids)), 'value', xBounds(1) / max(kidData) / (1- 1 / zoomFactor));
end
else
if info(2) ~= pointerLoc(2)
set(kids(length(kids) - 1 - info(1)), 'ylim', sort((([info(2) pointerLoc(2)] - .05 - (info(1) - 1) * (.95 / (length(kids) - 2))) / (.95 / (length(kids) - 2))) * diff(get(kids(length(kids) - 1 - info(1)),'ylim')) + min(get(kids(length(kids) - 1 - info(1)),'ylim'))));
end
end
elseif strcmp(get(gcf, 'SelectionType'), 'alt')
kids = get(gcf, 'children');
if pointerLoc(1) <= imageLoc(1) && pointerLoc(2) > 0.05
whichAxis = fix((pointerLoc(2) - .05) / .95 * (length(kids) - 2)) + 1;
lineKids = get(kids(length(kids) - 1 - whichAxis), 'children');
tempXData = get(lineKids(1), 'xData');
tempYData = get(lineKids(1), 'yData');
tempBounds = get(kids(length(kids) - 1 - whichAxis), 'xlim');
minBound = find(tempXData == min(tempBounds));
maxBound = find(tempXData == max(tempBounds));
set(lineKids(length(lineKids)), 'yData', [min(tempYData(minBound:maxBound)) max(tempYData(minBound:maxBound))]);
set(lineKids(length(lineKids) - 1), 'yData', [min(tempYData(minBound:maxBound)) max(tempYData(minBound:maxBound))]);
set(kids(length(kids) - 1 - whichAxis), 'YLimMode', 'auto');
elseif pointerLoc(1) > imageLoc(1) && pointerLoc(1) < imageLoc(1) + imageLoc(3) && pointerLoc(2) <= .05
set(kids(1:length(kids) - 2), 'xlim', getappdata(gcf, 'xlim'));
set(kids(length(kids) - 1), 'string', '1.0');
set(kids(length(kids)), 'sliderStep', [1 1/0]);
end
end
function keyPress(varargin)
kids = get(gcf, 'children');
switch varargin{2}.Key
case 'uparrow'
if ~isempty(varargin{2}.Modifier) && strcmp(varargin{2}.Modifier, 'shift')
% zoom the y axis
set(gca, 'ylim', get(gca, 'ylim') / 2 - mean(get(gca, 'ylim')))
else
% zoom the x axis
set(kids(end - 1), 'string', sprintf('%0.0f', str2double(get(kids(end - 1), 'string')) * 2));
traceScroll;
end
case 'downarrow'
if ~isempty(varargin{2}.Modifier) && strcmp(varargin{2}.Modifier, 'shift')
% zoom the y axis
set(gca, 'ylim', get(gca, 'ylim') * 2 - mean(get(gca, 'ylim')))
else
% zoom the x axis
set(kids(end - 1), 'string', sprintf('%0.0f', max([1 str2double(get(kids(end - 1), 'string')) / 2])));
traceScroll;
end
case 'leftarrow'
sliderStep = get(kids(end), 'sliderStep');
if ~isempty(varargin{2}.Modifier) && strcmp(varargin{2}.Modifier, 'control')
% little nudge for ctrl
set(kids(end), 'value', max([0 get(kids(end), 'value') - sliderStep(1)]));
else
% big nudge for normal
set(kids(end), 'value', max([0 get(kids(end), 'value') - sliderStep(2)]));
end
traceScroll;
case 'rightarrow'
sliderStep = get(kids(end), 'sliderStep');
if ~isempty(varargin{2}.Modifier) && strcmp(varargin{2}.Modifier, 'control')
% little nudge for ctrl
set(kids(end), 'value', min([1 get(kids(end), 'value') + sliderStep(1)]));
else
% big nudge for normal
set(kids(end), 'value', min([1 get(kids(end), 'value') + sliderStep(2)]));
end
traceScroll;
case 'a'
if ~isempty(varargin{2}.Modifier) && strcmp(varargin{2}.Modifier, 'shift')
% set y axis to full scale
axisKids = get(gca, 'children');
set(axisKids(end - 1:end), 'visible', 'off')
set(gca, 'yLimMode', 'auto');
set(axisKids(end - 1:end), 'yData', get(gca, 'ylim'), 'visible', 'on')
else
% set x axis to full scale
set(kids(1:end - 2), 'xlim', getappdata(gcf, 'xlim'));
set(kids(end - 1), 'string', '1.0');
set(kids(end), 'sliderStep', [1 1/0]);
end
end
function traceScroll(varargin)
kids = get(gcf, 'children');
firstKid = get(kids(1), 'children');
xData = get(firstKid(end - 4), 'xdata');
zoomFactor = str2double(get(kids(end - 1), 'string'));
scrollValue = get(kids(length(kids)), 'value') * (1- 1 / zoomFactor);
windowSize = (xData(end) - xData(1)) / zoomFactor;
newStep = 1 / zoomFactor / (1- 1 / zoomFactor);
if newStep > 10
set(kids(length(kids)), 'sliderStep', [1 newStep]);
else
set(kids(length(kids)), 'sliderStep', [newStep / 10 newStep]);
end
set(kids(1:length(kids) - 2), 'Xlim', [xData(end) * scrollValue xData(end) * scrollValue + windowSize], 'dataaspectratiomode', 'auto', 'plotboxaspectratiomode', 'auto');