how to animate 2 surfaces in Matlab? - matlab

I've written this code which makes an animation of 2 ellipsoids. Parameter k1 of these ellipsoids must depend on time (so they'd move asynchronously), but I need to animate them in one figure. Can I use loop for it or is it better to use timer & some kind of callback functions? The second problem - I need to move inner ellipsoid so they would have one common side. How can I do this?

You should use a loop. The majority of your time will be spent plotting and with the "getFrame" command. You can use profile to verfiy this. The for loop won't add significant overhead and is easiest to code and understand
As for your second question, I'm not sure exactly what you're asking, but if you want to keep a point in common, you should parametrize your surface in terms of the radii, skew angle, etc. and the common point, then just move the point around. You might want to consider writing a "drawEllipsoid" function, which would simplify and clarify your code.

The loop seems fine for this job. A timer would work as well.
I'm not sure quite what you mean when you say "have one common side". It looks like you're close with adding 5 to X2, but you would need a scaling term since they're not the same shape. Could you elaborate?
One suggestion though. I think that you'll be a lot happier if you move the object creation out of the loop like this:
a=5;
b=a;
c=10;
u = (0:0.05*pi:2*pi)'; %'
v = [0:0.05*pi:2*pi];
X = a*sin(u)*cos(v);
Y = a*sin(u)*sin(v);
Z = c*cos(u)*ones(size(v));
Z(Z>0)=0; % cut upper
V1=4/3*pi*a*b*c;
d=1/2;
e=2^d;
a2=a/e;
b2=a/e;
c2=c;
V2=4/3*pi*a2*b2*c2;
X2 = a2*sin(u)*cos(v);%-2.5;
Y2 = b2*sin(u)*sin(v);
Z2 = c2*cos(u)*ones(size(v));%+0.25;
Z2(Z2>0)=0; % cut
h=1/3;
hS1=surf(X,Y,Z);
alpha(.11)
hold on
hS2=surf(X2,Y2,Z2);
hold off
axis([-20 20 -20 20 -20 20]);
for j = 1:20
k1=(sin(pi*j/20)+0.5)^h;
a=a*k1;
c=c*k1;
X = a*sin(u)*cos(v);
Y = a*sin(u)*sin(v);
Z = c*cos(u)*ones(size(v));
Z(Z>0)=0;
a2=a2*k1;
b2=a2*k1;
c2=c2*k1;
X2 = a2*sin(u)*cos(v)+5;%-2.5;
Y2 = b2*sin(u)*sin(v);
Z2 = c2*cos(u)*ones(size(v));%+0.25;
Z2(Z2>0)=0;
set(hS1,'XData',X,'YData',Y,'ZData',Z);
set(hS2,'XData',X2,'YData',Y2,'ZData',Z2);
drawnow;
F(j) = getframe;
end
movie(F,4)
The drawnow is not strictly necessary here because the getframe contains one, but it is good to insert one so that you can see what's going on if you remove the getframe.

