Search code examples
matlabmatlab-figurelogarithm

Plot patches of same (visual) size on logarithmic scale


I would like to mark some peaks in a graph which has a logarithmic x-axis. I was thinking of using patch since I need a callback that responds when the user clicks on the marker. The Vertices property of patch is in units of data, not pixels. However, I'd like the patches to appear with the same visual width and height, but I cannot find a way to compute the width (in units of data) that leads to the same width (in pixels).

I thought it could not be too complicated, but I got totally stuck and maybe tried too much to see the easy answer.

I know this is not purely related to Matlab, but I'm happy to use something else than patch, as long as it provides the functionality to respond on a mouse click.

peakpos = [50 500 5000];
peakheight = [5000 7500 10000];

x = 0:5e4;
y = zeros(numel(x),1);
for k = 1:3
    y(peakpos(k)) = peakheight(k);
end

ax = axes(figure());
plot(ax, x,y);
ax.XScale = 'log';
ax.Units = 'pixels';

dataperpx_y = diff(ax.YLim)/ax.Position(4);

ax.YLim = [0 1e4+30 * dataperpx_y];
ax.NextPlot = 'add';
for k = 1:3
    patch(ax, ...
        'Faces', [1 2 3], ...
        'Vertices', ...
            [ x(peakpos(k)) peakheight(k)+5*dataperpx_y
              x(peakpos(k)) - 25 peakheight(k)+15*dataperpx_y  
              x(peakpos(k)) + 25 peakheight(k)+15*dataperpx_y 
            ], ...
        'ButtonDownFcn', @(h,e)display(h.Vertices));
end

Solution

  • Okay, as I thought, it was not too complicated. I do not know where exactly I got stuck, but one thing which was a little bit confusing was that setting the XScale property of an axes to log makes the axes use decadic logarithms, while the log() function is the natural logarithm.

    After plotting log10(x) at a linear scale, the solution was quite obvious:

    peakpos = [50 500 5000];
    peakheight = [5000 7500 10000];
    
    x = 0:5e4;
    y = zeros(numel(x),1);
    for k = 1:3
        y(peakpos(k)) = peakheight(k);
    end
    
    ax = subplot(2,1,1);
    
    plot(ax, log10(x),y);
    %ax.XScale = 'log';
    ax.Units = 'pixels';
    
    dataperpx_y = diff(ax.YLim)/ax.Position(4);
    dataperpx_x = diff(ax.XLim)/ax.Position(3);
    ax.YLim = [0 1e4+30 * dataperpx_y];
    ax.NextPlot = 'add';
    for k = 1:3
        patch(ax, ...
            'Faces', [1 2 3], ...
            'Vertices', ...
                [ log10(x(peakpos(k))) peakheight(k)+5*dataperpx_y
                  log10(x(peakpos(k))) - 5*dataperpx_x peakheight(k)+15*dataperpx_y  
                  log10(x(peakpos(k))) + 5*dataperpx_x peakheight(k)+15*dataperpx_y 
                ], ...
            'ButtonDownFcn', @(h,e)display(h.Vertices));
    end
    
    ax = subplot(2,1,2);
    
    plot(ax, x, y);
    ax.XScale = 'log';
    ax.Units = 'pixels';
    dataperpx_y = diff(ax.YLim)/ax.Position(4);
    dataperpx_x = diff(log10(ax.XLim))/ax.Position(3);
    ax.YLim = [0 1e4+30 * dataperpx_y];
    ax.NextPlot = 'add';
    
    for k = 1:3
        patch(ax, ...
            'Faces', [1 2 3], ...
            'Vertices', ...
                [ x(peakpos(k)) peakheight(k)+5*dataperpx_y
                  x(peakpos(k)) / 10^(5*dataperpx_x) peakheight(k)+15*dataperpx_y  
                  x(peakpos(k)) * 10^(5*dataperpx_x) peakheight(k)+15*dataperpx_y 
                ], ...
            'ButtonDownFcn', @(h,e)display(h.Vertices));
    end
    

    enter image description here