Rasterized PDF output after manually setting 'FaceVertexCData' of a patch in MATLAB - 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).

Related

A question about drawing a surface plot in 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

How do I customize the picture in matlab legends?

I want to draw a contour plot of a plane and a surface with legends. Plotting two surfaces in the same figure create the same legends. I want to change the resulting ellipses in the legend. Can I draw parallel lines instead of ellipses on the legend chart?
This is a sample source code:
[X,Y] = meshgrid(-3:.1:3);
Z1 = peaks(X,Y);
Z2 = 2*X+3*Y+4;
contour(X,Y,Z1)
colormap jet
shading interp
axis([-3 3 -3 3])
hold on
contour(X,Y,Z2)
legend('surface','plane')
Honestly there's no good way to get an image in front.
There are a few work arounds that I could show you, but you'll have to be specific.
The easiest things are, getting a colorbar or a rectangular box (1 color) infront of text. These are standard options for datatypes/legend entries.
I suggest you start off with giving your surfaces/countours handles.
h1=surf(....);
h2=plot(....);
lgd=legend([h1, h2, ....],[entries']);
For text with white in front you can do
h_separator1 = plot(NaN,NaN,'.','Color',[1 1 1]);
lgd=legend([h_separator1],['text']);
Here's an example you can run that let's you do the things I just said
clear all; % just for ease of quickly plotting this
close all; %closing all figures
myc=[1 1 1; 0 0 0; 1 1 1; 0 0 0; 1 1 1]; %this is want we will use to draw the paralel lines, can be of any color, just replace the zeros, with the respective values of the colors you want, 0 0 0 is black
x = [0 0 0 0]; %making it have 0 area and thus invisible
y = [0 0 0 0];
c = [0 0.33 0.66 1]; %lets you add a colorbar
figure
colormap(myc); %update the figure to use your colormap
hold on
h3 = plot(NaN,NaN,'Color','none'); %blank entry
h4 = plot(NaN,NaN,':'); % entry with dotted line, color "pseudo random"
h1=patch(x,y,'red','EdgeColor','none'); %For a rectangular color entry in legend
h2=patch(x,y,c,'EdgeColor','none'); %lets you add the colorbar, later use to place inside the legend as paralel lines
[lgd,OBJH,OUTH,OUTM]=legend([h1,h3,h2,h4],{'HI your text here','Nothing','paralel lines','line'}); %the lgd handle let's you later modify properties of the legend
hcb=colorbar; %the colorbar can still be modified, to have no number or a different scale, color, etc.
hcm=OBJH(5)
xlim([0 1])
ylim([0 1])
lpos=lgd.Position; % get position legend
lnr=numel(OUTH); %legend entries is useful
lhstp=lpos(4)/(lnr+1); %heigth step
hax=gca;
axpos=hax.Position; %to shift position because the colorbar is placed in the figure and not within the axes in comparison to the legend
% placing at by example 3rd entry
wdentry=0.04; %at the moment trial and error depends on width on legend box which is based on amount of characters and text size.
p1=axpos(1)+lpos(1)+0.01;
p2=lpos(2)+3/2*lhstp;
p3=wdentry;
p4=lhstp-0.01;
hcb.TickLabels=[]; %remove tick labels
hcb.Ticks=[]; %remove ticks
hcb.Color=[1 1 1]; %white border around it to make it "semi-invisible"
hcb.Position=[p1 p2 p3 p4];

A blank grid with a random green place in Matlab

I created a 100x100 lattice using this code :
L=ones(101,1)*(1:101);
for i=2:101
for j=1:101
L(i,j)=10*(i-1)+j;
end
end
M=L;
x=randi([1 100]);
y=randi([1 100]);
M(x,y)=0;
I want to generate a blank 100x100 grid with the case containing "0"
in green.
Note: I tried this method but it doesn't work
map1 = [1 1 1];
colormap(map1);
pcolor(L)
map2 = [0 1 0];
colormap(map2);
pcolor(M(x,y))
Most of your code is good. you just need to change the last part
map2 = ones(max(M(:)),3);
map2(1,:) = [0 1 0];
colormap(map2);
pcolor(M);
you need to get the a colormap that represents all of the possible colros in your map. si thats what the first line does, it sets all colors to be white. the next line changes the first element (wich will correspond to 0) to be green
this is the final answer
L=ones(101,1)*(1:101);
L(1,1)=2;
for i=2:101
for j=1:101
L(i,j)=10*(i-1)+j;
end
end
M=L;
x=randi([1 100]);
y=randi([1 100]);
M(x,y)=0;
map2 = ones(max(M(:)),3);
map2(1,:) = [0 1 0];
image(M);

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

plot a structure in matlab

I have a structure of known (but variable length) like this-
1 0 1
0 1 1
I want to plot this structure as colored squares- color each 1 as a green square, and 0s as a red square
Something like
[green][red][green]
[red][green][green]
It would be nice to add some optional text on each square.
Also, I have another data structure of same length, with numbers going from 0.0 to 1.0 .. something like
0.99 0.09 1.0
0.09 0.87 1.0
I want to possibly change the intensity of red and green in the above pic depending on how close to 0 or 1 is the corresponding number.
Any suggestions are helpful. Thanks a lot.
You can set the colormap after displaying the matrix as a scaled image:
Z = [1 0 1; 0 1 1];
figure; imagesc(Z);
colormap([1 0 0; 0 1 0]);
axis off; axis image;
Essentially, you want to turn the 2-d structure into a 3-d one, the last dimension being x3, one for each of the RGB colors. Start with this code, and play with it until it does what you want.
map=zeros(2,2,3);
map(:,:,1)=[1 1; 0 0];
map(:,:,2)=[1 0; 1 0];
map(:,:,3)=[0 0; 0 0];
figure;image(map);
Alternatively, you could have a colormap, which would translate the pixel counts to intensity. It's been a while since I've done it, but I can at least point you in the right direction. Run the first command, and look at the colormap. You want to have a gradual changing from Green to Red. Format it how you want it, pass it back in with the last command, and see what you get out.
cmap = colormap;
%You'll want to change cmap to meet your needs
imagesc([.1 .2; .8 .9]);
colormap(cmap);