Matlab onclick callback to execute function - matlab

I want Matlab to execute a function that takes the specific point I clicked on as an input, so for example, if I plot
plot(xy(:,1),xy(:,2))
scatter(xy(:,1),xy(:,2))
and then click on a specific point (see figure), it will execute a callback function whose inputs are not only the x,y coordinate of that point but also its index value (ie its the 4th row of variable xy)
Thanks alot!

This can be done by using the ButtonDownFcn property of Scatter objects.
In the main script:
% --- Define your data
N = 10;
x = rand(N,1);
y = rand(N,1);
% --- Plot and define the callback
h = scatter(x, y, 'bo');
set(h, 'ButtonDownFcn', #myfun);
and in the function myfun:
function myfun(h, e)
% --- Get coordinates
x = get(h, 'XData');
y = get(h, 'YData');
% --- Get index of the clicked point
[~, i] = min((e.IntersectionPoint(1)-x).^2 + (e.IntersectionPoint(2)-y).^2);
% --- Do something
hold on
switch e.Button
case 1, col = 'r';
case 2, col = 'g';
case 3, col = 'b';
end
plot(x(i), y(i), '.', 'color', col);
i is the index of the clicked point, so x(i) and y(i) are the coordinates of the clicked point.
Amazingly enough, the mouse button which performed the action is stored in e.Button:
1: left click
2: middle click
3: right click
so you can play around with that too. Here is the result:
Best,

Related

How to make animation in MATLAB which the curve is moving, not axes is moving?

I want to plot y=omega*x^2, which omega is -3 into 3 with step size 0.25, x from -4 into 4 with step size 0.001. But this code give me the curve is cannot moving and axes is moving. I want the curve is moving, like this.
x=-4:0.001:4;
for omega=-3:0.25:3
for i=1:length(x)
y(i)=omega*x(i)^2;
end
plot(x,y);
pause(0.1);
end
How to do that?
As another answer has indicated, you need to set the axis limits.
(Also note that there is no reason to calculate y using a loop.)
But instead of using plot every time through the loop it's more efficient to create the line only once, and then replace the x and y data of the line each time through the loop.
x=-4:0.001:4;
all_omega=-3:0.25:3;
for idx = 1:numel(all_omega)
omega = all_omega(idx);
y=omega*(x.^2);
if idx == 1
% create line
hl = plot(x,y);
axis([-4,4,-40,40]);
box on
grid on
else
% replace line data
set(hl,'XData',x,'YData',y);
end
title(sprintf('\\Omega = %.2f',omega));
pause(0.1);
end
Or you might want to use animatedline,
x=-4:0.001:4;
all_omega=-3:0.25:3;
for idx = 1:numel(all_omega)
omega = all_omega(idx);
y=omega*(x.^2);
if idx == 1
% create animated line
am = animatedline(x,y);
axis([-4,4,-40,40]);
box on
grid on
else
% replace the line
clearpoints(am)
addpoints(am,x,y);
end
title(sprintf('\\Omega = %.2f',omega));
pause(0.1);
end
A quick method is setting the x- and y-axes limits in the loop after each plot, using the axis([xmin, xmax, ymin, ymax]) command. This method isn't foolproof, in the case that the script gets help up after plotting but before setting the axes limits, but in most cases it should work.
figure(1)
x=-4:0.001:4;
for omega=-3:0.25:3
for i=1:length(x)
y(i)=omega*x(i)^2;
end
plot(x,y);
axis([-4 4 -50 50])
pause(0.1);
end
This is the same as #phil-goddard's answer, but makes more efficient use of handles to the animated YData and title string.
x = -4 : 0.001 : 4;
y = zeros(1, numel(x));
omega_range = -3 : 0.25 : 3;
% Create line and title, and obtain handles to them.
h_plot = plot(x, y);
axis([-4, 4, -40, 40]);
box on;
grid on;
h_title = title(sprintf('\\omega = %.2f', 0));
for idx = 1:numel(omega_range)
omega = omega_range(idx);
y = omega * (x.^2);
% Replace y data.
h_plot.YData = y;
% Replace title string.
h_title.String = sprintf('\\omega = %.2f', omega);
pause(0.1);
end

Animate a MATLAB figure by pressing left and right arrow keys

I am trying to change the radius of a sphere by using left and right arrow keys, plotted in a MATLAB figure. For example by pressing right arrow, the value of radius increases by one and then plot the new sphere with the updated radius. Similarly, pressing left arrow key decreases the radius by one and then plots a smaller sphere. However, I want this change to be limited between 1 and rmax.
I got some ideas of how I can approach after reading this post but still it is not what I am looking for. Therefore, I used two global variables to achieve this task in order to somehow pass the information by reference to KeyPressFcn so when a key is pressed KeyPressFcn know what those limits are. In the example below, the code does increase and decrease the radius by one but it is it does not restrict the change of radius within the specified range after left and right arrows are hit.
Is there a better way of approaching this? How can I pass the value of radius and its limits to KeyPressFcn? I want KeyPressFcn to know how much it can change the radius when left and right arrows are pressed.
function animateme()
fig_h = figure;
set(fig_h,'KeyPressFcn', #key_pressed_fcn);
global r rmax
p0 = [0 0 0];
[x,y,z] = sphere;
rmax = 10;
r = 1;
while 1==1
h = surf(x*r+p0(1), y*r+p0(2), z*r+p0(3));
set(h, 'FaceAlpha', 0.5, 'FaceColor', rand([1 3]))
axis equal;
pause
end
function key_pressed_fcn(fig_obj, eventDat)
global r rmax
if strcmpi(eventDat.Key, 'rightarrow')
r = r + 1;
if r < 1
r = 1;
end
elseif strcmpi(eventDat.Key, 'leftarrow')
r = r - 1;
if r > rmax
r = rmax;
end
end
disp(r)
First of all, don't use global variables as there is (almost) always a better way of accomplishing the same thing.
Here is an example by using nested functions which automatically have access to the variables within the parent function's workspace.
function animateme()
fig = figure();
hax = axes('Parent', fig);
set(fig, 'KeyPressFcn', #keypress)
p0 = [0,0,0];
[x,y,z] = sphere();
% Specify limits here which are accessible to nested functions
rmax = 10;
r = 1;
h = surf(x,y,z, 'Parent', hax);
% Subfunction for re-plotting the data
% This prevents you from needing a while loop
function redraw()
set(h, 'XData', x * r + p0(1), ...
'YData', y * r + p0(2), ...)
'ZData', z * r + p0(3));
set(h, 'FaceAlpha', 0.5, ...
'FaceColor', rand([1 3]))
axis(hax, 'equal')
drawnow
end
% Go ahead and do the first redraw
redraw();
% Callback to process keypress events
function keypress(~, evnt)
switch lower(evnt.Key)
case 'rightarrow'
r = min(r + 1, rmax);
case 'leftarrow'
r = max(1, r - 1);
otherwise
return
end
% Always do a redraw
redraw();
end
end
Another option is to store the current value of r within the graphics objects themselves using the UserData field. So you could put it in the surf plot itself. This is actually my preferred method because then your callback function can live anywhere and still have access to the data it needs.
function animateme()
fig = figure();
% Data to store for plotting
data.p = [0,0,0];
data.r = 1;
data.rmax = 10;
% Create a blank surface for starters
h = surf(nan(2), nan(2), nan(2));
set(h, 'UserData', data);
% Update the display of the surface
redraw(h);
% Set the callback and pass the surf handle
set(fig, 'KeyPressFcn', #(fig, evnt)keypress(h, evnt))
end
function redraw(h)
% Get the stored data from the graphics object
userdata = get(h, 'Userdata');
[x,y,z] = sphere();
set(h, 'XData', x * userdata.r + userdata.p(1), ...
'YData', y * userdata.r + userdata.p(2), ...
'ZData', z * userdata.r + userdata.p(3));
set(h, 'FaceAlpha', 0.5, ...
'FaceColor', rand([1 3]))
axis equal
drawnow;
end
function keypress(h, evnt)
% Get the stored data
userdata = get(h, 'Userdata');
switch lower(evnt.Key)
case 'rightarrow'
userdata.r = min(userdata.r + 1, userdata.rmax);
case 'leftarrow'
userdata.r = max(1, userdata.r - 1);
otherwise
return;
end
% Update the stored value
set(h, 'UserData', userdata);
redraw(h);
end

Draw tangent line on curve with MatLab

Can some one help me to find a tangent for a curve.
I have two data set (time, temp). Now I first fit this data using logarithmic function and now I want to draw a tangent over mouse click. I upload a code which is useful but I am not aware how to modify this. Thanks for help.
function test
hh = figure(1); clf, hold on
grid on
x = 0:0.01:2*pi;
f = #(x) sin(x);
fprime = #(x) cos(x);
plot(x, f(x), 'r')
axis tight
D = [];
L = [];
set(hh, ...
'WindowButtonMotionFcn', #mouseMove,...
'WindowButtonDownFcn', #mouseClick);
function mouseMove(varargin)
coords = get(gca, 'currentpoint');
xC = coords(1);
if ishandle(D)
delete(D); end
D = plot(xC, f(xC), 'ko');
end
function mouseClick(obj, varargin)
switch get(obj, 'selectiontype')
% actions for left mouse button
case 'normal'
coords = get(gca, 'currentpoint');
xC = coords(1);
yC = f(xC);
a = fprime(xC);
b = yC-a*xC;
if ishandle(L)
delete(L); end
L = line([0; 2*pi], [b; a*2*pi+b]);
case 'alt'
% actions for right mouse button
case 'extend'
% actions for middle mouse button
case 'open'
% actions for double click
otherwise
% actions for some other X-mouse-whatever button
end
end
end

Mathematic graphs with matlab gui

I need to create a GUI in Matlab that enables me to draw graphs interactively, and the give values to the edges and vertices.
I then need to return these values (x, y, value) for edges and (x1, y1, x2, y2, value) for vertices.
Unfortunately I don't even know where to start. I created a gui that lets me draw lines interactively, with 2 different methods, but I don't know how to continue. Please help.
You can always handle mouse events to enable interactive drawing. I spent some time on this and came up with the following GUI.
function interactive_graph_gui
% data
showLabels = false; % flag to determine whether to show node labels
prevIdx = []; % keeps track of 1st node clicked in creating edges
selectIdx = []; % used to highlight node selected in listbox
pts = zeros(0,2); % x/y coordinates of vertices
adj = sparse([]); % sparse adjacency matrix (undirected)
% create GUI
h = initGUI();
function h = initGUI()
h.fig = figure('Name','Interactive Graph', 'Resize','off');
h.ax = axes('Parent',h.fig, 'ButtonDownFcn',#onMouseDown, ...
'XLim',[0 1], 'YLim',[0 1], 'XTick',[], 'YTick',[], 'Box','on', ...
'Units','pixels', 'Position',[160 20 380 380]);
h.list = uicontrol('Style','listbox', 'Parent',h.fig, 'String',{}, ...
'Min',1, 'Max',1, 'Value',1, ...
'Position',[20 80 130 320], 'Callback',#onSelect);
uicontrol('Style','pushbutton', 'Parent',h.fig, 'String','Clear', ...
'Position',[20 20 60 20], 'Callback',#onClear);
uicontrol('Style','pushbutton', 'Parent',h.fig, 'String','Export', ...
'Position',[90 20 60 20], 'Callback',#onExport);
uicontrol('Style','pushbutton', 'Parent',h.fig, 'String','Delete', ...
'Position',[50 50 60 20], 'Callback',#onDelete);
h.cmenu = uicontextmenu('Parent',h.fig);
h.menu = uimenu(h.cmenu, 'Label','Show labels', 'Checked','off', ...
'Callback',#onCMenu);
set(h.list, 'UIContextMenu',h.cmenu)
h.pts = line(NaN, NaN, 'Parent',h.ax, 'HitTest','off', ...
'Marker','o', 'MarkerSize',10, 'MarkerFaceColor','b', ...
'LineStyle','none');
h.selected = line(NaN, NaN, 'Parent',h.ax, 'HitTest','off', ...
'Marker','o', 'MarkerSize',10, 'MarkerFaceColor','y', ...
'LineStyle','none');
h.prev = line(NaN, NaN, 'Parent',h.ax, 'HitTest','off', ...
'Marker','o', 'MarkerSize',20, 'Color','r', ...
'LineStyle','none', 'LineWidth',2);
h.edges = line(NaN, NaN, 'Parent',h.ax, 'HitTest','off', ...
'LineWidth',2, 'Color','g');
h.txt = [];
end
function onMouseDown(~,~)
% get location of mouse click (in data coordinates)
p = get(h.ax, 'CurrentPoint');
% determine whether normal left click was used or otherwise
if strcmpi(get(h.fig,'SelectionType'), 'Normal')
% add a new node
pts(end+1,:) = p(1,1:2);
adj(end+1,end+1) = 0;
else
% add a new edge (requires at least 2 nodes)
if size(pts,1) < 2, return; end
% hit test (find node closest to click location: euclidean distnce)
[dst,idx] = min(sum(bsxfun(#minus, pts, p(1,1:2)).^2,2));
if sqrt(dst) > 0.025, return; end
if isempty(prevIdx)
% starting node (requires a second click to finish)
prevIdx = idx;
else
% add the new edge
adj(prevIdx,idx) = 1;
prevIdx = [];
end
end
% update GUI
selectIdx = [];
redraw()
end
function onDelete(~,~)
% check that list of nodes is not empty
if isempty(pts), return; end
% delete selected node
idx = get(h.list, 'Value');
pts(idx,:) = [];
adj(:,idx) = [];
adj(idx,:) = [];
% clear previous selections
if prevIdx == idx
prevIdx = [];
end
selectIdx = [];
% update GUI
set(h.list, 'Value',max(min(idx,size(pts,1)),1))
redraw()
end
function onClear(~,~)
% reset everything
prevIdx = [];
selectIdx = [];
pts = zeros(0,2);
adj = sparse([]);
% update GUI
set(h.list, 'Value',1)
redraw()
end
function onExport(~,~)
% export nodes and adjacency matrix to base workspace
assignin('base', 'adj',(adj+adj')>0) % make it symmetric
assignin('base', 'xy',pts)
end
function onSelect(~,~)
% update index of currently selected node
selectIdx = get(h.list, 'Value');
redraw()
end
function onCMenu(~,~)
% flip state
showLabels = ~showLabels;
redraw()
end
function redraw()
% edges
p = nan(3*nnz(adj),2);
[i,j] = find(adj);
p(1:3:end,:) = pts(i,:);
p(2:3:end,:) = pts(j,:);
set(h.edges, 'XData',p(:,1), 'YData',p(:,2))
% nodes
set(h.pts, 'XData',pts(:,1), 'YData',pts(:,2))
set(h.prev, 'XData',pts(prevIdx,1), 'YData',pts(prevIdx,2))
set(h.selected, 'XData',pts(selectIdx,1), 'YData',pts(selectIdx,2))
% list of nodes
set(h.list, 'String',num2str(pts,'(%.3f,%.3f)'))
% node labels
if ishghandle(h.txt), delete(h.txt); end
if showLabels
set(h.menu, 'Checked','on')
h.txt = text(pts(:,1)+0.01, pts(:,2)+0.01, ...
num2str((1:size(pts,1))'), ...
'HitTest','off', 'FontSize',8, ...
'VerticalAlign','bottom', 'HorizontalAlign','left');
else
set(h.menu, 'Checked','off')
end
% force refresh
drawnow
end
end
It all boils down to handling the ButtonDownFcn callback of the axis object, and querying the location of the last mouse click using the CurrentPoint property.
Here is a list of the possible ways to interact with the GUI:
left-click inside the axis to create vertices
right-click on two nodes to create an edge
use the listbox to select and highlight nodes. Use the "delete" button to remove the selected vertex.
The "clear" button resets everything
The "export" button create two variables in the base workspace containing the vertices 2D coordinates (N-by-2 matrix) and the edges (as a sparse N-by-N matrix). You can use those variables with other graph functions as usual:
gplot(adj, xy, 'b.-')
Finally you can right click on the listbox. This will bring up a popup menu, containing the option to display labels for the vertices.
You can extend the above code to assign values to vertices. For example you could use the callback function of the listbox to assign values to vertices (display an input dialog when the user selects an item from the list). You could also use the same technique shown of handling the ButtonDownFcn callback. Similarly you could create a second listbox to display the edges and handle the assignment of values in the same manner... I will that part to you :)
If you want to use the mouse to draw interactively you can use the function ginput. Here there is the manual.
I found very useful also this example.

Matlab code to draw a tangent to a curve

I need to draw a tangent to a curve at a particular point (say the point is chosen by the user). I have written a code that allows the user to manually pick up two points and then a line is drawn between them. But I would like to automate the process. Can someone please suggest any algorithms/already implemented matlab codes to do so?
Try the function below. Of course, it needs lots of tweaking to apply to your case, but I think this is roughtly what you want.
function test
hh = figure(1); clf, hold on
grid on
x = 0:0.01:2*pi;
f = #(x) sin(x);
fprime = #(x) cos(x);
plot(x, f(x), 'r')
axis tight
D = [];
L = [];
set(hh, ...
'WindowButtonMotionFcn', #mouseMove,...
'WindowButtonDownFcn', #mouseClick);
function mouseMove(varargin)
coords = get(gca, 'currentpoint');
xC = coords(1);
if ishandle(D)
delete(D); end
D = plot(xC, f(xC), 'ko');
end
function mouseClick(obj, varargin)
switch get(obj, 'selectiontype')
% actions for left mouse button
case 'normal'
coords = get(gca, 'currentpoint');
xC = coords(1);
yC = f(xC);
a = fprime(xC);
b = yC-a*xC;
if ishandle(L)
delete(L); end
L = line([0; 2*pi], [b; a*2*pi+b]);
case 'alt'
% actions for right mouse button
case 'extend'
% actions for middle mouse button
case 'open'
% actions for double click
otherwise
% actions for some other X-mouse-whatever button
end
end
end