OK, here's the shortcut to doing a trimmed surface I mentioned in my earlier comment. What you're going to do is put NaNs into the coordinates everywhere the surface is outside the domain. Then the surface object will drop any quads which touch those coordinates. That doesn't give you a nice clean edge, but it is really easy.
a=5;
b=a;
c=10;
u = (0:0.05*pi:pi)'; %'
v = [0:0.05*pi:2*pi];
X = a*sin(u)*cos(v);
Y = a*sin(u)*sin(v);
Z = c*cos(u)*ones(size(v));
Z(Z>0)=0; % cut upper
V1=4/3*pi*a*b*c;
d=1/2;
e=2^d;
a2=a/e;
b2=a/e;
c2=c;
V2=4/3*pi*a2*b2*c2;
X2 = a2*sin(u)*cos(v);%-2.5;
Y2 = b2*sin(u)*sin(v);
Z2 = c2*cos(u)*ones(size(v));%+0.25;
Z2(Z2>0)=0; % cut
h=1/3;
hS1=surf(X,Y,Z);
alpha(.11)
hold on
hS2=surf(X2,Y2,Z2);
hold off
axis([-20 20 -20 20 -20 20]);
for j = 1:20
k1=(sin(pi*j/20)+0.5)^h;
a=a*k1;
c=c*k1;
X = a*sin(u)*cos(v);
Y = a*sin(u)*sin(v);
Z = c*cos(u)*ones(size(v));
Z(Z>0)=0;
a2=a2*k1;
b2=a2*k1;
c2=c2*k1;
X2 = a2*sin(u)*cos(v)+5;%-2.5;
Y2 = b2*sin(u)*sin(v);
Z2 = c2*cos(u)*ones(size(v));%+0.25;
Z2(Z2>0)=0;
set(hS1,'XData',X,'YData',Y,'ZData',Z);
% substitute into implicit form of 1st ellipsoid
d = (X2.^2) / a^2 + (Y2.^2) / a^2 + (Z2.^2) / c^2;
% and zap any that are outside [0 1]
X2(d>1) = nan;
Y2(d>1) = nan;
Z2(d>1) = nan;
set(hS2,'XData',X2,'YData',Y2,'ZData',Z2);
drawnow;
F(j) = getframe;
end
movie(F,4)
Note that I also changed the range of u. That's because you were actually drawing 2 copies of the surface. That was causing some problems with how the transparency was rendered.

Related

two plots are linked by an unwanted line - animated line

I am using animatedline to create an animation of a camshaft rotating and moving the bucket which sits on the valve.
I have created what i thought were two separate plots, one of the polar coordinates of the valve lift profile, and a horizontal line which is based on the minimum value of the camshaft in the Y direction. However, I have plotted these two and the animation works well apart from there being a line which connects the last point of the cam lobe to the horizontal line and I'm trying to get rid of it.
Below is a picture explaining what i mean;
Example picture
i have attached the .mat file of the cam lift profile, and my code is below. I'm sure I am missing something obvious but I might be too close to the woods to see the trees.
Raw_Cam_Data file is attached by dropbox in below link (i think this will work?)
Raw_Cam_Data.mat
clear
clc
load('Raw_Cam_Data')
theta = deg2rad(1:1:360)
phi = theta'
R = Lift' + 17.6
%==============================
h = animatedline;
axis([-30 30 -30 30])
rotation = (-pi:pi/180:pi - (pi/180))'
output = zeros(360,1)
for II = 1:1:length(phi)-1
x = R .* sin(theta + rotation(II))
y = R .* cos(theta + rotation(II))
[bucket, indice] = (min(y))
output(II) = indice
x_bucket = linspace(-25,25,359)
y_bucket = bucket * ones(359,1)
addpoints(h, x, y)
addpoints(h, x_bucket, y_bucket)
drawnow
clearpoints(h)
end
Thanks
Found an answer on this thread on the MATLAB forum
MATLAB Forum Post
The part i missed was i was plotting both the datasets on one graph without 'hold on' and then creating two separate graphs/ animations.
This is intuitive but i stupidly missed it. Code for a working example is below along with the .mat file
Raw_Cam_Data.mat
clear
clc
load('Raw_Cam_Data')
theta = deg2rad(1:1:360)
phi = theta'
R = Lift' + 17.6
%==============================
figure % create new figure
hold on % hold on to plot all data on the same figure
h = animatedline; %animation 1, AKA first plot
h2 = animatedline %animation 2, AKA second plot
axis([-30 30 -30 30])
rotation = (-pi:pi/180:pi - (pi/180))'
output = zeros(360,1)
for II = 1:1:length(phi)-1
x = R .* sin(theta + rotation(II))
y = R .* cos(theta + rotation(II))
[bucket, indice] = (min(y))
output(II) = indice
x_bucket = linspace(-25,25,360)
y_bucket = bucket * ones(360,1)
addpoints(h, x, y) %add points from first plot/ animation
addpoints(h2, x_bucket, y_bucket) %add points from second plot/ animation
drawnow
clearpoints(h) % clear points from first plot
clearpoints(h2) % clear points from second plot
end

