So I have a simple loop in MATLAB that does the following:
for p = 1:100
x = 4.*randn(1,100);
y = 7.*randn(1,100);
figure(1)
plot(randn(1,100));
figure(2);
plot(randn(1,100));
end
The x and y are made up, but that is the jist of it. Anyway, when I run this code, not surprisingly, MATLAB will make two figures and plot accordingly. The problem is, I get a sort of 'blinking' between figures when I do this, and it makes the quality of seeing x and y evolve over time poorer.
I discovered a way to make one of the plots smoother like this:
figure(1);
for p = 1:100
x = 4.*randn(1,100);
y = 7.*randn(1,100);
plot(randn(1,100));
drawnow
end
If I do this, then of course figure(1) will plot very smoothly showing x nicely, without figure(1) 'blinking' between plots, but now I cant show figure(2) or y!
How can I plot both those quantities on different figures (not subplots) smoothly without 'blinking'?
EDIT:
Thanks Geodesic for your answer, the solution works, however there is a subtlety that I did not think would be an issue, however it is.
1) I am unable to use 'imagesc' with this solution.
For example,
figure(1);
aone = axes;
figure(2);
atwo = axes;
for p = 1:100
x = 4.*randn(1,100);
y = 7.*rand(10,100);
plot(aone,x);
drawnow;
imagesc(atwo,y);
drawnow;
end
In this case the part with imagesc(atwo, y) crashes.
Your flicker is because you're generating each figure window again and again through the loop, which is forcing the window to come to the foreground each time. Generate the figures first, attach some axes to them, and plot your data to each axis like so:
figure(1);
aone = axes;
figure(2);
atwo = axes;
for p = 1:100
x = 4.*randn(1,100);
y = 7.*randn(1,100);
plot(aone,randn(1,100));
drawnow;
imagesc(y,'Parent',atwo);
drawnow;
end
Edit: functions like plot take an axis argument directly, but imagesc does not. In this particular case you'll need to send a Property Name/Value pair in as an argument. The 'Parent' of the image generated will be our axis atwo (see above).
For p = 1, create the plots you need, using the plot command or the imagesc command. Keep the handle of the resulting graphics object by getting an output argument: for example h = plot(.... or h = imagesc(..... This will be a Handle Graphics lineseries or image object, or something else, depending on the particular plot type you create.
For p = 2:100, don't use the plotting commands directly, but instead update the relevant Data properties of the original Handle Graphics object h. For example, for a lineseries object resulting from a plot command, set its XData and YData properties to the new data. For an image object resulting from an imagesc command, set its CData property to the new image.
If necessary, call drawnow after updating to force a flush of the graphics queue.
Related
What is a proper way to separate the initialization and the display of plots in Matlab? (I mean plots in a wide sense here; could be plot, plot3, scatter etc.) To give a concrete example, I have a pretty complex 3D visualization that uses sphere and mesh to draw a static sphere mesh and then scatter3 to plot a moving trajectory on the sphere. To be able to do this in real time I have implemented some simple optimizations, such as only updating the scatter3 object each frame. But the code is a bit messy, making it hard to add additional features that I want, so I would like improve code separation.
I also feel like it might sometimes be useful to return some kind of plot object from a function without displaying it, for example to combine it with other plots in a nice modular way.
An example of what I have in mind would be something like this:
function frames = spherePlot(solution, options)
% Initialize sphere mesh and scatter objects, configure properties.
...
% Configure axes, maybe figure as well.
...
% Draw sphere.
...
if options.display
% Display figure.
end
for step = 1:solution.length
% Update scatter object, redraw, save frame.
% The frames are saved for use with 'movie' or 'VideoWriter'.
end
end
Each step might also be separated out as a function.
So, what is a neat and proper way to do stuff like this? All documentation seems to assume that one wants to display everything right away.
For example
% some sample data
N = 100;
phi = linspace(-pi, pi, N);
theta = linspace(-pi, pi, N);
f = #(phi, theta) [sin(phi).*cos(theta); sin(phi).*sin(theta); cos(phi)];
data = f(phi, theta);
% init plot
figure(1); clf
plot3(data(1,:), data(2,:), data(3,:)); % plot path, not updated
hold on
p = plot3([0 data(1,1)], [0 data(2,1)], [0 data(3,1)]); % save handle to graphics objects to update
s = scatter3(data(1,1), data(2,1), data(3,1), 'filled');
axis equal
xlabel('x'); ylabel('y'); zlabel('z');
t = title('first frame'); % also store handle for title or labels to update during animation
% now animate the figure
for k = 1:N
p.XData = [0 data(1,k)]; % update line data
p.YData = [0 data(2,k)];
p.ZData = [0 data(3,k)];
s.XData = data(1,k); % update scatter data
s.YData = data(2,k);
s.ZData = data(3,k);
t.String = sprintf('frame %i', k); % update title
drawnow % update figure
end
Basically you can update all values for a graphics handle, in this case 'p' and 's'. If you open the matlab doc for plot or plot3 you will find a link to all properties of that primitive: e.g. Line Properties. Similar documentation pages exist for scatter/imagesc etc.
So the general idea is to first create a figure with the first frame, save the handles to the objects you would like to update (p = plot(...), and then enter a loop in which you update the required property of that graphics object (e.g. p.Color = 'r', or p.XData = ...).
Question:
Write a function called point_cloud that takes one scalar as an input argument (the function does not have to check the format of the input) and has no output argument.
If it is called like this, point_cloud(100), then it plots 100 points. Each point has a random x coordinate and a random y coordinate, each of which is gotten by a call to randn, which uses a normal distribution with a standard deviation equal to 1. The range of the plot axes should be −5 to 5 in both the x and y dimensions. The grid should be turned off. The points should be plotted and displayed one at a time by calling plot with only one point specified and, following the call of plot, by a call of drawnow, which causes the point to be plotted immediately. The command hold on should be included so that all previous points are retained when a new point is plotted.
Figure 2.41 shows an example view of the plot after point_cloud(100000) has completed its point-by-point plotting on a Mac. (Note that on Windows the points are much larger. Also note that it takes a long time to plot this many points with drawnow. Finally, try zooming in the middle.)
Figure 2.41
My Code:
function point_cloud(N)
hold on
grid off
axis([-5,5,-5,5])
for ii = 1:N
plot(randn(ii));
drawnow;
end
I know this is wrong, but I'm not sure how to solve this problem. Can someone help?
Solved code:
function point_cloud(N)
figure
hold on
grid off
axis([-5,5,-5,5])
x = randn(N,1);
y = randn(N,1);
for ii = 1:N
plot(x(ii),y(ii),'b.');
drawnow;
end
You do not need the for loop at all. And drawing the plot each iteration is very time consuming. How about rather using the scatter function.
figure
hold on
grid off
axis([-5,5,-5,5])
x = randn(N,1);
y = randn(N,1);
scatter(x,y,'b.')
This will be a lot faster.
To add to the other answer, here is the code as a function, with the added functionality that the points are one pixel on Windows as well:
function point_cloud(N)
f = figure;
x = randn(N,1);
y = randn(N,1);
scatter(x,y,1/36,'b.');
f.GraphicsSmoothing = 'off';
grid off
axis([-5,5,-5,5])
axis equal
end
The size of the markers is set with the third parameter of scatter: 1/36. The graphics smoothing of the figure needs to be set to 'off' as well, to make sure that the pixels don't become blurry or lighter.
Here's a 3D version:
function point_cloud3D(N)
f = figure;
x = randn(N,1);
y = randn(N,1);
z = randn(N,1);
scatter3(x,y,z,1/36,'b.');
f.GraphicsSmoothing = 'off';
grid off
axis([-5,5,-5,5,-5,5])
axis square
view(3)
end
figure;
ax1 = axes;
figure;
ax2 = axes;
x = 0; y = 0;
while ishandle(ax1) && ishandle(ax2)
x = x + 1;
y = y + 1;
figure(1)
scatter(x,y, 'MarkerEdgeColor', 'red')
hold on
figure(2)
scatter(x,y, 'MarkerEdgeColor', 'blue')
hold on
end
In my script I have multiple figures, which are going to be updated in a loop. The figures have to be displayed, while the script is running. Unfortunately the currently updated figure is always popping in the foreground, which makes it impossible to monitor a certain figure. I understand that the calling of figure(1) and figure(2) causes this behaviour, but I how can I plot to these figures, without bringing the window into foreground?
As mikkola suggested in a comment, you can specify to which axes scatter or plot add data points. However, there is a better method: create a single line object, and update its xdata and ydata properties. This is both faster and more memory efficient. Your code would become:
x = 0; y = 0;
figure;
h1 = plot(x,y,'ro');
figure;
h2 = plot(x,y,'bo');
while ishandle(h1) && ishandle(h2)
x = x + 1;
y = y + 1;
h1.XData(end+1) = x;
h1.YData(end+1) = y;
h2.XData(end+1) = x;
h2.YData(end+1) = y;
drawnow
pause(0.1)
end
I keep a set of rules of thumb for when working with MATLAB handle graphics. These are relevant to this question:
Use figure only to create a new figure, or to bring an existing figure to the front (which you want to avoid in general, but sometimes is necessary).
Always specify with which figure or axes you want to work, by keeping and using their handles. I never rely on gcf or gca (not explicitly nor implicitly). Using the current figure is useful when typing on the command line, but in a script or a function there is the real danger than someone clicks randomly on windows while the function is executing. Creating a window then writing to gcf could end up writing to a different figure (really, I click on random things all the time).
Don't create more objects than necessary. Creating a new line object for every point you plot is wasteful.
Note also that plot(...'o') is equivalent to scatter(...) unless you specify a different color or size for each point. But using the dot size or color to specify additional information is not a good way to convey that information. Read Tufte's "The visual display of quantitative information" if you're interested in learning about effective communication through graphs.
The relevant part can be found in the part of the documentation of scatter that includes the input ax:
scatter(ax,___) plots into the axes specified by ax instead of into
the current axes.
This allows the user to specify an axis handle pointing to which axes should be used for drawing the scatter plot. Thus if you skip using figure in your code and use the ax input instead, you avoid the "bring to front" behavior associated with figure.
You can modify your code as follows:
figure;
ax1 = axes;
figure;
ax2 = axes;
x = 0; y = 0;
while ishandle(ax1) && ishandle(ax2)
x = x + 1;
y = y + 1;
scatter(ax1, x,y, 'MarkerEdgeColor', 'red')
hold on
scatter(ax2, x,y, 'MarkerEdgeColor', 'blue')
hold on
end
I want to update a plot with multiple data lines/curves as fast as possible. I have seen some method for updating the plot like using:
h = plot(x,y);
set(h,'YDataSource','y')
set(h,'XDataSource','x')
refreshdata(h,'caller');
or
set(h,'XData',x,'YData',y);
For a single curve it works great, however I want to update not only one but multiple data curves. How can I do this?
If you create multiple plot objects with a single plot command, the handle returned by plot is actually an array of plot objects (one for each plot).
plots = plot(rand(2));
size(plots)
1 2
Because of this, you cannot simply assign another [2x2] matrix to the XData.
set(plots, 'XData', rand(2))
You could pass a cell array of new XData to the plots via the following syntax. This is only really convenient if you already have your new values in a cell array.
set(plots, {'XData'}, {rand(1,2); rand(1,2)})
The other options is to update each plot object individually with the new values. As far as doing this quickly, there really isn't much of a performance hit by not setting them all at once, because they will not actually be rendered until MATLAB is idle or you explicitly call drawnow.
X = rand(2);
Y = rand(2);
for k = 1:numel(plots)
set(plots(k), 'XData', X(k,:), 'YData', Y(k,:))
end
% Force the rendering *after* you update all data
drawnow
If you really want to use the XDataSource and YDataSource method that you have shown, you can actually do this, but you would need to specify a unique data source for each plot object.
% Do this when you create the plots
for k = 1:numel(plots)
set(plots(k), 'XDataSource', sprintf('X(%d,:)', k), ...
'YDataSource', sprintf('Y(%d,:)', k))
end
% Now update the plot data
X = rand(2);
Y = rand(2);
refreshdata(plots)
You can use drawnow:
%Creation of the vectors
x = 1:100;
y = rand(1,100);
%1st plot
h = plot(x,y);
pause(2);
%update y
y = rand(1,100);
set(h,'YData',y)
%update the plot.
drawnow
I know how to create the Bode plots with bode() function. If I want to overlap two or more systems frequency responses, I use
bode(sys1,sys2,...)
or
hold on
When I want to reach the plot in order to put a legend with text(), for instance, is easy to reach the second plot. Something like the figure pointer always returns to the second plot (phase graph).
i.e., if try these lines:
G = tf([1],[1 6]); figure(1); bode(G); text(10,-20,'text');
G = tf([1],[1 6]); figure(2); bode(G); text(10,-20,'text');
when I return to the first figure, with figure(1), and try
figure(1); text(10,-20,'text')
legend is displayed in the second plot (Phase plot)
I try these other lines:
P = bodeoptions; % Set phase visiblity to off
P.PhaseVisible = 'off';
G = tf([1],[1 6]);
figure(1); bode(G,P); text(10,-20,'text');
figure(1); text(10,-20,'text');
As you can see, even I turn off the phase plot visiblity, the legend is not displayed.
Essentialy, my question is, how do I reach first and second plots, one by one? I tried with subplot(), but it is pretty clear this is not the way Matlab traces these plots.
Thanks in advance.
It all comes to getting into upper plot, since after bodeplot command the lower one is active. Intuitively one would want to call subplot(2,1,1), but this just creates new blank plot on top of if. Therefore we should do something like this:
% First, define arbitrary transfer function G(s), domain ww
% and function we want to plot on magnitude plot.
s = tf('s');
G = 50 / ( s*(1.6*s+1)*(0.23*s+1) );
ww = logspace(0,3,5000);
y = 10.^(-2*log10(ww)+log10(150));
hand = figure; % create a handle to new figure
h = bodeplot(G,ww);
hold on;
children = get(hand, 'Children') % use this handle to obtain list of figure's children
% We see that children has 3 objects:
% 1) Context Menu 2) Axis object to Phase Plot 3) Axis object to Magnitude Plot
magChild = children(3); % Pick a handle to axes of magnitude in bode diagram.
% magChild = childern(2) % This way you can add data to Phase Plot.
axes(magChild) % Make those axes current
loglog(ww,y,'r');
legend('transfer function','added curve')
you can get magnitude and phase data separately for each system using:
[mag,phase] = bode(sys,w)
now you can use subplot or plot to plot the diagram you want.
The only solution I was able to perform is taking into account axis position. It is not very clean but it works.
Here is the code to select mag plot:
ejes=findobj(get(gcf,'children'),'Type','axes','visible','on');
posicion=get(ejes,'pos');
tam=length(posicion);
for ii=1:tam
a=posicion{ii}(2);
vectorPos(ii)=a;
end
[valorMax,ind]=max(vectorPos); % min for choosing the phase plot
axes(ejes(ind))