Updating plot while maintaining axes rotations? - matlab

So far I have a 3d plot that updates in realtime during a Monto-Carlo simulation of a Kessel Run. I want the plot to be rotatable while updating, but with the rotate3d flag enabled, when I call drawnow after updating the plot data, it resets the axes rotation to the default orientation, canceling whatever the user changed it to. My relevant code is as follows:
s = scatter3(pg(:,1), pg(:,2), pg(:,3), 'O', 'filled');
set(s, 'MarkerEdgeColor', 'none', 'MarkerFaceColor', 'k');
hold on
p_x = curr_path(:,1);
p_y = curr_path(:,2);
p_z = curr_path(:,3);
p = plot3(p_x, p_y, p_z);
set(p, 'LineWidth', 2, 'Color', 'k');
p_x = longest.path(:,1);
p_y = longest.path(:,2);
p_z = longest.path(:,3);
p = plot3(p_x, p_y, p_z);
set(p, 'LineWidth', 2, 'Color', 'r');
p_x = shortest.path(:,1);
p_y = shortest.path(:,2);
p_z = shortest.path(:,3);
p = plot3(p_x, p_y, p_z);
set(p, 'LineWidth', 2, 'Color', 'b');
sample_str = sprintf('%d samples', sample);
short_str = sprintf('shortest: %g parsecs', shortest.dist);
long_str = sprintf('longest: %g parsecs', longest.dist);
title(sprintf('%s, %s, %s', sample_str, short_str, long_str));
xlim([-10 10]);
ylim([-10 10]);
zlim([-10 10]);
hold off
drawnow
This is executed every time I update the data being drawn on the plot. What can I add to ensure that the axes rotation is maintained during an update?

I hope I understood you problem right. Well, here goes!
I suppose the problem is related to using plot3, which apparently resets the view settings of the figure to their defaults.
I verified this with the following program:
figure;
x = randn(10,3);
p = plot3(x(:,1), x(:,2), x(:,3));
drawnow
pause; %// do rotations in the figure GUI, press a button when done
x = randn(10,3);
p = plot3(x(:,1), x(:,2), x(:,3)); %// rotations reset
drawnow;
pause
You can rotate the figure when the pause is active, but when the pause is released and the second call to plot3 made, the rotation settings are reset.
A solution which avoids the reset is to directly update the XData, YData, and ZData in the already drawn graphics object. This can be achieved as follows:
figure;
x = randn(10,3);
p = plot3(x(:,1), x(:,2), x(:,3));
drawnow
pause; %// do rotations in the figure GUI, press a button when done
x = randn(10,3);
set(p, 'XData', x(:,1), 'YData', x(:,2), 'ZData', x(:,3)); %// no reset!
drawnow;
pause
So whatever code you have, use the handle of the graphics object to directly update the line properties to avoid the reset.

I had the same problem, in Matlab 2015b you can solve it in this simple way.
[az,el] = view;
scatter3(x,y,z);
view(az,el);
drawnow

Related

Linkaxes of Heterogenous Matrixes in Square Control of Matlab?