Animate a line on matlab

I would like to animate a self updating plot on matlab. For example a string vibrating between two ends. All the basic animate functions I have found in the documentation accomplish the same thing, mainly to animate an evolving plot. I.e the function remains the same but the number of points plotted increases (or decreases) in time. For example this script:
h = animatedline;
axis([0,4*pi,-1,1])
x = linspace(0,4*pi,1000);
y = sin(x);
for k = 1:length(x)
addpoints(h,x(k),y(k));
drawnow
end
traces a sine function as if an invisible hand was drawing it. What I would like to do is animate the entire function but with a varying parameter, like the phase or amplitude. I tried to modify using the following:
x = linspace(0,4*pi,1000);
;
axis([0,4*pi,-1,1])
for k = 1:10
h = animatedline(x,sin(k*x))
drawnow
end
This is sort of close to what I need but the functions are progressively appended, not replaced. This results in a total of 10 functions being plotted instead of an animation.
Does anyone understand what I need to do? If so how can this be accomplished?
What about this:
h = animatedline;
axis([0,4*pi,-1,1]);
x = linspace(0,4*pi,1000);
for k = 1:0.01:10
y = sin(k*x);
clearpoints(h);
addpoints(h,x,y);
drawnow
end

Generate trajectories for 3 agents/robots with same tangent in specific point - MATLAB

I want to simulate the movements of 3 robots/agents in space and I would like to generate 3 different trajectories which have one constraint: in a certain time T the all the trajectories must have the same tangent.
I want something like in the following picture:
I need to do it through MATLAB and/or SIMULINK.
Thanks a lot for your help.
I do not know if this is enough for what I need or not but I probably figured out something.
What I did is to fit a polynomial to some points and constraining the derivative of the polynomial in a certain point to be equal to 0.
I used the following function:
http://www.mathworks.com/matlabcentral/fileexchange/54207-polyfix-x-y-n-xfix-yfix-xder-dydx-
It is pretty easy, but it saved me some work.
And, if you try the following:
% point you want your functions to pass
p1 = [1 1];
p2 = [1 3];
% First function
x1 = linspace(0,4);
y1 = linspace(0,4);
p = polyfix(x1,y1,degreePoly,p1(1),p1(2),[1],[0]);
% p = [-0.0767 0.8290 -1.4277 1.6755];
figure
plot(x1,polyval(p,x1))
xlim([0 3])
ylim([0 3])
grid on
hold on
% Second function
x2 = linspace(0,4);
y2 = linspace(0,4);
p = polyfix(x2,y2,degreePoly,[1],[3],[1],[0])
% p = [0.4984 -2.7132 3.9312 1.2836];
plot(x2,polyval(p,x2))
xlim([0 3])
ylim([0 3])
grid on
If you don't have the polyfix function and you don't want to download it you can try the same code commenting the polyfix lines:
p = polyfix(x1,y1,degreePoly,p1(1),p1(2),[1],[0]);
p = polyfix(x2,y2,degreePoly,p2(1),p2(1),[1],[0]);
And uncommenting the lines:
% p = [-0.0767 0.8290 -1.4277 1.6755];
% p = [0.4984 -2.7132 3.9312 1.2836];
You will get this:
Now I will use this polynomial as a position (x,y) over time of my robots and I think I should be done. The x of the polynomial will be also the time, in this way I am sure that the robots will arrive in the 0-derivative point at the same time.
What do you think? Does it make sense?
Thanks again.
To have tangents same all the time, curves (or trajectories) needs to be always parallel. Generate trajectory for one object and keep other objects at fixed distance apart and following master trajectory.
In image provides if we take time next to three dots trajectories will not be same (or parallel) as objects moved in different directions. So i guess you've to keep then always parallel

How would I plot the for loop from my code below?

