Resizing of axes when plotting in the same figure - matlab

Please, create two functions to be able to reproduce what I mean:
First function:
function testPlot1()
pointData = rand(20000,3);
figure;
%hold on; % <- if commented out, does not work
plot3(pointData(:,1), pointData(:,2), pointData(:,3),'Marker', '.', 'MarkerEdgeColor', 'b','MarkerSize', 5, 'LineStyle', 'none');
axis equal;
xh = xlabel('X');
yh = ylabel('Y');
zh = zlabel('Z');
set([xh,yh, zh],...
'fontweight','bold',...
'fontsize',14,...
'color',[0,0,0]);
view(0,20);
end
Second function:
function testPlot2(fighandle)
axes(fighandle);
hold on;
plot3([0 3],[0 3],[0 3], 'r', 'LineWidth', 10);
end
If you now call
testPlot1();testPlot2(gca)
you will get the following:
If you however uncomment the "hold on" line in testPlot1() and call the above statement again, you will get:
To me this is unclear behavior. In the first case, testPlot1() creates a figure, draws the point cloud into it and modifies the axes properties. Then the call to testPlot2(gca) adds the line to the figure, but the line is clipped.
In the second case however the line is not clipped anymore. Why is it now not clipped and previously it was?
It seems to be related to the changes I make in the axes properties in testPlot1(). Could somebody explain this behavior to me? (why does it work with hold on, what do my changes in the axes properties cause)

hold on is a Matlab command (hold off turns it off again), where you can draw multiple elements on a single figure without the previous elements being erased.
What happens
If you call the plot function, a figure is created (or an already exisiting figure is used!) and Matlab draws a new plot into that figure. The previous plot that was in that figure is gone.
If you want to add more points to your plot, you can call hold on and then call plot again, this time with different numbers and maybe a different colour or so.
However, if you forget to turn hold off again for the active figure, any drawing activity you do (like plot) will be added to the figure. This is what happens in your second image in your question. You drew some points in the range 0 to 1, and then in the second function, you add some more but in the range 2 to 3. As a result, the axes expand to the range of 0 to 3.
Alternatively, you can call figure, which will cause a new figure to appear. figure_handle = figure(); will return a figure handle, which you can pass to your function, in case you have multiple figures and want to change one of them after a while.

Related

How to update a scatter3 plot (in a loop) in Matlab

Quite a simple question but just couldn't find the answer online... I want to visualise a point cloud gathered from a lidar. I can plot the individual frames but wanted to loop them to create a "animation". I know how to do it for normal plots with drawnow but can't get it working with a scatter3. If I simply call scatter3 again like I have done in the commented code then the frame that I am viewing in the scatter plot jumps around with every update (Very uncomfortable). How do i get the scatter3 plot to update to the new points without changing the UI of the scatter ie. Still be able to pan and zoom around the visualised point cloud while it loops through.
EDIT: The file is a rosbag file, I cannot attach it because it is 170MB. The problem doesn't happen when using scatter3 in a loop with a normal array seems to be something with using scatter3 to call a PointCloud2 type file using frame = readMessages(rawBag, i).
EDIT: The problem does not seem to be with the axis limits but rather with the view of the axis within the figure window. When the scatter is initialised it is viewed with the positive x to the right side, positive y out of the screen and positive z upwards, as shown in view 1. Then after a short while it jumps to the second view, where the axis have changed, positive x is now out of the screen, positive y to the right and positive z upwards (both views shown in figures). This makes it not possible to view in a loop as it is constantly switching. So basically how to update the plot without calling scatter3(pointCloudData)?
rawBag = rosbag('jackwalking.bag');
frame = readMessages(rawBag, 1);
scatter3(frame{1});
hold on
for i = 1:length(readMessages(rawBag))
disp(i)
frame = readMessages(rawBag, i);
% UPDATE the 3D Scatter %
% drawnow does not work?
% Currently using:
scatter3(frame{1})
pause(.01)
end
The trick is to not use functions such as scatter or plot in an animation, but instead modify the data in the plot that is already there. These functions always reset axes properties, which is why you see the view reset. When modifying the existing plot, the axes are not affected.
The function scatter3 (as do all plotting functions) returns a handle to the graphics object that renders the plot. In the case of scatter3, this handle has three properties of interest here: XData, YData, and ZData. You can update these properties to change the location of the points:
N = 100;
data = randn(N,3) * 40;
h = scatter3(data(:,1),data(:,2),data(:,3));
for ii = 1:500
data = data + randn(N,3);
set(h,'XData',data(:,1),'YData',data(:,2),'ZData',data(:,3));
drawnow
pause(1/5)
end
The new data can be totally different too, it doesn't even need to contain the same number of points.
But when modifying these three properties, you will see the XLim, YLim and ZLim properties of the axes change. That is, the axes will rescale to accommodate all the data. If you need to prevent this, set the axes' XLimMode, YLimMode and ZLimMode to 'manual':
set(gca,'XLimMode','manual','YLimMode','manual','ZLimMode','manual')
When manually setting the limits, the limit mode is always set to manual.
As far as I understood what you describe as "plots jumpying around", the reason for this are the automatically adjusted x,y,z limits of the scatter3 plot. You can change the XLimMode, YLimMode, ZLimMode behaviour to manual to force the axis to stay fixed. You have to provide initial axes limits, though.
% Mock data, since you haven't provided a data sample
x = randn(200,50);
y = randn(200,50);
z = randn(200,50);
% Plot first frame before loop
HS = scatter3(x(:,1), y(:,1), z(:,1));
hold on
% Provide initial axes limits (adjust to your data)
xlim([-5,5])
ylim([-5,5])
zlim([-5,5])
% Set 'LimModes' to 'manual' to prevent auto resaling of the plot
set(gca, 'XLimMode', 'manual', 'YLimMode', 'manual', 'ZLimMode', 'manual')
for i=2:len(x,2)
scatter3(x(:,i), y(:,i), z(:,i))
pause(1)
end
This yields an "animation" of plots, where you can pan and zoom into the data while continuous points are added in the loop

