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
Related
I am trying to plot two different ellipses in Matlab with the same foci. Essentially I am plotting the elliptical orbit before and after an impulse maneuver.
The first (before maneuver) ellipse will always be in-line with the x-axis, whereas the second ellipse (after maneuver) will be at an angle above/below the x-axis (The second one will have a different major/minor axis and eccentricity as well).
In order to save space, the code for plotting one ellipse will be the same as the second ellipse, so I'll only show my code here for plotting one ellipse.
The problem is that it plots from the center of the grid, and not the foci.
Re = 6378.136; %km
e = .65;
x1 = -4.4*Re; %-a
x2 = 4.4*Re; % a
y1 = 0;
y2 = 0;
a = 1/2*sqrt((x2-x1)^2+(y2-y1)^2);
b = a*sqrt(1-e^2);
t = linspace(0,2*pi);
X = a*cos(t);
Y = b*sin(t);
w = atan2(y2-y1,x2-x1);
x = (x1+x2)/2 + X*cos(w) - Y*sin(w);
y = (y1+y2)/2 + X*sin(w) + Y*cos(w);
plot(x,y,'b')
axis equal
grid on
hold on
I'm sure it's something simple but I can't seem to figure it out.
Any help is much appreciated. Thanks.
Edit: David helped solve this issue, thank you.
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
I was wondering if anyone knew how to do an animation plot of
x = (dataset of 1000 points)
y = (dataset of 1000 points)
plot(x,y)
big problem is these are datasets that i am trying to plot , or x,y coordinates as opposed to a function which I would know how to plot via an animation.
I tried to do frames in a for loop but it gave me dots and didn't join them in a line graph so I couldn't really watch the path being traced out.
code I used was
for i = 1:length(DATASET1)
pause(0.1)
plot(DATASET1(i),DATASET2(i))
draw on
end
If what you want is for the plot to "grow" point by point: the easiest way is to create an empty plot and then update its XData and YData properties at each iteration:
h = plot(NaN,NaN); %// initiallize plot. Get a handle to graphic object
axis([min(DATASET1) max(DATASET1) min(DATASET2) max(DATASET2)]); %// freeze axes
%// to their final size, to prevent Matlab from rescaling them dynamically
for ii = 1:length(DATASET1)
pause(0.01)
set(h, 'XData', DATASET1(1:ii), 'YData', DATASET2(1:ii));
drawnow %// you can probably remove this line, as pause already calls drawnow
end
Here's an example1 obtained with DATASET1 = 1:100; DATASET2 = sin((1:100)/6);
1 In case someone's interested, the figure is an animated gif which can be created by adding the following code (taken from here) within the loop, after the drawnow line:
frame = getframe(1);
im = frame2im(frame);
[imind,cm] = rgb2ind(im,256);
if ii == 1;
imwrite(imind,cm,filename,'gif','Loopcount',inf);
else
imwrite(imind,cm,filename,'gif','WriteMode','append');
end
Looks like you were close. Not sure draw on is any command though.
See if the code here inspires you to solve your case -
%// Sample x and y values assumed for demo.
x = 1:1000;
y = x.^2;
%// Plot starts here
figure,hold on
%// Set x and y limits of the plot
xlim([min(x(:)) max(x(:))])
ylim([min(y(:)) max(y(:))])
%// Plot point by point
for k = 1:numel(x)
plot(x(k),y(k),'-') %// Choose your own marker here
%// MATLAB pauses for 0.001 sec before moving on to execue the next
%%// instruction and thus creating animation effect
pause(0.001);
end
Since R2014b, you can work with annimatedline object (doc and how-to) that is meant to handle animated graphs pretty well. Basically, the annimatedline object has a addpoints function that adds new points to the line without having to redefine the existing points, along with a clearpoints function that clears lines for more complex animations.
Here is an example:
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
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.
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.