I have 3D flow data of the velocity of a fluid through a tube. I know the diameter of the tube and have looked at the velocity field and found the centre of the field for an xy plane at both ends of the tube. So I essentially have a line through the centre axis of the tube. I want to NaN all data points that are outside of the diameter. For this I am using an equation that gives the distance to a point from a line in 3D which I found here mathworld.wolfram.com/Point-LineDistance3-Dimensional.html. I then created an if statement which states points smaller than diameter will be NaN.
I am new to matlab so I don't know how I would now plot this.
%%
diff_axis = end_axis-start_axis;
diff_axis_mag = (diff_axis(1)^2 + diff_axis(2)^2 + diff_axis(3)^2)^0.5;
[rw col pl] = size(X);
for j = 1:col
for i = 1:rw
for k = 1:pl
x_curr = X(i,j,k);
y_curr = Y(i,j,k);
z_curr= Z(i,j,k);
x0 = [x_curr y_curr z_curr]
t = - dot((start_axis-x0),(diff_axis))./(diff_axis_mag)^2;
d = sqrt(((start_axis(1) - x0(1)) + (end_axis(1) - start_end(1))*t)^2 + ((start_axis(2)-x0(2))+(end_axis(2)-start_end(2))*t)^2+((start_axis(3)-x0(3))+(end_axis(3)-start_end(3))*t)^2);
if (d > D)
x_curr=NaN
y_curr=NaN
z_curr=NaN
end
end
end
end
It were nice to have explanatory names for your X, Y, and Z. I am guessing they are flow components, and diff_axis are axis coordinates? It is a very cumbersome notation.
what you do in your loops is you take point values (X,Y,Z), copy them to temporary constants and then set them to NaN if they fall out. But the problem is that usually you do not plot point-by-point in MATLAB. So these temorary guys like x_curr will be lost.
Also, the most optimal way to do things in MATLAB is to avoid loops whenever possible.
What you can do is to create first a mask
%// remember to put a dot like in `.^` for entrywise array operations
diff_axis_mag = sqrt(diff_axis(1).^2 + diff_axis(2).^2 + diff_axis(3).^2);
%// are you sure you need to include the third axis?
%// then it is a ball, not a tube
%// create a binary mask
mask = diff_axis_mag < tube_radius
X(~mask) = NaN;
Y(~mask) = NaN;
Z(~mask) = NaN;
Then you can plot your data with quiver3 or
stream3

How to make a MATLAB plot interactive?