I am trying to have the linked control of two matrices which have different xlims and ylims by linkaxes() where there exists an inverse function between the two matrices.
The axes of the matrices (signal D / square matrix D_square) are ax2/ax4, respectively.
The dimensions of the axes are
The limits of ax2 are different than those of ax4, since linkaxes() just makes all input axes have identical limits.
A selection of Fig. ax2 (= zooming an area in the figure) should bring forward appropriate selection in Fig. ax4.
I think you should tell Matlab how to do it.
The only difference between the two datasets is that data of ax2 is the matrix D, while the data of ax4 is the matrix D_square=squareform(D, 'tomatrix') which has an inverse transform, D=squareform(D_square, 'tomatrix') so it is possible to have a control between the two figures.
Code
data=randi(513,513);
D=mat2gray(pdist(data, 'correlation'));
% Set normalized outer position (x,y,width,height)
ax2 = axes('OuterPosition', [0.51 0.5 0.5 0.5]);
plot(D, 'Parent', ax2);
axis(ax2, 'square');
title('Corr pdist');
cbar2 = colorbar();
set(ax2, 'XLim', [0 size(D,2)]);
set(cbar2, 'Visible', 'off')
grid minor;
% Force a draw event to have the axes determine where the
labelconverter = #(x)sprintf('%.2g', x); % http://stackoverflow.com/a/35780915/54964
callback = #(varargin)set(ax2, 'xticklabels', arrayfun(labelconverter, get(ax2, 'xtick'), 'uniform', 0));
set(hFig, 'SizeChangedFcn', callback);
callback(); % necessary for the original small window and its scientific numbering
%% Problem here!
D_square=squareform(D, 'tomatrix');
ax4 = axes('OuterPosition', [0.51 0 0.5 0.5]);
set(ax4, 'XLim', [0 size(D_square,2)]);
image( D_square, 'Parent', ax4 ); % TODO problem here!
set(gca,'YDir','normal');
colormap('parula'); colorbar;
axis(ax4, 'square');
title('Square Corr pdist');
linkaxes([ax2,ax4], 'x');
Output
where Fig. ax4 gets substituted because lims of ax4 are substituted by ax2, vice versa for linkaxes([ax4,ax2], 'x'); in the last line.
Some fixes in Suever's answer
Some changes
Use imagesc() in him=imagesc( D_square, 'Parent', ax4 );
initiate/let things be square by axis(ax4, 'square'); just after imagesc(); not sure why
unknown about if linkaxes([ax2,ax4], 'xy'); or just one axis linkaxes([ax2,ax4], 'x');
Code
data=randi(513,513);
D=mat2gray(pdist(data, 'correlation'));
% Figure out the xrange of your first plot
xrange = [1, numel(D)];
% Set normalized outer position (x,y,width,height)
ax2 = axes('OuterPosition', [0.51 0.5 0.5 0.5]);
plot(D, 'Parent', ax2);
axis(ax2, 'square');
title('Corr pdist');
cbar2 = colorbar();
set(ax2, 'XLim', [0 size(D,2)]);
set(cbar2, 'Visible', 'off')
grid minor;
% Force a draw event to have the axes determine where the
labelconverter = #(x)sprintf('%.2g', x); % http://stackoverflow.com/a/35780915/54964
callback = #(varargin)set(ax2, 'xticklabels', arrayfun(labelconverter, get(ax2, 'xtick'), 'uniform', 0));
set(hFig, 'SizeChangedFcn', callback);
callback(); % necessary for the original small window and its scientific numbering
D_square=squareform(D, 'tomatrix');
ax4 = axes('OuterPosition', [0.51 0 0.5 0.5]);
set(ax4, 'XLim', [0 size(D_square,2)]);
him=imagesc( D_square, 'Parent', ax4 );
axis(ax4, 'square'); % To let things be square!
set(gca,'YDir','normal');
colormap('parula'); colorbar;
title('Square Corr pdist');
% Set XData AND YData (to keep things square)
set(him, 'XData', xrange, 'YData', xrange);
linkaxes([ax2,ax4], 'xy');
where the problem is
to keep things square by set(him, 'XData', xrange, 'YData', xrange);
and to initiate/let them be square by axis(ax4, 'square'); just after imagesc(); code not working as expected having initiation of square shape too far from plotting for some reason.
I think personally that it may not be possible to have strict control (both x,y axes by linkaxes([ax2,ax4], 'xy')) between the two figures.
I think only one axis (by linkaxes([ax2,ax4], 'x') or linkaxes([ax2,ax4], 'y')) can be controlled at least because there exists an inverse function. Some empirical evidence
Output in linkaxes([ax2,ax4], 'xy'); is two empty figures.
Output in linkaxes([ax2,ax4], 'x'); is one figure with some graph and full matrix. Similar with y control.
Active control of square shape
Here active control of square shape by set(him, 'XData', xrange, 'YData', xrange);.
Outputs with keeping square in original view
where one full matrix.
Outputs without keeping square in original view
Disable set(him, 'XData', xrange, 'YData', xrange);
where Fig.ax4 is right. No
Successful Output from Suever's 2nd edit
Original view in x control by linkaxes([ax2,ax4], 'x'); and other changes about labels as described in Suever's answer
where the medium picture is also functional.
How can you tell Matlab the relation between the figures in using linkaxes()?
How can you have a linked control between the two figures in Matlab?
You're having issues because the image in your bottom axes is 513 x 513 (xlims = [0.5 513.5]) whereas the xlimits of your top axes [0 131328]. If you need them to be the same, you can simply alter the XData and YData of your image to be the same as your XLims for your top plot.
% Figure out the xrange of your first plot
xrange = [1, numel(D)];
% Set XData AND YData (to keep things square)
him = image( D_square, 'Parent', ax4 );
set(him, 'XData', xrange, 'YData', xrange);
Now when you call linkaxes (which links XLims) they should move together. You will also need to call axis(ax4, 'tight') to refit the axes to the new image data.
I have included a modified version of your code below that also keeps the xticklabels the same between the two plots.
hFig = figure();
data = randi(513,513);
D = mat2gray(pdist(data, 'correlation'));
D_square = squareform(D, 'tomatrix');
% Set normalized outer position (x,y,width,height)
ax2 = axes('OuterPosition', [0.51 0.5 0.5 0.5]);
plot(D, 'Parent', ax2);
axis(ax2, 'square');
title('Corr pdist');
cbar2 = colorbar();
set(ax2, 'XLim', [0 size(D,2)]);
set(cbar2, 'Visible', 'off')
grid minor;
% Force a draw event to have the axes determine where the
labelconverter = #(x)sprintf('%.2g', x); % http://stackoverflow.com/a/35780915/54964
callback = #(varargin)set(ax2, 'xticklabels', arrayfun(labelconverter, get(ax2, 'xtick'), 'uniform', 0));
set(hFig, 'SizeChangedFcn', callback);
callback(); % necessary for the original small window and its scientific numbering
ax4 = axes('OuterPosition', [0.51 0 0.5 0.5]);
him = imagesc( D_square, 'Parent', ax4 ); % TODO problem here!
% Set the XData and YData of the image
set(him, 'xdata', [1, size(D, 2)], 'ydata', [1, size(D,2)])
set(ax4,'YDir','normal');
colormap('parula');
colorbar;
axis(ax4, 'square');
% Fit the axes to the new XData and YData
axis(ax4, 'tight')
title('Square Corr pdist');
% Link the two together
linkaxes([ax2,ax4], 'x');
% Ensure that the labels ALSO remain the same
linkprop([ax2,ax4], 'XTickLabel')

