Code covered by the BSD License  

Highlights from
UISIGNALBUILDER

image thumbnail

UISIGNALBUILDER

by

 

22 Apr 2009 (Updated )

A visual tool that allows to build signals and save them to workspace variables

Editor's Notes:

This file was selected as MATLAB Central Pick of the Week

uisignalbuilder()
function uisignalbuilder()
% UISIGNALBUILDER Signal Builder GUI
%
% UISIGNALBUILDER allows the user to create, delete and drag control points in
% order to build a signal using different interpolation methods.
%
% Useful commands:
%  - double-click on a blank spot to create a control point
%  - double-click on an existing control point to delete it
%  - right-click on a control point to specify its coordinates manually
%  - drag control points to adjust interpolation
%
% Example:
%  >> uisignalbuilder()
%
% See also :
%
%
% Author: Laurent VAYLET
% Release: 1.0
% Release date: 04/22/2009
%
% Copyright 2009 - Laurent VAYLET
%

% Create controls
% Do not specify positions as GridBagLayout will take care of the layout later
hFig = figure('Name', 'Signal Builder', ...
    'NumberTitle', 'off', ...
    'MenuBar', 'none', ...
    'Color', 'w');

hAxes = axes('Parent', hFig, ...
    'ButtonDownFcn', @(src,evt)axesbuttondown());

hHelp = uicontrol(hFig, ...
    'Style', 'pushbutton', ...
    'BackgroundColor', 'w', ...
    'String', 'Help', ...
    'Callback', @(srv,evt)showhelp());

hOptions = uipanel(hFig, 'Title', ' Options ', 'FontWeight', 'bold', 'BackgroundColor', 'w', 'ForegroundColor', [0 0 0.5]);
hInterpMethodsLbl = uicontrol(hOptions, ...
    'Style', 'text', ...
    'BackgroundColor', 'w', ...
    'String', 'Interpolation method:', ...
    'HorizontalAlignment', 'left');
hInterpMethods = uicontrol(hOptions, ...
    'Style', 'popup', ...
    'BackgroundColor', 'w', ...
    'String', {'linear', 'spline', 'pchip', 'nearest'}, ...
    'Callback', @(src,evt)changeinterpmethod(), ...
    'ToolTipString', 'Method used when interpolating');
hSamplingPeriodLbl = uicontrol(hOptions, ...
    'Style', 'text', ...
    'BackgroundColor', 'w', ...
    'String', 'Sampling period (s):', ...
    'HorizontalAlignment', 'left');
hSamplingPeriod = uicontrol(hOptions, ...
    'Style', 'edit', ...
    'BackgroundColor', 'w', ...
    'String', '0.01', ...
    'Callback', @(src,evt)changesamplingperiod(src), ...
    'ToolTipString', 'Sampling period (s)');
hSignalLengthLbl = uicontrol(hOptions, ...
    'Style', 'text', ...
    'BackgroundColor', 'w', ...
    'String', 'Signal length (s):', ...
    'HorizontalAlignment', 'left');
hSignalLength = uicontrol(hOptions, ...
    'Style', 'edit', ...
    'BackgroundColor', 'w', ...
    'String', '6', ...
    'Callback', @(src,evt)changesignallength(src), ...
    'ToolTipString', 'Signal length (s)');
hSnapGrid = uicontrol(hOptions, ...
    'Style', 'checkbox', ...
    'BackgroundColor', 'w', ...
    'String', 'Snap To Grid', ...
    'Value', 1, ...
    'Callback', @(src,evt)togglesnapgrid());
hSave = uicontrol(hOptions, ...
    'Style', 'pushbutton', ...
    'BackgroundColor', 'w', ...
    'String', 'Save...', ...
    'Value', 1, ...
    'Callback', @(src,evt)save(), ...
    'ToolTipString', 'Save interpolated signal to workspace');

% Layout controls using GridBagLayout
figureLayout = layout.GridBagLayout(hFig, 'HorizontalGap', 15, 'VerticalGap', 15);
figureLayout.add(hAxes, [1 2], 1, 'Fill', 'Both');
figureLayout.add(hHelp, 1, 2, 'Fill', 'Horizontal', 'MinimumHeight', 25);
figureLayout.add(hOptions, 2, 2, 'Fill', 'Vertical', 'MinimumWidth', 130);
figureLayout.VerticalWeights = [0 1];
figureLayout.HorizontalWeights = [1 0];
figureLayout.setConstraints([1 2], 1, ... % hAxes
    'LeftInset', 25, ...
    'BottomInset', 25, ...
    'RightInset', 15, ...
    'TopInset', 15);

