Matlab code to draw a tangent to a curve - matlab

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

Related

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

Speed up creation of impoint objects

I have to create some draggable points on an axes. However, this seems to be a very slow process, on my machine taking a bit more than a second when done like so:
x = rand(100,1);
y = rand(100,1);
tic;
for i = 1:100
h(i) = impoint(gca, x(i), y(i));
end
toc;
Any ideas on speed up would be highly appreciated.
The idea is simply to provide the user with the possibility to correct positions in a figure that have been previously calculated by Matlab, here exemplified by the random numbers.
You can use the the ginput cursor within a while loop to mark all points you want to edit. Afterwards just click outside the axes to leave the loop, move the points and accept with any key.
f = figure(1);
scatter(x,y);
ax = gca;
i = 1;
while 1
[u,v] = ginput(1);
if ~inpolygon(u,v,ax.XLim,ax.YLim); break; end;
[~, ind] = min(hypot(x-u,y-v));
h(i).handle = impoint(gca, x(ind), y(ind));
h(i).index = ind;
i = i + 1;
end
Depending on how you're updating your plot you can gain a general speedup by using functions like clf (clear figure) and cla (clear axes) instead of always opening a new figure window as explained in this answer are may useful.
Alternatively the following is a very rough idea of what I meant in the comments. It throws various errors and I don't have the time to debug it right now. But maybe it helps as a starting point.
1) Conventional plotting of data and activating of datacursormode
x = rand(100,1);
y = rand(100,1);
xlim([0 1]); ylim([0 1])
f = figure(1)
scatter(x,y)
datacursormode on
dcm = datacursormode(f);
set(dcm,'DisplayStyle','datatip','Enable','on','UpdateFcn',#customUpdateFunction)
2) Custom update function evaluating the chosen datatip and creating an impoint
function txt = customUpdateFunction(empt,event_obj)
pos = get(event_obj,'Position');
ax = get(event_obj.Target,'parent');
sc = get(ax,'children');
x = sc.XData;
y = sc.YData;
mask = x == pos(1) & y == pos(2);
x(mask) = NaN;
y(mask) = NaN;
set(sc, 'XData', x, 'YData', y);
set(datacursormode(gcf),'Enable','off')
impoint(ax, pos(1),pos(2));
delete(findall(ax,'Type','hggroup','HandleVisibility','off'));
txt = {};
It works for the, if you'd just want to move one point. Reactivating the datacursormode and setting a second point fails:
Maybe you can find the error.

MATLAB - How to make multiple markers moving simultaneous in 3d plot in MATLAB

