Question

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
Was it helpful?

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 ^_^

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top