A question about drawing a surface plot in MATLAB - matlab

I wrote a matlab code for topology optimization. The code showing the results in a surface plot is:(a very simple example)
XYZG=[2.5 0 0;2.5 0 2.5;2.5 0 5;0 -2.5 0;0 -2.5 2.5;0 -2.5 5;-2.5 0 0;-2.5 0 2.5;-2.5 0 5];
ELNOD=[1 4 5 2;2 5 6 3;4 7 8 5;5 8 9 6];
nelement=4;
Dens=[0 1 1 0];
for element=1:nelement
for i=1:4
X(i)=XYZG(ELNOD(element,i),1);
Y(i)=XYZG(ELNOD(element,i),2);
Z(i)=XYZG(ELNOD(element,i),3);
end
XX=[X(1) X(2);X(4) X(3)];
YY=[Y(1) Y(2);Y(4) Y(3)];
ZZ=[Z(1) Z(2);Z(4) Z(3)];
tick=-Dens(element)*[1 1;1 1];
figure(1)
hold on
surf(XX,YY,ZZ,tick);
colormap gray;
end
This code is too slow. If I have for example 10,000 elements it takes a long time for drawing plot.
So I would appreciate any help on how to make this faster.

1] Minor improvement:
The code you gave as a sample can be simplified to avoid temporary assignment. Consider the following:
%% Sample data
XYZG=[2.5 0 0;2.5 0 2.5;2.5 0 5;0 -2.5 0;0 -2.5 2.5;0 -2.5 5;-2.5 0 0;-2.5 0 2.5;-2.5 0 5];
ELNOD=[1 4 5 2;2 5 6 3;4 7 8 5;5 8 9 6];
nelement=4;
Dens=[0 1 1 0];
%% pre-initialisation just to set the size (size=[2,2])
XX = zeros(2) ;
YY = XX ;
ZZ = XX ;
figure(1)
hold on
colormap gray;
for element=1:nelement
% Base vector combining (unused now)
% idx=1:4;
% X = XYZG( ELNOD(element,idx),1 ).';
% Y = XYZG( ELNOD(element,idx),2 ).';
% Z = XYZG( ELNOD(element,idx),3 ).';
% The block above is commented because we do not need these intermediate
% vectors to build the base matrices XX, YY and ZZ.
% This can be done directly using linear indexing:
idxOrder = [1 4 2 3] ;
XX(1:4) = XYZG( ELNOD(element,idxOrder) , 1 ) ;
YY(1:4) = XYZG( ELNOD(element,idxOrder) , 2 ) ;
ZZ(1:4) = XYZG( ELNOD(element,idxOrder) , 3 ) ;
tick=-Dens(element)*[1 1;1 1];
surf(XX,YY,ZZ,tick) ;
end
This should run marginally faster. Thanks to the avoidance of a few temporary arrays. We are now building each patch coordinates more directly. Also the function calls which only need to be used once have been taken out of the loop (something to generally look for if your loop is taking too long).
Now this is not going to be satisfying anyway. The real bottleneck in your structure is not so much the calculations/indexing in each loop iteration, it is the growing number of graphic handles that the system has to maintain. Every iteration of your loop create a surface object. These objects require memory to keep their coordinates, but also to maintain the large number of internal properties. Once you multiply these objects, your system will start to slow down. Some systems may not even be able to create 10,000 surface graphic objects on the same figure, and if they can it's going to be sluggishly painful (you know these situations where you click on the screen and wait ~25s to notice any reaction ...).
2] Major improvement:
One way to limit the number of graphic object would be to combine all of these cordinates in order to create a single graphic object. Of course we'd then have to color each face according to your rule.
Fortunately, I noticed that your base coordinates are actually organised perfectly to inject directly as a patch in Matlab. SO no need to demultiplex/remultiplex the data in and out, we can directly create and color a global patch:
% create a Black and White colormap
cmap = [1 1 1;
0 0 0] ;
figure
% generate a patch with all the 'faces','vertices' and 'color' data :-)
hp = patch('Faces',ELNOD, 'Vertices',XYZG, 'FaceVertexCData',Dens(:) ) ;
% last refinement to have the same appearance than in former code
shading flat
colormap(cmap)
hp.EdgeColor = 'k' ; % <= this needs to be executed AFTER "shading flat"
Voila !! No need any loop or any calculations. You already had all the data needed in the first place ;-)
I'd encourage you to read the documentation for patch, especially the way to use the property FaceVertexCData

Related

