Search code examples
matlabgraphcoordinatesdrawcurve

Draw log graph curve on Matlab by clicking?


I'd like to draw a curve on an empty (semilog-y) graph by clicking the points I want it to run through, on the X-Y plane.

Is there a function for this?

edit: I'm trying to do this by obtaining the position of last pointer click -

axis([0 3000 0 1000]); 
co=get(gcf, 'CurrentPoint'); 

It seems to return the cursor position at the time of execution, but it does not change later.

edit2: Here's what works for me. The actual drawing I can do by using the arrays of points collected.

clear
clc
h=plot(0);
grid on;

xlim([0 3000]);
ylim([0 1000]);
datacursormode on;

% Enlarge figure to full screen.
screenSize = get(0,'ScreenSize');
set(gcf, 'units','pixels','outerposition', screenSize);
hold on;

% Print the x,y coordinates - will be in plot coordinates
x=zeros(1,10); y=zeros(1,10);
for p=1:10;

[x(p),y(p)] = ginput(1) ;

% Mark where they clicked with a cross.
plot(x(p),y(p), 'r+', 'MarkerSize', 20, 'LineWidth', 3);

% Print coordinates on the plot.
label = sprintf('(%.1f, %.1f)', x(p), y(p));
text(x(p)+20, y(p), label);

end

Solution

  • Not really, but now there is:

    function topLevel
    
        %// parameters
        xrange = [0 100];
        yrange = [1e-4 1e4];
    
        %// initialize figure, plot    
        figure, clf, hold on
        plot(NaN, NaN);
        axis([xrange yrange]);
        set(gca, 'YScale', 'log')
        t = text(sum(xrange)/2, sum(yrange)/2, ...
            '<< Need at least 3 points >>',...
            'HorizontalAlignment', 'center');
    
        %// Main loop
        xs = [];  p = [];
        ys = [];  P = [];
        while true
    
            %// Get new user-input, and collect all of them in a list
            [x,y] = ginput(1);
            xs = [xs; x]; %#ok<AGROW>
            ys = [ys; y]; %#ok<AGROW>
    
            %// Plot the selected points
            if ishandle(p)
                delete(p); end        
            p = plot(xs, ys, 'rx');
            axis([xrange yrange]);
    
            %// Fit curve through user-injected points
            if numel(xs) >= 3
    
                if ishandle(t)
                    delete(t); end
    
                %// Get parameters of best-fit in a least-squares sense
                [A,B,C] = fitExponential(xs,ys);
    
                %// Plot the new curve
                xp = linspace(xrange(1), xrange(end), 100);
                yp = A + B*exp(C*xp);            
                if ishandle(P)
                    delete(P); end
                P = plot(xp,yp, 'b');            
    
            end               
        end
    
        %// Fit a model of the form  y = A + B·exp(C·x)  to data [x,y]
        function [A, B, C] = fitExponential(x,y)
    
            options = optimset(...
                'maxfunevals', inf);
    
            A = fminsearch(@lsq, 0, options);
            [~,B,C] = lsq(A);
    
            function [val, B,C] = lsq(A)
    
                params = [ones(size(x(:))) x(:)] \ log(abs(y-A));
    
                B = exp(params(1));
                C = params(2);
    
                val = sum((y - A - B*exp(C*x)).^2);
    
            end
    
        end
    
    end
    

    Note that as always, fitting an exponential curve can be tricky; the square of the difference between model and data is exponentially much greater for higher data values than for lower data values, so there will be a strong bias to fit the higher values better than the lower ones.

    I just assumed a simple model and used a simple solution, but this gives a biased curve which might not be "optimal" in the sense that you need it to be. Any decent solution really depends on what you want specifically, and I'll leave that up to you ^_^