Code covered by the BSD License  

Highlights from
Scrollable Data Display

image thumbnail
from Scrollable Data Display by Phil Larimer
A scrollable, zoomable, multichannel data display.

scope(name, data, xData)
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');

Contact us