Search code examples
matlabplotgraphicsmatlab-figure

CapSize for a customized errorbar plot


I am writing a customized errorbar function for myself. But I do not know how to control the CapSize, like it is controlled in the default errorbar; zooming-in or zooming-out does not enlarge the Cap. A simplified version of my code is as follow-

function myErrorbar(x, y, h)
for i = 1:length(x)
    y1 = y(i)-h(i);
    y2 = y(i)+h(i);

    x1 = x(i)-0.1*h(i);
    x2 = x(i)+0.1*h(i);
    % errorbar
    line([x(i), x(i)], [y1, y2]); hold on 
    % caps
    line([x1, x2], [y1, y1])
    line([x1, x2], [y2, y2])
end

In the above code I have fixed the size of caps equal to 10% of h on either sides. I want to control the capsize like it could be done in the default. The code could be tested with following code

x = 1:10:100;
y = [20 30 45 40 60 65 80 75 95 90];
err = 8*ones(size(y));
myErrorbar(x,y,err)

Solution

  • Like Adriaan mentioned in his comment, this can be achieved by adding a listener to the XLim property of the axes to draw to. See the code below and comments for explanation.

    The idea is to get the XLim after drawing the vertical lines, then determine the width fraction per cap of the axes' XLim, and use this to scale the caps accordingly when the XLim is changed.

    function myErrorbar(ax, x, y, err, color)
    
        % color input argument handling
        if ~exist('color', 'var') || isempty(color)
            color = lines(1); % default lightblue color
        end
    
        % first plot the vertical lines (so XLim is set to right value)
        y_bot = y - err;
        y_top = y + err;
        % errorbar
        l = line([x; x], [y_bot; y_top], 'color', color);
        hold on
    
        % get the current XLim
        x_fracs = NaN(size(x)); % variable to store fractions of XLim
        cur_xlim = diff(ax.XLim); % current XLim
    
        % plot the caps
        x_left = x - 0.1 .* err;
        x_right = x + 0.1 .* err;
    
        c_top = line([x_left; x_right], [y_top; y_top], 'color', color);
        c_bot = line([x_left; x_right], [y_bot; y_bot], 'color', color);
    
        % determine width fraction of current x limit
        x_fracs = (x_right - x_left) ./ cur_xlim;
    
        % add listener for xlim
        addlistener(ax, 'XLim', 'PostGet', @updateCaps);
    
        % --------------------------------------------------------------------------
    
        % callback to update cap width
        function updateCaps(hProperty, eventData)
    
            % update XLim
            cur_xlim = diff(ax.XLim);
    
            % determine new cap widths and positions
            cap_width = x_fracs .* cur_xlim;
            x_left = x - 0.5 .* cap_width;
            x_right = x + 0.5 .* cap_width;
    
            % set cap line x and y data
            for k = 1:length(x)
                c_top(k).XData = [x_left(k), x_right(k)];
                c_bot(k).XData = [x_left(k), x_right(k)];
            end
        end
    end