optionsLayout = layout.GridBagLayout(hOptions, 'HorizontalGap', 15, 'VerticalGap', 0);
optionsLayout.add(hInterpMethodsLbl,  1, 1, 'Fill', 'Horizontal');
optionsLayout.add(hInterpMethods,     2, 1, 'Fill', 'Horizontal');
optionsLayout.add(hSamplingPeriodLbl, 3, 1, 'Fill', 'Horizontal');
optionsLayout.add(hSamplingPeriod,    4, 1, 'Fill', 'Horizontal');
optionsLayout.add(hSignalLengthLbl,   5, 1, 'Fill', 'Horizontal');
optionsLayout.add(hSignalLength,      6, 1, 'Fill', 'Horizontal');
optionsLayout.add(hSnapGrid,          7, 1, 'Fill', 'Horizontal', 'Anchor', 'North');
optionsLayout.add(hSave,              8, 1, 'Fill', 'Horizontal', 'MinimumHeight', 30, 'Anchor', 'South');
optionsLayout.VerticalWeights = [0 0 0 0 0 0 1 0];
optionsLayout.HorizontalWeights = 1;
optionsLayout.setConstraints(1, 1, ... % hInterpMethodsLbL
    'TopInset', 30);
optionsLayout.setConstraints(3, 1, ... % hSamplingPeriodLbL
    'TopInset', 15);
optionsLayout.setConstraints(5, 1, ... % hSignalLengthLbL
    'TopInset', 15);
optionsLayout.setConstraints(7, 1, ... % hSnapGrid
    'TopInset', 15);
optionsLayout.setConstraints(8, 1, ... % hSave
    'BottomInset', 15);

% Perform some initializations
hMarkers = []; % handles to markers
hSignal  = []; % handle to interpolated signal

% Add some default markers
hold(hAxes, 'all')
signalLength = str2double(get(hSignalLength, 'String'));
addmarker(0:signalLength, zeros(1,7));

% Freeze axis
axis tight
ylim([-3 3])
axis equal
axis manual % freeze axis

% Add a grid
xLim = xlim(hAxes);
yLim = ylim(hAxes);
xGridInc = 0.2;
yGridInc = 0.2;
xGrid = xLim(1):xGridInc:xLim(2);
yGrid = yLim(1):yGridInc:yLim(2);
[XGrid,YGrid] = meshgrid(xGrid, yGrid);
hGrid = plot(XGrid, YGrid, ...
    'Color', [.6 .6 .6], ...
    'LineStyle', 'none', ...
    'Marker', '.', ...
    'MarkerSize', 1, ...
    'HitTest', 'off');
snapGrid = true;

% Update plot
updateplot()

% -------------------------------------------------------------------------

    function selectmarker(hMarker)
        
        switch get(hFig, 'SelectionType')
            
            case 'normal' % left click: drag
                set(hFig, 'WindowButtonMotionFcn', @(src,evt)drag(hMarker), ...
                    'WindowButtonUpFcn',@(src,evt)releasemarker());
                
            case 'open' % double click: delete
                if length(hMarkers) > 2
                    delete(hMarker);
                    hMarkers(hMarkers == hMarker) = [];
                    updateplot()
                else
                    warning('UISIGNALBUILDER:TwoMarkersNeeded', 'Cannot delete this marker. At least two markers are needed.');
                end
                
            case 'alt' % right click: change coordinates
                prompt = {'X:', 'Y:'};
                dlgTitle = 'Coordinates?';
                numLines = 1;
                defaults = {num2str(get(hMarker, 'XData')), ...
                    num2str(get(hMarker, 'YData'))};
                answer = inputdlg(prompt, dlgTitle, numLines, defaults);
                if ~isempty(answer)
                    set(hMarker, 'XData', str2double(answer{1}), ...
                        'YData', str2double(answer{2}));
                    updateplot()
                end
        end
        
    end

% -------------------------------------------------------------------------

    function [x,y] = getclosestgridpoint(x,y)
        
        x = xGrid(abs(xGrid - x) <= xGridInc/2);
        y = yGrid(abs(yGrid - y) <= yGridInc/2);
        
    end

% -------------------------------------------------------------------------

    function drag(hMarker)
        
        cp = get(hAxes, 'CurrentPoint');
        x = cp(1,1);
        y = cp(1,2);
        if snapGrid
            [x, y] = getclosestgridpoint(x, y);
        end
        set(hMarker, 'XData', x, 'YData', y, 'ZData', -1);
        updateplot()
        
    end