I am currently working on a project simulating the movement of two spacecraft and a moon (Phobos) around Mars. A MATLAB tool called SPICE gives me an array with the x, y and z distances and I have used these to plot the orbit which works fine. Now I want to get markers for each of the spacecraft and Phobos to see when they flyby each other. I got the markers working but not at the same time, they run after each other. I found an example on youtube so it must be possible but he has not released the code how to do it (https://www.youtube.com/watch?v=nArR2P0o4r4).
This is the code I have:
a = position_MEX_Mars(1,:);
b = position_MEX_Mars(2,:);
c = position_MEX_Mars(3,:);
k = position_MAVEN_Mars(1,:);
l = position_MAVEN_Mars(2,:);
m = position_MAVEN_Mars(3,:);
x = position_Phobos_Mars(1,:);
y = position_Phobos_Mars(2,:);
z = position_Phobos_Mars(3,:);
ah = axes;
set(ah,'XLim',[min(x) max(x)],'YLim',[min(y) max(y)],...
'ZLim',[min(z) max(z)]);
plot3(0,0,0,'ro-',x,y,z,a,b,c,k,l,m);
grid on;
hold on;
hpoint = line('XData', 0,'YData', 0,'ZData', 0,'Color','black','Marker',...
'o','MarkerSize',10);
ht = hgtransform('parent',ah);
set(hpoint,'Parent',ht);
for i =2:length(x)
trans = makehgtform('translate',[x(i) y(i) z(i)]);
set(ht,'Matrix',trans);
pause(0.001);
end
This will run a nice animated plot of the trajectory of Phobos in time but only Phobos and not simultaneous with MEX and MAVEN (spacecraft from ESA and NASA).
I tried this but does not work:
a = position_MEX_Mars(1,:);
b = position_MEX_Mars(2,:);
c = position_MEX_Mars(3,:);
k = position_MAVEN_Mars(1,:);
l = position_MAVEN_Mars(2,:);
m = position_MAVEN_Mars(3,:);
x = position_Phobos_Mars(1,:);
y = position_Phobos_Mars(2,:);
z = position_Phobos_Mars(3,:);
ah = axes;
set(ah,'XLim',[min(x) max(x)],'YLim',[min(y) max(y)],...
'ZLim',[min(z) max(z)]);
plot3(0,0,0,'ro-',x,y,z,a,b,c,k,l,m);
grid on;
hold on;
hpoint = line('XData', 0,'YData', 0,'ZData', 0,'Color','black','Marker',...
'o','MarkerSize',10);
ht = hgtransform('parent',ah);
set(hpoint,'Parent',ht);
for i =2:length(x)
trans1 = makehgtform('translate',[x(i) y(i) z(i)]);
set(ht,'Matrix',trans1);
trans2 = makehgtform('translate',[a(i) b(i) c(i)]);
set(ht,'Matrix',trans2);
pause(0.001);
end
I also tried merging the arrays so that it plots them each one step after each other but that makes the animation not smooth and is not satisfying for the project.
a = position_MEX_Mars(1,:);
b = position_MEX_Mars(2,:);
c = position_MEX_Mars(3,:);
k = position_MAVEN_Mars(1,:);
l = position_MAVEN_Mars(2,:);
m = position_MAVEN_Mars(3,:);
x = position_Phobos_Mars(1,:);
y = position_Phobos_Mars(2,:);
z = position_Phobos_Mars(3,:);
tempx = [position_MEX_Mars(1,:); position_Phobos_Mars(1,:); position_MAVEN_Mars(1,:)];
xt = tempx(:);
tempy = [position_MEX_Mars(2,:); position_Phobos_Mars(2,:); position_MAVEN_Mars(2,:)];
yt = tempy(:);
tempz = [position_MEX_Mars(3,:); position_Phobos_Mars(3,:); position_MAVEN_Mars(3,:)];
zt = tempz(:);
ah = axes;
set(ah,'XLim',[min(x) max(x)],'YLim',[min(y) max(y)],...
'ZLim',[min(z) max(z)]);
plot3(0,0,0,'ro-',x,y,z,a,b,c,k,l,m);
grid on;
hold on;
hpoint = line('XData', 0,'YData', 0,'ZData', 0,'Color','black','Marker',...
'o','MarkerSize',10);
ht = hgtransform('parent',ah);
set(hpoint,'Parent',ht);
for i =2:length(x)
trans = makehgtform('translate',[xt(i) yt(i) zt(i)]);
set(ht,'Matrix',trans);
pause(0.001);
end
I think I am close but I seem to be missing something and my knowledge of MATLAB is not that great yet. I hope you can help me out.
Cheers Jeroen
Here's a simplified (and not physically correct) example that could perhaps be useful:
t = linspace(0,2,1000); %// time parameter
x1 = 10*cos(2*pi*t+1);
y1 = 5*sin(2*pi*t+1); %// trajectory of object 1
x2 = 2*cos(6*pi*t-2);
y2 = 3*sin(6*pi*t-2); %// trajectory of object 1
plot(x1,y1,'color',[.5 .5 .5]); %// plot trajectory of object 1
hold on
plot(x2,y2,'color',[.5 .5 .5]); %// plot trajectory of object 2
h1 = plot(x1(1),y1(1),'ro'); %// plot initial position of object 1
h2 = plot(x2(1),y2(1),'b*'); %// plot initial position of object 2
axis([-12 12 -12 12]) %// freeze axis size
grid on
for n = 1:numel(t)
set(h1, 'XData', x1(n), 'YData', y1(n)); %// update position of object 2
set(h2, 'XData', x2(n), 'YData', y2(n)); %// update position of object 2
drawnow %// refresh figure
end
The thing that you tries to do is not really as easy as you think. The main issue is that you need to update everything at the same time. This have been implemented in several ways during the years. One way to do this is by using a so called a double buffer.
This means that you have two "surfaces" to paint on. In matlab this is translated to 2 axes (or 2 figures maybe). However, you do only have one axes visible at the same time. This means that you will have time to paint everything on the "hidden surface" before displaying the content. When you are done with everything you need you just switch which surface being visible.
It is possible that this can be done in a simpler way, but I am not familiar with the hgtransfrom functions in matlab.
EDIT
This is an example how it can be done
function test()
fig = figure;
ax1 = axes;
plot(1:10);
ax2 = axes;
plot(1:10,'r');
setVisibility(ax2,'off');
pause(1);
for k = 1:5
setVisibility(ax2,'on');
setVisibility(ax1,'off');
pause(1);
setVisibility(ax1,'on');
setVisibility(ax2,'off');
pause(1);
end
function setVisibility(ax, visibility)
set(ax, 'visible', visibility)
set(findall(ax), 'visible', visibility)

Matlab onclick callback to execute function

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,