MATLAB: PlotCube with large dataset. Use uint8 uint16 datatype? Preallocate?

I have a large dataset (several hundred thousand 3D points (x,y,z).
I wish to display these points as cubes in Matlab (v. R2016a) (using the (x,y,z) points as the cube centroids).
(The data points also have other attributes: A,B; which I wish to use to both colour the cubes and also dictate the 'alpha' or transparency of the cubes).
I have (mildly) adapted Olivers' PlotCube code from:
http://au.mathworks.com/matlabcentral/fileexchange/15161-plotcube
to implement a variable colour and transparency for each cube already, however trying to plot even a subset of the data (say 20,000 points) takes forever on this computer (2.5GHz i7, 16GB RAM +4GB GPU, 64-bit Windows 10).
Also, once it is finally plotted (in a new Matlab figure window), I cannot rotate the object in 3D; its too (RAM? GPU?) intensive.
I have tried increasing the Java Heap Memory (from 128MB to 4GB) in case that was a bottleneck (to no avail):
Matlab > Home > Preferences > Matlab > General > Java Heap Memory
If all of the input variables only have a range from 0-255 (say) is it possible to change all data types from the standard 'double' (floating point integers) into uint8 format? (and thus impove computing/ graphics-display speed I guess).
Is there a way to change the PlotCube code to allow this? (uint8 or uint16 variables)
Note that the 'alpha' (transparency) values must lie within the range 0-1.
(so is there a way for matlab to only store/use a uint8 precision for each point, but in decimal format within this range? As soon as I convert it to a decimal it goes back to 'double' type e.g. by dividing each value (0-255 range) by 255 to 'normalise' it: 0-1 range.)
Note that all computations in the code can easily be performed in 'double' data type format on the whole dataset; it is only the final data points to be plotted that (may) need to be in a smaller data format (to reduce memory requirements to plot and rotate 3D objects).
If there is a smarter way to plot/display large datasets of points in matlab (other-than/as-well-as changing the data type) I would much appreciate any advice!
I have read that 'pre-allocating' arrays in Matlab loops grants huge time savings*, however in the main 'for' loop (below) which I am using to plot all the points I am not sure how to implement pre-initialising.
% Run for loop for every point; an origin for each unique point.
% Note that edges, alpha and colour are all constant here for every point.
for i = 1:length(origin)
plotcube(edges,origin(i,:),alpha,[0 0 1])
end
Thanks for any help/advice!
The code for PlotCube (taken from the first url above) is:
function plotcube(varargin)
% PLOTCUBE - Display a 3D-cube in the current axes
%
% PLOTCUBE(EDGES,ORIGIN,ALPHA,COLOR) displays a 3D-cube in the current axes
% with the following properties:
% * EDGES : 3-elements vector that defines the length of cube edges
% * ORIGIN: 3-elements vector that defines the start point of the cube
% * ALPHA : scalar that defines the transparency of the cube faces (from 0
% to 1)
% * COLOR : 3-elements vector that defines the faces color of the cube
%
% Example:
% >> plotcube([5 5 5],[ 2 2 2],.8,[1 0 0]);
% >> plotcube([5 5 5],[10 10 10],.8,[0 1 0]);
% >> plotcube([5 5 5],[20 20 20],.8,[0 0 1]);
% Default input arguments
inArgs = { ...
[10 56 100] , ... % Default edge sizes (x,y and z)
[10 10 10] , ... % Default coordinates of the origin point of the cube
.7 , ... % Default alpha value for the cube's faces
[1 0 0] ... % Default Color for the cube
};
% Replace default input arguments by input values
inArgs(1:nargin) = varargin;
% Create all variables
[edges,origin,alpha,clr] = deal(inArgs{:});
XYZ = { ...
[0 0 0 0] [0 0 1 1] [0 1 1 0] ; ...
[1 1 1 1] [0 0 1 1] [0 1 1 0] ; ...
[0 1 1 0] [0 0 0 0] [0 0 1 1] ; ...
[0 1 1 0] [1 1 1 1] [0 0 1 1] ; ...
[0 1 1 0] [0 0 1 1] [0 0 0 0] ; ...
[0 1 1 0] [0 0 1 1] [1 1 1 1] ...
};
XYZ = mat2cell(...
cellfun( #(x,y,z) x*y+z , ...
XYZ , ...
repmat(mat2cell(edges,1,[1 1 1]),6,1) , ...
repmat(mat2cell(origin,1,[1 1 1]),6,1) , ...
'UniformOutput',false), ...
6,[1 1 1]);
cellfun(#patch,XYZ{1},XYZ{2},XYZ{3},...
repmat({clr},6,1),...
repmat({'FaceAlpha'},6,1),...
repmat({alpha},6,1)...
);
view(3);
The final outcome is to get something like Ben has achieved in the diagram here:
Bens PlotCube image from 3D voxel Display in matlab

Sliding window for matlab

The intention is to estimate in a [3 3] sliding window. 0.5*[(A(i)-A(5))^2] is computed where a(i) is the pixels around center pixel a(5).
The mean of each of these 8 half square differences is stored in the center pixel's location.
To tackle this conv2 and nlfilter were used on a training example matrix as follows.
clc;
close all;
clear all;
A = [1 2 3 4 5 6;5 4 6 3 2 1;2 3 2 1 4 5];
kernel = [-1 -1 -1; -1 8 -1; -1 -1 -1];
outputImage = conv2(A, kernel);
fun = #(x) mean(x(:));
B= nlfilter (outputImage,[3 3],fun);
The initial idea was to calculate the difference and store it in the location of the pixels instead of the center pixel. Then use a sliding window to take mean of those differences and replace the center pixel.
It was apparent that my logic was flawed.
Though i was able to calculate the difference(i first tried to see simple difference was possible for me) i had to deal with data being overwritten. moreover this method will create a matrix larger than the original which is also wrong.
the function mean and the kernel you are using are both linear and do not represent the non-linear operation you are trying to achieve.
One way of using conv and mean is by computing the 8 differences as different output channels
ker = cell(1,8);
for ii=1:8
ker{ii} = zeros(3);
ker{ii}(2,2) = 1; %// for a(5)
if ii < 5
ker{ii}(ii) = -1;
else
ker{ii}(ii+1) = -1;
end
end
interim = zeros( [size(A,1) size(A,2), numel(ker)] ); % allocate room for intermidiate results
for ii=1:numel(ker)
interim(:,:,ii) = conv2(A, ker{ii}, 'same' ); %//'same' takes care of output size for you
end
Now interim holds each of the different a(5)-a(i) we are ready for the non-linear operation
interim = interim.^2;
B = 0.5 * interim;

How do I add values to a vector when I do not have a subindex specified

I am working on a Bézier curve and this how my code looks right now
x=[3 2 6 4 1];
y=[8 1 7 5 9];
n=length(x)-1;
scatter(x,y)
hold on
for t=0:0.01:1
for i=0:1:n
xt=nchoosek(n,i)*(t^i)*(1-t)^(n-1)*x(n+1);
yt=nchoosek(n,i)*(t^i)*(1-t)^(n-1)*y(n+1);
end
end
plot(xt,yt)
hold off
I'm a beginner using Matlab so I'm experiencing some issues. I would like to know how to make xt and yt vectors of the same size as t (that means 101x1) and make the values they get while just i changes be added to the other values as long as t is the same. When t changes, they must start from 0 and add the values they get while just i changes until it gets to n.
Thank you
The variable xt and yt should be vectors and their tth value updated at each iteration of i. Try:
x=[3 2 6 4 1];
y=[8 1 7 5 9];
n=length(x)-1;
scatter(x,y)
hold on
T = 0:0.01:1
xt = zeros(size(T));
yt = zeros(size(T));
for idt = 1:length(T)
t=T(idt);
for i=0:1:n
xt(idt)=xt(idt)+nchoosek(n,i)*(t^i)*(1-t)^(n-i)*x(i);
yt(idt)=yt(idt)+nchoosek(n,i)*(t^i)*(1-t)^(n-i)*y(i);
end
end
plot(xt,yt)
hold off

Rasterized PDF output after manually setting 'FaceVertexCData' of a patch in MATLAB

I'm running into a problem trying to produce vector PDF plots after manually setting colors of patches.
After setting the colors of patch faces and vertices using a call to set(...) to set the 'FaceVertexCData' property of a patch, the PDF output produced by both 'savefig' and 'saveas' is rasterized and no longer in vector format. This does not occur when 'FaceVertexCData' is not altered.
For example,
clear all; close all;
h = bar([1 2 3 ; 3 2 1 ; 3 4 4]);
saveas(gcf, 'barplot.pdf', 'pdf');
savefig('barplot.pdf', 'pdf');
produces a perfectly fine vectorized PDF of the plots.
On the other hand, the following code will produce ugly vectorized PDF plots:
clear all; close all;
h = bar([1 2 3 ; 3 2 1 ; 3 4 4]);
ch = get(h,'children');
set(ch{1},'FaceVertexCData',[1 0 0 ; 0 1 0; 0 0 1]);
set(ch{2},'FaceVertexCData',[1 0 0 ; 0 1 0; 0 0 1]);
set(ch{3},'FaceVertexCData',[1 0 0 ; 0 1 0; 0 0 1]);
saveas(gcf, 'barplot_savefig_FaceVertexCData.pdf', 'pdf');
savefig('barplot_saveas_FaceVertexCData.pdf', 'pdf');
What is the cause of the problem? How can this be resolved? Any hints will be welcome.
Thanks much.
Edit: MATLAB Version: 8.0.0.783 (R2012b) on OS X
I have solved the problem.
For posterity:
The workaround for this is to not specify RGB colors directly, but define them in a color map, which is then indexed.
The following code will solve the problem:
clear all; close all;
% Make the bar plot
h = bar([1 2 3 ; 3 2 1 ; 3 4 4]);
ch = get(h,'children');
% Define the colors in a color map
cMap = [1 0 0 ; 0 1 0; 0 0 1];
colormap(cMap);
% Now set the FaceVertexCData by indexing into the colormap
set(ch{1},'CDataMapping', 'direct', 'FaceVertexCData',[1 2 3]');
set(ch{2},'CDataMapping', 'direct', 'FaceVertexCData',[1 2 3]');
set(ch{3},'CDataMapping', 'direct', 'FaceVertexCData',[1 2 3]');
% Save out, this will produce vectorized PDF
saveas(gcf, 'barplot_savefig_FaceVertexCData.pdf', 'pdf');
savefig('barplot_saveas_FaceVertexCData.pdf', 'pdf');
The following piece of information about this is relevant:
"RGB color data not yet supported in Painter's mode - you will see this as a warning if you try to export a figure which contains patch objects whose face or vertex colors are specified as a an RGB colour, rather than an index into the colormap, using the painters renderer (the default renderer for vector output). This problem can arise if you use pcolor, for example. This is a problem with MATLAB's painters renderer, which also affects print; there is currently no fix available in export_fig (other than to export to bitmap). The suggested workaround is to avoid colouring patches using RGB. First, try to use colours in the figure's colourmap - change the colourmap, if necessary. If you are using pcolor, try using uimagesc (on the file exchange) instead."
(https://sites.google.com/site/oliverwoodford/software/export_fig, accessed June 11, 2013).

How to plot function with different color for different interval?

I have a typical scenario in which there is a Vector X and Vector Y. Vector X contains increasing values, for example X = [1 1 1 2 2 3 4 4 4 4 4]. Vector Y contains real values of same size as X. Im looking to plot Index Vs Y with color change for each different value of X for the corresponding index.
For example, the plot should have color1 for the first 3 values of 1, color2 for the next 2 values of 2, color3 for 1 value 3 and so on.
Can any one help me
Building on Laurent's answer and implementing your "Index vs Y" requirement,
function color_plot(data_vector, color_vector)
styles={'ro','g.','bx','kd'};
hold off;
for i=unique(color_vector)
thisIdx=find(color_vector==i);
thisY=data_vector(color_vector==i);
thisStyle=styles{mod(i-1,numel(styles))+1};
plot(thisIdx,thisY,thisStyle);
hold on;
end
hold off;
My version also allows arbitrarily large color indices; if you don't have enough styles defined, it just wraps back around and reuses colors.
Update note I had to fix a sign above in the calculation oh thisStyle.
Testing it with
X = [1 1 1 2 2 3 4 4 4 4 4];
Y=rand(size(X))
color_plot(Y,X)
now gives
A plot() function option would be better (and maybe it exists).
Here's a workaround function to do this:
function colorPlot( data_vector, colors_vector)
%PLOTCOL plots data_vector with colors found in colors_vector
Styles=[{'r-'} {'g-'} {'b-'} {'k-'}];
last_off=0;
last_data=0;
for i=unique(colors_vector)
data_segment=data_vector(colors_vector==i);
len=length(data_segment);
if last_off==0
hold off;
plot( data_segment, 1:len,char(Styles(i)));
last_off=len;
else
plot([last_data data_segment],last_off:last_off+len,char(Styles(i)));
last_off=last_off+len;
end
last_data=data_segment(len);
hold on;
end
hold off;
end
Call it this way :
colorPlot(Y,X);