How to change limits of y-axis? `ylim` does not work

When the graph below is plotted, NSS1 which is simply a constant set equal to one is right on the top border of the graph and thus hard to see.
How can I change the length of the y-axis to say 1.2 so that the NSS1 can be seen more clearly?
lambda=5;
tau=0:30;
tau(1)=0.000001;
NSS1=1*ones(1,31);
NSS2=(1-exp(-tau/lambda))./(tau/lambda);
NSS3=((1-exp(-tau/lambda))./(tau/lambda)-exp(-tau/lambda));
%ylim([0, 1.2])
plot(tau,NSS1,'-k*',tau,NSS2,'-k+',tau,NSS3,'-ko');
xlabel('t = 0 to 30y', 'FontSize',30)
ylabel('yield','FontSize',30)
The reason why ylim doesn't work if you put it before the plot command is that there is no axes object it can relate to.
So there are two options:
First, you create an axes object and hold it with hold on, so the upcoming plot is plotted on the same axis.
ax = axes; hold on;
ylim([0, 1.2])
plot(tau,NSS1,'-k*',tau,NSS2,'-k+',tau,NSS3,'-ko');
or second, you plot first, the command automatically generates an axes object and you can modify its y-limits afterwards:
plot(tau,NSS1,'-k*',tau,NSS2,'-k+',tau,NSS3,'-ko');
ylim([0, 1.2])

Multiple axis: using plot vs. line

I have 2 sets of data I want to plot on the same graph.
First an histogram:
hist(data1);
ax1 = gca;
I set the next set of axis, y on the other side
ax2 = axes('Position',get(ax1,'Position'),...
'XAxisLocation','bottom',...
'YAxisLocation','right',...
'Color','none',...
'XColor','k');
If I use line() to plot my data it works:
line(data2a, data2b, 'Color', 'r', 'LineStyle', '-', 'Marker', '.', 'Parent', ax2);
But if I use plot(), the histogram is erased and both axis appear on the left.
plot(ax2, data2a, data2b);
Can somebody figure out why the second axis is not valid for plot()?
You should check out doc hold.
Axes in MATLAB have the 'NextPlot' property, specifying what to do when a new plot-function is issued on this axis.
The default for 'nextplot' is replace, meaning that before anything new is drawn, existing plots are erased.
Using hold(ax, 'on') or set(ax, 'nextplot', 'add') you can specify that new plots are added to the existing ones, instead of replacing them.
The reason that line and plot behave differently is, that high level functions (like plot) respect this axis property, while low-level functions like line, patch and others do not. They are added to axis in any case and do not remove existing children.
EDIT:
Now I'm noticing that ax2 should be empty in your case - maybe just try the above nevertheless ;)