matlab: how do I do animated plot in a figure of two subplot

There is example of the web that shows how to do animated plot in a single figure.
However, I want to do two subplots in a single figure, such that they will show animation in a first subplot, and then the animation ina second subplot.
Using 'figure(1)' or 'figure (2)' and 'hold on', I can do the animation plot as follows. However, How do I call the subplot to do the similiar things?
So the effect I am looking for is: 1) figure that is opened and has two subplot. 2) plot the animated curve in the 1st subplot, then plot the animated curve in the 2nd subplot. 3) I want to go back to the 1st subplot to plot more things, and also go to 2nd subplot to plot more things.
figure(1); hold on; 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
Just do the subplot's in the loop:
for k = 1:numel(x)
subplot(1,2,1)
plot(x(k),y(k),'-') %// Choose your own marker here
subplot(1,2,2)
plot(x(1:k),y(1:k))
%// MATLAB pauses for 0.001 sec before moving on to execue the next
%%// instruction and thus creating animation effect
pause(0.001);
end
% Easiest way
x = rand(1, 11); y = rand(1, 11);
z = rand(1, 11); a = rand(1, 11);
figure
for i = 1 : 10
subplot(211)
plot(x(i : i+1), y(i : i+1), '.-k');
hold on; % include this if you want to show plot history
subplot(212)
plot(z(i : i+1), a(i : i+1), '.-k');
drawnow;
pause(0.1);
end
% If you don't want to call "plot" interatively
x = rand(1, 11); y = rand(1, 11);
z = rand(1, 11); a = rand(1, 11);
figure
subplot(211)
p1 = plot(NaN, NaN, 'marker', 'o');
subplot(212)
p2 = plot(NaN, NaN, 'marker', 'd');
for i = 1 : 10
set(p1, 'xdata', x(i : i+1), 'ydata', y(i : i+1));
set(p2, 'xdata', z(i : i+1), 'ydata', a(i : i+1));
drawnow;
pause(0.1);
end
First define your plot as a construct, so p1 = plot(x,y). Then you set up your loop and in the loop your write
set(p1,'YData',y);
This will update the plot p1s YData which is y. If you want to see it in an animated form just add a pause(0.1) %seconds after the set.

Shift the z -value of contour plot in Matlab 2014b

