This is part of the code the makes the video:
k = 10000;
j = 1;
v = VideoWriter('myVideo.avi');
open(v)
while j < k
axis([0 5 0 1000]);
plot(0:dr:R, u_sol_matrix(:,j))
frame = getframe(gcf);
writeVideo(v,frame);
j = j + 50;
% pause(0.01)
end
close(v)
Now, given a matrix u_sol_matrix, where every column represents a solution for a PDE at a certain time point, I plot the solutions and the by the getframe command capture these plots and make a movie out of it.
The problem is that the axis keeps changing as the plot keeps adjusting to the solution. I want the axis to be constant. How do I get this to work? I have tried adding axis but this does not work apparently.
k = 10000;
j = 1;
v = VideoWriter('myVideo.avi');
open(v)
while j < k
fig = figure(); % Explicitly create figure
plot(0:dr:R, u_sol_matrix(:,j))
axis([0 5 0 1000]); % first plot, then change axis
frame = getframe(gcf);
writeVideo(v,frame);
close(fig) % close figure explicitly.
j = j + 50;
% pause(0.01)
end
close(v)
Flipping the figure creation and setting axis limits should do the trick. When you call axis without an open figure, MATLAB creates one, only to overwrite it if you don't call hold on, thus changing the limits to whatever the plot "needs" to fit.
Like Adriaan answered, you just have to flip the order of plotting and setting axis limits to make this work.
However, when creating animations, it is faster to first initialize a figure and the graphics objects (i.e. lines, scatter points, etc.), and later update the data in a loop.
k = 10000;
j = 1;
v = VideoWriter('myVideo.avi');
open(v)
% some test data
x = 10;
y = sin(1:k);
% init a figure and plot handles
fig = figure(1);
p = plot(x, y(1), 'o'); % create line object, and store the handle
axis([9 11 -1 1]) % axis limits for test data
% update data during animation
while j < k
p.XData = x; % update X and Y data properties of line object
p.YData = y(j);
frame = getframe(gcf);
writeVideo(v,frame);
j = j + 50;
pause(0.01)
end
close(v)
Since you don't have to create a new line primitive each loop iteration, this will save a lot of time. You only have to adjust the X and Y data properties of the already existing line, which has considerable less overhead.
Related
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
Suppose I have a 5x5 matrix.
The elements of the matrix change (are refreshed) every second.
I would like to be able to display the matrix (not as a colormap but with the actual values in a grid) in realtime and watch the values in it change as time progresses.
How would I go about doing so in MATLAB?
A combination of clc and disp is the easiest approach (as answered by Tim), here's a "prettier" approach you might fancy, depending on your needs. This is not going to be as quick, but you might find some benefits, such as not having to clear the command window or being able to colour-code and save the figs.
Using dispMatrixInFig (code at the bottom of this answer) you can view the matrix in a figure window (or unique figure windows) at each stage.
Example test code:
fig = figure;
% Loop 10 times, pausing for 1sec each loop, display matrix
for i=1:10
A = rand(5, 5);
dispMatrixInFig(A,fig)
pause(1)
end
Output for one iteration:
Commented function code:
function dispMatrixInFig(A, fig, strstyle, figname)
%% Given a figure "fig" and a matrix "A", the matrix is displayed in the
% figure. If no figure is supplied then a new one is created.
%
% strstyle is optional to specify the string display of each value, for
% details see SPRINTF. Default is 4d.p. Can set to default by passing '' or
% no argument.
%
% figname will appear in the title bar of the figure.
if nargin < 2
fig = figure;
else
clf(fig);
end
if nargin < 3 || strcmp(strstyle, '')
strstyle = '%3.4f';
end
if nargin < 4
figname = '';
end
% Get size of matrix
[m,n] = size(A);
% Turn axes off, set origin to top left
axis off;
axis ij;
set(fig,'DefaultTextFontName','courier', ...
'DefaultTextHorizontalAlignment','left', ...
'DefaultTextVerticalAlignment','bottom', ...
'DefaultTextClipping','on');
fig.Name = figname;
axis([1, m-1, 1, n]);
drawnow
tmp = text(.5,.5,'t');
% height and width of character
ext = get(tmp, 'Extent');
dy = ext(4);
wch = ext(3);
dwc = 2*wch;
dx = 8*wch + dwc;
% set matrix values to fig positions
x = 1;
for i = 1:n
y = 0.5 + dy/2;
for j = 1:m
y = y + 1;
text(x,y,sprintf(strstyle,A(j,i)));
end
x = x + dx;
end
% Tidy up display
axis([1-dwc/2 1+n*dx-dwc/2 1 m+1]);
set(gca, 'YTick', [], 'XTickLabel',[],'Visible','on');
set(gca,'XTick',(1-dwc/2):dx:x);
set(gca,'XGrid','on','GridLineStyle','-');
end
I would have thought you could achieve this with disp:
for i=1:10
A = rand(5, 5);
disp(A);
end
If you mean that you don't want repeated outputs on top of each other in the console, you could include a clc to clear the console before each disp call:
for i=1:10
A = rand(5, 5);
clc;
disp(A);
end
If you want to display your matrix on a figure it is quite easy. Just make a dump matrix and display it. Then use text function to display your matrix on the figure. For example
randMatrix=rand(5);
figure,imagesc(ones(20));axis image;
hold on;text(2,10,num2str(randMatrix))
If you want to do it in a for loop and see the numbers change, try this:
for i=1:100;
randMatrix=rand(5);
figure(1),clf
imagesc(ones(20));axis image;
hold on;text(2,10,num2str(randMatrix));
drawnow;
end
I have written a while loop in Matlab that is supposed to send each value in an array from Matlab to arduino at a specified time interval using a tic toc delay in Matlab and then read values and store them in a variable and graph them.
The output of the while-loop slows down with each successive iteration.
I increased the buffersize which helped it a lot, but it still slows down too much. Is there another way to increase the speed to print the values on time. I have included another tic toc and graph to show the execution speed here is the code:
max = 80;
min = 40;
amp = (max-min)/2;
offset = amp + min;
btime = 5;
bpm = 12;
spb = 60/bpm;
sapb = spb/.05;
tosd = sapb*bpm*btime;
time1 = btime*60;
x = linspace(0,time1,tosd)';
x1 = amp*sin(x*(2*pi/20)) + offset;
pause(1);
fprintf(handles.UltraM,(['<P' num2str(offset) '>']))
pause(5);
y = [];
i = 1;
figure(1);
hold on;
title('Pressure Data');
xlabel('Data Number');
ylabel('Analog Voltage (0-1023)');
t1 = [];
figure(2);
hold on;
title('Time to execute task');
xlabel('iteration number');
ylabel('time taken');
while (i<=length(x))
t2 = tic;
t = tic;
fprintf(handles.UltraM,(['<P' num2str(x1(i)) '>']));
%disp((['<P' num2str(x1(i)) '>']));
y(i) = fscanf(handles.UltraM,'%d');
figure(1);
hold on;
plot(i, y(i), 'b*');
drawnow;
hold off;
while toc(t) < 0.05
continue
end
t1(i) = toc(t2);
figure(2);
hold on;
plot(i,t1(i),'b*');
drawnow;
hold off;
i = i + 1;
end
After a bit of back and forth I think I know what you're trying to achieve and what stands in your way.
I have edited your code to make it slightly more fast and readable. Most of the time the operations take just slightly above 0.05 seconds, and at several time points it can take about 5 milliseconds longer than expected. Your millage may vary of course. Since I do not have an arduino, I cannot know if there is a bottle neck there. You should also try profiling your code using the builtin Matlab profiler (it's very useful) to see what exactly slows your code down.
The main thing I found to slow your code is that you used the plot function to add one point at a time to your figure. Each time you call this function, it creates a new graphics object. After a few hundred of those, things get sluggish. Instead you should simply update the already plotted data and redraw it using drawnow.
In short, the solution is this:
1) Initialize you plot with a single point and save the graphics handle for later use:
p1 = plot(0,0,'b*');
2) Then, inside the loop, once your data arrays have been updated, replace the data in your existing plot with the new arrays.
set(p1, 'XData', 1:i, 'YData', y(1:i));
3) Redraw the plots to reflect the latest update.
drawnow;
drawnow will eventually slow down your code also since it has to redraw increasingly large plots at every iteration. To make things work faster, you might want to refresh your plot after a longer interval. For example, the following will refresh every 10 iterations:
if rem(i,10) == 0
drawnow;
end
Full code below. Let me know if you have any more issues.
max = 80;
min = 40;
amp = (max-min)/2;
offset = amp + min;
btime = 5;
bpm = 12;
spb = 60/bpm;
sapb = spb/.05;
tosd = sapb*bpm*btime;
time1 = btime*60;
x = linspace(0,time1,tosd)';
x1 = amp*sin(x*(2*pi/20)) + offset;
pause(1);
%fprintf(handles.UltraM,(['<P' num2str(offset) '>']))
disp(['<P' num2str(offset) '>']); % replacing with disp (I don't have an arduino)
pause(5);
%y = []; % unnecessary here, preallocated before loop
figure(1);
p1 = plot(0,0,'b*'); % plotting one dot to create an object, data will be overwritten
hold on;
title('Pressure Data');
xlabel('Data Number');
ylabel('Analog Voltage (0-1023)');
%t1 = []; % unnecessary here, preallocated before loop
figure(2);
p2 = plot(0,0,'b*'); % plotting one dot to create an object, data will be overwritten
hold on;
title('Time to execute task');
xlabel('iteration number');
ylabel('time taken');
% preallocate t1 and y arrays for faster operation
t1 = zeros(size(x));
y = zeros(size(x));
i = 1; % moved closer to loop beginning for better readability
while i <= length(x) % parentheses unnecessary in Matlab
t2 = tic;
t = tic;
%fprintf(handles.UltraM,(['<P' num2str(x1(i)) '>']));
disp((['<P' num2str(x1(i)) '>'])); % replacing with disp (I don't have an arduino)
%y(i) = fscanf(handles.UltraM,'%d');
y(i) = randn; % replacing with random number (I don't have an arduino)
%figure(1); % unnecessary
%hold on; % unnecessary
%plot(i, y(i), 'b*');
% replacing the above with a slightly faster version
set(p1, 'XData', 1:i, 'YData', y(1:i));
%drawnow; % first one is annecessary
%hold off; % unnecessary
while toc(t) < 0.05
continue
end
t1(i) = toc(t2);
%figure(2); % unnecessary
%hold on; % unnecessary
%plot(i,t1(i),'b*');
% replacing the above with a slightly faster version
set(p2, 'XData', 1:i, 'YData', t1(1:i));
if rem(i,10) == 0 % refreshing every 10 iterations
drawnow;
end
%hold off; % unnecessary
i = i + 1;
end
ANSWER TO PREVIOUS VERSION OF QUESTION
You can vectorize your loop by replacing it entirely with the following two statements:
% vectorizing num-to-string conversion
y4 = cellstr(strcat('<P',num2str(x1), '>'));
% deleting all spaces
y4 = cellfun(#(u) u(~isspace(u)), y4, 'UniformOutput', false)
This small adjustment makes your program run x4 faster on my PC.
Displaying/printing the results could also be done using the cellfun iterator: cellfun(#disp, y4)
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.
I have a matlab function that contain some constant parameter, I want to draw that function, on say same figure, using hold on (probably) while changing the value of that constant.
This my code:
close all
clear all
clc
m = 5;
x = 1:1:10;
y = m*x + 10;
h1 = figure;
plot(x,y)
m = 10;
figure(h1);
hold on
plot(x,y,': r')
When I tried using this code, I got two lines coincident on each others; and it looks matlab just used last value for the parameter m how can I make it use different values.
I found some stuff here, but doesn't fulfill my needs.
Any suggestions?
You need to recalculate y as well:
m = 5;
x = 1:1:10;
y = m*x + 10;
h1 = figure;
plot(x,y); hold on;
m = 10;
y = m*x + 10;
figure(h1);
plot(x,y,': r')
Or create an anonymous function:
x = 1:1:10;
f = #(m) m*x + 10;
%// and then:
h1 = figure;
plot(x,f(5) ); hold on;
plot(x,f(10),': r');
Currently, you're only updating m but you also have to calculate y again. This is why it plots exactly the same y (i.e. m is still 5) function when you issue the second plot.
You might want to use a simple for loop for that, like:
m = 5;
x = 1:1:10;
figure;
hold on;
for m=1:1:10
y = m*x + 10;
plot(x,y,': r')
end
In addition to the short answer - improving the plot..
%% Data Preparations
x = 1:10;
ms = 3; % number of different slopes
%% Graph Preparations
hold on;
% Prepare the string cell array
s = cell(1, ms);
% Handle storage
h = zeros(1, ms);
% Plot graphs
for m=1:ms
y = m*x + 10;
h(m)= plot(x,y,'Color',[1/m rand() rand()]);
s{m} = sprintf('Plot of y(m=%d)', m);
end
% Plot all or select the plots to include in the legend
ind = [ms:-1:1] .* ones(1,ms); % plot all
%ind = [ 1 3 4 ]; % plot selected
% Create legend for the selected plots
legend(h(ind), s{ind});
Additional advice: When working with MATLAB and you try to improve the performance of your code, you shoud try to avoid using for-loops since MATLAB is MATrix manipulation and that's what it can do best. Ones you've taken this philosophy in, you'll create the most beautiful code one-liners! ;)
This script is an adoption of Steve Lord's post.