I'm trying to create a simple interface for plotting quadratic Lagrange polynomials. For this, you need just 3 points (with each their own x,y,z coordinates), which are then interpolated using the quadratic Lagrange polynomials.
It's easy to make a static version, or even one that lets the user input the 3 points before plotting the curve. But it should also be possible for the user to drag an existing point in the plot window to another position, and then re-plot the curve automatically using the new position of this point!
So in short, the user should be able to drag these black dots to another location. After that (or while dragging), the curve should be updated.
function Interact()
% Interactive stuff here
figure();
hold on;
axis([0 7 0 5])
DrawLagrange([1,1; 3,4; 6,2])
function DrawLagrange(P)
plot(P(:,1), P(:,2), 'ko--', 'MarkerSize', 10, 'MarkerFaceColor', 'k')
t = 0:.1:2;
Lagrange = [.5*t.^2 - 1.5*t + 1; -t.^2 + 2*t; .5*t.^2 - .5*t];
CurveX = P(1,1)*Lagrange(1,:) + P(2,1)*Lagrange(2,:) + P(3,1)*Lagrange(3,:);
CurveY = P(1,2)*Lagrange(1,:) + P(2,2)*Lagrange(2,:) + P(3,2)*Lagrange(3,:);
plot(CurveX, CurveY);
I think I either have to use functions like WindowButtonDownFcn, WindowButtonUpFcn and WindowButtonMotionFcn, or the ImPoint from the Image Processing Toolbox. But how?
[Edit]
It should also work in 3D, since I'd like to generalize this concept to tensor product surfaces.
Ok, I searched for some more information on the ImPoint option from the Image Processing Toolbox, and wrote this script.
Since ImPoint only works for a 2D setting (and I'd like to generalize this to 3D to be able to work with surfaces instead of curves), it is not really an acceptable answer! But somebody might benefit from it, or get an idea how to do this in 3D.
% -------------------------------------------------
% This file needs the Image Processing Toolbox!
% -------------------------------------------------
function Interact(Pos)
% This part is executed when you run it for the first time.
% In that case, the number of input arguments (nargin) == 0.
if nargin == 0
close all;
clear all;
clc;
figure();
hold on;
axis([0 7 0 5])
% I do not know how to do this without global variables?
global P0 P1 P2
% GCA = Get handle for Current Axis
P0 = ImPoint(gca,1,1);
setString(P0,'P0');
P1 = ImPoint(gca,2,4);
setString(P1,'P1');
P2 = ImPoint(gca,6,2);
setString(P2,'P2');
% Call subfunction
DrawLagrange(P0,P1,P2)
% Add callback to each point
addNewPositionCallback(P0,#Interact);
addNewPositionCallback(P1,#Interact);
addNewPositionCallback(P2,#Interact);
else
% If there _is_ some input argument, it has to be the updated
% position of a moved point.
global H1 H2 P0 P1 P2
% Display X and Y coordinates of moved point
Pos
% Important: remove old plots! Otherwise the graph will get messy.
delete(H1)
delete(H2)
DrawLagrange(P0,P1,P2)
end
function DrawLagrange(P0,P1,P2)
P = zeros(3,2);
% Get X and Y coordinates for the 3 points.
P(1,:) = getPosition(P0);
P(2,:) = getPosition(P1);
P(3,:) = getPosition(P2);
global H1 H2
H1 = plot(P(:,1), P(:,2), 'ko--', 'MarkerSize', 12);
t = 0:.1:2;
Lagrange = [.5*t.^2 - 1.5*t + 1; -t.^2 + 2*t; .5*t.^2 - .5*t];
CurveX = P(1,1)*Lagrange(1,:) + P(2,1)*Lagrange(2,:) + P(3,1)*Lagrange(3,:);
CurveY = P(1,2)*Lagrange(1,:) + P(2,2)*Lagrange(2,:) + P(3,2)*Lagrange(3,:);
H2 = plot(CurveX, CurveY);
I added some comments for clarity.
[Edit] In the preview the syntax highlighting doesn't look very good! Should I define the language to be highlighted somewhere?
A better solution (one that does not need any additional toolboxes) is to use events. First step:
H = figure('NumberTitle', 'off');
set(H, 'Renderer', 'OpenGL');
set(H, 'WindowButtonDownFcn', #MouseClick);
set(H, 'WindowButtonMotionFcn', #MouseMove);
set(H, 'WindowScrollWheelFcn', #MouseScroll);
set(H, 'KeyPressFcn', #KeyPress )
The next step is to define the functions like MouseClick, this is the place where you implement how to react to events (e.g. mouse buttons being clicked, keys being pressed).
In the meantime I implemented an interactive B-spline environment in MATLAB, the source code (along with a concise manual) can be downloaded from https://github.com/pjbarendrecht/BsplineLab.
Great question! I've had this problem too and wondered how to solve it before, but never looked into it. My first thought was to use ginput and then minimize the distance to the line and find the closest point. I thought that was a bit of a hack so I looked around. Seems like that's the only reasonable answer out there and was confirmed here with this code as an example.
%minimum absolute differences kick in again
xx = 1:10; %xdata
yy = exp(xx);
plot(xx,yy);
[xm ym] = ginput(1); %xmouse, ymouse
%Engine
[~, xidx] = min(abs(xx-xm)); %closest index
[~, yidx] = min(abs(yy-ym));
x_closest = xx(xidx) %extract
y_closest = yy(yidx)
Not sure how it scales to 3D, but I thought this would be a good start.