two simultaneous plots in matlab

I want to plot two simultaneous plots in two different positions in Matlab, looped animations and both are different animations, one with hold on and another with hold off.
Also, one is 2D and one is 3D
I am doing something like this:
for i=1:some_number
axes('position',...)
plot(...);hold on;
axes('position',...)
clf
plot3(...) (or fill3 but has to do with 3d rendering)
view(...)
set(gca, 'cameraview',...)
set(gca,'projection',...)
mov(i)=getframe(gcf)
end
Q1. Do the set properties effect first axes? if so, how to avoid that?
Q2. In my plot hold on did not work. Both were instantenous. like using hold off. How do I make it work?
Q3. I hope the mov records both axes.
P.S. I hope clf is not a problem. I must use clf or if there are equivalents more suitable in my case do suggest me.
You need to store the return from the axes function and operate specifically on a given axes with subsequent function calls, rather than just the current axes.
% Create axes outside the loop
ax1 = axes('position',...);
ax2 = axes('position',...);
hold(ax1, 'on');
for i=1:some_number
plot(ax1, ...);
cla(ax2); % use cla to clear specific axes inside the loop
plot3(ax2, ...) (or fill3 but has to do with 3d rendering)
view(ax2, ...)
set(ax2, 'cameraview',...)
set(ax2,'projection',...)
mov(i)=getframe(gcf)
end
Here is a snippet from a piece of my code which plots orbits of three celestial bodies which I think will help you:
for i = 1:j, %j is an arbitrary number input by the user
plot(x, y, '*')
plot(x2, y2, 'r')
plot(xa, ya, '+')
grid on
drawnow %drawnow immediately plots the point(s)
hold on %hold on keeps the current plot for future plot additions
%dostuff to x,y,x2,y2,xa,ya
end
The two main functions you want are the drawnow and hold on.
Just to note: x,y,x2,y2,xa, and ya change with each iteration of the loop, I have just omitted that code.
EDIT: I believe the drawnow function will solve your problem with hold on.
I think this may solve your problem.
for i=1:some_number
axes('position',...)
plot(...);
drawnow %also note that you must not put the ; at the end
hold on %see above comment
axes('position',...)
clf
plot3(...) (or fill3 but has to do with 3d rendering)
view(...)
set(gca, 'cameraview',...)
set(gca,'projection',...)
mov(i)=getframe(gcf)
end

Matlab: Plot a subplot with hold on and hold off in a loop without always calling xlabel, ylabel, xlim, etc

Matlab question: this might be really simple but I can't figure it out...I'm pretty new. I have a plot window, broken up into two subplots, lets call them A and B, which have different labels and limits. I (hold on), make several plots to B, then I (hold off), then start iterating. In the loop, I want to update both A and B with NEW plots, but I want the axis labels, and xlim and ylim to stay the same, WITHOUT having to call xlabel, xlim, etc every iteration.
Now, (hold off) destroys all axis properties. How do I save the axis properties so I don't have to keep calling xlabel, etc in the loop? I've tried newplot, setting the Nextplot property, etc to no avail. I'd like a simple solution please...not something like re-writing the plot command. Thanks!
hfig=figure();
hax = axes('Parent',hfig);
plot(hax,x,y);
hold on
plot(hax,x1,y1);
%this hold off resets the axes
hold off
while (1)
subplot('Position',[.07 .05 .92 .44]);
%I want to do this without having to call xlabel, ylabel, etc
%over and over
plot(newx, newy);
xlabel()
ylabel()
hold on
plot(newx1, newx2)
hold off
...
end
One solution here is to initialize your plot and axes properties before your loop, then within your loop set the 'NextPlot' property of the axes to 'replacechildren' so that only the plot objects (and not the axes settings) will be changed on the next call to PLOT:
hFigure = figure();
hAxes = axes('Parent',hFigure);
plot(hAxes,x,y);
hold on;
plot(hAxes,x1,y1);
xlabel(...); %# Set the x label
ylabel(...); %# Set the y label
xlim([...]); %# Set the x limits
ylim([...]); %# Set the y limits
while (1)
set(hAxes,'NextPlot','replacechildren');
plot(hAxes,newx,newy);
hold on;
plot(hAxes,newx1,newx2);
...
end
This should maintain the settings for hAxes when new data is plotted in the loop.