I'm trying to make a figure of a surface plot, and beneath the surface I wish to show the contour lines, but I want the contour to be at z = -1 instead of at the default value 0. I found a previous post about this problem here, but when I try the solution the contour is still at z = 0. Maybe it has something to do with the version of MATLAB I'm using, which is 2014b?
Any ideas on how to make it work?
The code I tried:
%# plot surface and contour
Z = peaks;
surf(Z), hold on
[~,h] = contourf(Z); %# get handle to contourgroup object
%# change the ZData property of the inner patches
hh = get(h,'Children'); %# get handles to patch objects
for i=1:numel(hh)
zdata = ones(size( get(hh(i),'XData') ));
set(hh(i), 'ZData',-10*zdata)
end
So, I couldn't really figure out to do it as proposed in the example I found and posted, but I found a way that works. What I ended up doing was basically this:
figure
hold on
surf(X,Y,Z+1);
contour(X,Y,Z);
zz = get(gca,'ZTick');
set(gca,'ZTickLabel',sprintf('%3.1f\n',zz-1));
This gets me the surf and contour in the same figure, but yields some problems with color mappings.
I figured out how to solve the problem with color mappings the user Kine faced. Note: I've done the following code on MATLAB R2015b:
offset = 0.5;
plotHandle = surfc(X1, Y1, Z1);
hold on;
% This line moves the surface up from its original location and increases the space between the surface and the contour plot
plotHandle(1).ZData = plotHandle.ZData + offset;
% However in doing so the color mappings are changed. So, the line below restores these mappings
plotHandle(1).CData = plotHandle.CData - offset;
% This line changes fills the contour plot
plotHandle(2).Fill = 'on';
grid on;
% The following lines draw critical areas on the contour line, as it was more readable in my case
axisHandle = gca;
ZHeight = axisHandle.ZLim(1);
plot3(X2, Y2, ZHeight, 'o', 'MarkerSize', 10, 'LineWidth', 1, 'Color', 'k', 'MarkerFaceColor', 'm');
plot3(Y2, X2, ZHeight, 'o', 'MarkerSize', 10, 'LineWidth', 1, 'Color', 'k', 'MarkerFaceColor', 'm');
hold off
I got the same problem.
And finally, I got the contourf on plane Z=-10.
My MATLAB version is
MATLAB Version: 8.5.0.197613 (R2015a)
hope the codes work 4 you
clear all
clc
[X,Y,Z] = peaks;
[~,hContour] = contourf(X,Y,Z,20,'edgecolor','none');
hContour.ContourZLevel = -10; % set the contour's Z position
view(44,30)
colormap(jet)

Extract outline from MATLAB Isosurface

I have an isosurface I've plotted in MATLAB, e.g.:
And I'd like to extract the outline from this, for the given view settings that I currently have. The output I expect is like this (produced by GIMP):
Is there any way to programatically do this so I don't have to manually do it in GIMP?
Is this good enough? Edge detection taken from the bwboundaries documentation.
clear, close all
[x y z v] = flow;
figure(1)
p = patch(isosurface(x, y, z, v, -3));
set(p, 'FaceColor', 'red', 'EdgeColor', 'none');
daspect([1 1 1])
view(3)
grid off
axis off
print -dbmp test
I=imread('test.bmp');
G = im2bw(I, graythresh(I));
[B,L] = bwboundaries(~G,'noholes');
for k = 1:length(B)
boundary = B{k};
plot(boundary(:,2), -boundary(:,1), 'k', 'LineWidth', 2)
hold on
end
hold off
Which results in:

Loop over subplots

Somehow, the title, xlabel, ylabel, ylim and xlim does not stay fixed in the script below, how do I fix this?
x = 0:0.01:1;
figure
p1 = subplot(2,1,1);
xlabel(p1, '$x$','Interpreter','LaTex', 'Fontsize', 16);
ylabel(p1, '$\sin(x+t)$','Interpreter','LaTex', 'Fontsize', 16);
title(p1, 'Sine','Interpreter','LaTex', 'Fontsize', 20);
ylim(p1, [-1 2])
xlim(p1, [0 1])
p2 = subplot(2,1,2);
xlabel(p2, '$x$','Interpreter','LaTex', 'Fontsize', 16);
ylabel(p2, '$\cos(x+t)$','Interpreter','LaTex', 'Fontsize', 16);
title(p2, 'Cosine','Interpreter','LaTex', 'Fontsize', 20);
ylim(p2, [-2 1])
xlim(p2, [0 1])
for t = 0:0.1:2
plot(p1, x, sin(x+t))
plot(p2, x, cos(x+t))
pause(0.1)
end
By default, since you haven't got hold on or hold all, when you replot in the loop everything is reset. Since you only want to change one set of values in each graph, you can use set rather than plot here to get around the issue.
First, after each subplot is called, plot the initial graph and take a handle for it:
p1 = subplot(2,1,1);
h1 = plot(p1,x,sin(x);
Then continue on setting up the labels, etc, as you already do.
In the loop, to replace the data in your existing graph:
for t = 0.1:0.1:2
set(h1,'Ydata',sin(x+t))
set(h2,'Ydata',cos(x+t))
pause(0.1)
end