% -------------------------------------------------------------------------

    function releasemarker()
        
        set(hFig,'WindowButtonMotionFcn',[]);
        
    end

% -------------------------------------------------------------------------

    function updateplot()
        
        idxMethod = get(hInterpMethods, 'Value');
        availableMethods = get(hInterpMethods, 'String');
        chosenMethod = availableMethods{idxMethod};
        
        x = cell2mat(get(hMarkers, 'XData'));
        [x, sortOrder] = sort(x);
        y = cell2mat(get(hMarkers, 'YData'));
        y = y(sortOrder);
        
        % Use specified sampling period for interpolating values
        xi = xLim(1):str2double(get(hSamplingPeriod, 'String')):xLim(2);
        
        try
            yi = interp1(x, y, xi, chosenMethod);
        catch me
            if strcmp(me.identifier, 'MATLAB:interp1:RepeatedValuesX')
                yi = NaN(size(xi)); % plot nothing
            end
        end
        
        % Plot if signal does not exist yet, update it otherwise
        if ~isempty(hSignal)
            set(hSignal, 'XData', xi, 'YData', yi, 'Visible', 'on');
        else
            hSignal = plot(xi, yi, 'b', 'HitTest', 'off');
        end
        
    end

% -------------------------------------------------------------------------

    function axesbuttondown()
        
        if strcmp(get(hFig, 'SelectionType'), 'open') % double click
            cp = get(hAxes, 'CurrentPoint');
            addmarker(cp(1,1), cp(1,2));
            updateplot();
        end
        
    end

% -------------------------------------------------------------------------

    function addmarker(x,y)
        
        error(nargchk(0, 2, nargin))
        
        for k = 1:length(x)
            hMarkers(end+1) = plot3(x(k), y(k), -1, 'o', ...
                'MarkerEdgeColor', 'b', ...
                'MarkerFaceColor', 'y', ...
                'MarkerSize', 8, ...
                'ButtonDownFcn', @(src,evt)selectmarker(src)); %#ok<AGROW>
        end
        
    end

% -------------------------------------------------------------------------

    function changeinterpmethod()
        
        updateplot()
        
    end

% -------------------------------------------------------------------------

    function togglesnapgrid()
        
        snapGrid = ~snapGrid;
        
    end

% -------------------------------------------------------------------------

    function save()
        
        checkLabels = {'Save X coordinates to variable named:' ...
            'Save Y coordinates to variable named:'};
        varNames = {'x', 'y'};
        items = {get(hSignal, 'XData'), get(hSignal, 'YData')};
        export2wsdlg(checkLabels, varNames, items, 'Save to Workspace');
        
    end

% -------------------------------------------------------------------------

    function changesamplingperiod(src)
        
        % Get new sampling period
        newValue = str2double(get(src, 'String'));
        % Validate
        if isempty(newValue)
            warning('UISIGNALBUILDER:InvalidSamplingPeriod', 'Invalid value for sampling period. Resetting to 0.01.');
            newValue = 0.01;
            set(src, 'String', num2str(newValue));
        end
        
        updateplot()
        
    end

% -------------------------------------------------------------------------

    function changesignallength(src)
        
        % Get new sampling period
        newValue = str2double(get(src, 'String'));
        % Validate
        if isempty(newValue)
            warning('UISIGNALBUILDER:InvalidSignalLength', 'Invalid value for signal length. Resetting to 6.');
            newValue = 6;
            set(src, 'String', num2str(newValue));
        end
        
        xlim(hAxes, [0 newValue])
        updategrid()
        updateplot()
        
    end

% -------------------------------------------------------------------------

    function updategrid()
        
        delete(hGrid)
        xLim = xlim(hAxes);
        xGrid = xLim(1):xGridInc:xLim(2);
        yGrid = yLim(1):yGridInc:yLim(2);
        [XGrid,YGrid] = meshgrid(xGrid, yGrid);
        hGrid = plot(XGrid, YGrid, ...
            'Color', [.6 .6 .6], ...
            'LineStyle', 'none', ...
            'Marker', '.', ...
            'MarkerSize', 1, ...
            'HitTest', 'off');
        
    end

% -------------------------------------------------------------------------

    function showhelp()
        
        mes = {' - Drag a marker to change its position', ...
            ' - Double-click anywhere to create a marker', ...
            ' - Double-click on a marker to delete it', ...
            ' - Right-click on a marker to fine tune its position'};
        uiwait(msgbox(mes,'Help','help','modal'))
        
    end

% -------------------------------------------------------------------------

end

Contact us