I am struggling with the concepts behind plotting a surface polar plot.
I am trying to plot the values measured by a sensor at a combination of different angles over a hemisphere.
I have an array containing the following information:
A(:,1) = azimuth values from 0 to 360º
A(:,2) = zenith values from 0 to 90º
A(:,3) = values measured at the combination of angles of A(:,1) and A(:,2)
For example, here is a snippet:
0 15 0.489502132167206
0 30 0.452957556748497
0 45 0.468147850273115
0 60 0.471115818950192
0 65 0.352532182508945
30 15 0.424997863795610
30 30 0.477814980942155
30 45 0.383999653859467
30 60 0.509625464595446
30 75 0.440940431784788
60 15 0.445028058361392
60 30 0.522388502880219
60 45 0.428092266657885
60 60 0.429315072676194
60 75 0.358172892912138
90 15 0.493704001125912
90 30 0.508762762699997
90 45 0.450598496609200
90 58 0.468523071441297
120 15 0.501619699042408
120 30 0.561755273071577
120 45 0.489660355057938
120 60 0.475478615354648
120 75 0.482572226928475
150 15 0.423716506205776
150 30 0.426735372570756
150 45 0.448548968227972
150 60 0.478055144126694
150 75 0.437389584937356
To clarify, here is a piece of code that shows the measurement points on a polar plot.
th = A(:,1)*pi/180
polar(th,A(:,2))
view([180 90])
This gives me the following plot:
I would like now to plot the same thing, but instead of the points, use the values of these points stored in A(:,3). Then, I would like to interpolate the data to get a colored surface.
After some research, I found that I need to interpolate my values over a grid, then translate to Cartesian coordinates. From there I do not know how to proceed. Could someone point me in the right direction?
I have trouble getting the concept of the interpolation, but this is what I have attempted:
x1 = linspace(0,2*pi,100)
x2 = linspace(0,90,100)
[XX,YY] = meshgrid(x1,x2)
[x,y] = pol2cart(th,A(:,2))
gr=griddata(x,y,A(:,3),XX,YY,'linear')
With this piece of code, your example data points are converted into cartesian coords, and then plotted as "lines". The two tips of a line are one data point and the origin.
az = bsxfun(#times, A(:,1), pi/180);
el = bsxfun(#times, A(:,2), pi/180);
r = A(:,3);
[x,y,z] = sph2cart(az,el,r);
cx = 0; % center of the sphere
cy = 0;
cz = 0;
X = [repmat(cx,1,length(x));x'];
Y = [repmat(cy,1,length(y));y'];
Z = [repmat(cz,1,length(z));z'];
Still thinking how to interpolate the data so you can draw a sphere. See my comments to your question.
I want to plot a heart shape wireframe as shown in the following image
(source):
I have tried to make it by using this MATLAB program:
n=100;
x=linspace(-3,3,n);
y=linspace(-3,3,n);
z=linspace(-3,3,n);
[X,Y,Z]=ndgrid(x,y,z);
F=((-(X.^2) .* (Z.^3) -(9/80).*(Y.^2).*(Z.^3)) + ((X.^2) + (9/4).* (Y.^2) + (Z.^2)-1).^3);
isosurface(F,0)
lighting phong
caxis
axis equal
colormap('flag');
view([55 34]);
But I didn't get the desired shape of framework as shown in the figure.
I have identified the problem: to create a wireframe we usually use the command mesh(). But this plotting facility only allow us to plot a function of two variables such as z=f(x,y). But my program makes use of three variables: F(x,y,z).
How can I solve the problem?
Here's my best attempt at reproducing the entire figure:
Generating the contoured heart mesh:
I used the contourc function to generate a series of contours in the x-y, x-z, and y-z planes. Notice that in the image you want to reproduce, the mesh lines on the back-facing side of the heart are not rendered. The quickest and easiest way I could think of to reproduce that aspect of the plot was to use isosurface to render a white surface just beneath the inside surface of the mesh, blocking the view of the back side.
Here's the code for the function heart:
function heart
% Initialize the volume data, figure, and axes:
[X,Y,Z] = meshgrid(linspace(-3,3,101));
F = -X.^2.*Z.^3-(9/80).*Y.^2.*Z.^3+(X.^2+(9/4).*Y.^2+Z.^2-1).^3;
hFigure = figure('Position',[200 200 400 400],'Color','w');
hAxes = axes('Parent',hFigure,'Units','pixels',...
'Position',[1 1 400 400],'NextPlot','add',...
'DataAspectRatio',[1 1 1],'Visible','off',...
'CameraViewAngle',10,...
'XLim',[32 70],'YLim',[39 63],'ZLim',[34 73]);
view([-39 30]);
% Create and plot contours in the y-z plane:
for iX = [35 38 41 45 48 51 54 57 61 64 67]
plane = reshape(F(:,iX,:),101,101);
cData = contourc(plane,[0 0]);
xData = iX.*ones(1,cData(2,1));
plot3(hAxes,xData,cData(2,2:end),cData(1,2:end),'k');
end
% Create and plot contours in the x-z plane:
for iY = [41 44 47 51 55 58 61]
plane = reshape(F(iY,:,:),101,101);
cData = contourc(plane,[0 0]);
yData = iY.*ones(1,cData(2,1));
plot3(hAxes,cData(2,2:end),yData,cData(1,2:end),'k');
end
% Create and plot contours in the x-y plane:
for iZ = [36 38 40 42 44 46 48 50 52 54 56 58 60 62 64 66 69 71]
plane = F(:,:,iZ);
cData = contourc(plane,[0 0]);
startIndex = 1;
if size(cData,2) > (cData(2,1)+1)
startIndex = cData(2,1)+2;
zData = iZ.*ones(1,cData(2,1));
plot3(hAxes,cData(1,2:(startIndex-1)),...
cData(2,2:(startIndex-1)),zData,'k');
end
zData = iZ.*ones(1,cData(2,startIndex));
plot3(hAxes,cData(1,(startIndex+1):end),...
cData(2,(startIndex+1):end),zData,'k');
end
% Fill the inside of the mesh with an isosurface to
% block rendering of the back side of the heart:
p = patch(isosurface(F,-0.001));
set(p,'FaceColor','w','EdgeColor','none');
end
Putting the figure together:
To reproduce the entire figure I first generated the heart mesh using the heart function above, then added the other elements around it. I also used a few submissions from The MathWorks File Exchange:
arrow.m from Erik Johnson (to generate the arrows)
myaa.m from Anders Brun (to create a nice anti-aliased final image)
Here's the code for the function I_Heart_Math (which generates the above figure):
function I_Heart_Math
% Initialize heart plot and adjust figure and axes settings:
heart;
set(gcf,'Position',[200 200 700 300],'Name','Original image');
offset = get(gca,'CameraPosition')-get(gca,'CameraTarget');
offset = 35.*offset./norm(offset);
set(gca,'Position',[65 -9 300 300],'CameraViewAngle',6,...
'XLim',[21+offset(1) 70],'YLim',[16+offset(2) 63],...
'ZLim',[32 81+offset(3)]);
% Create the axes and labels, offsetting them in front of the
% heart to give the appearance they are passing through it:
arrowStarts = [81 51 51; 51 86 51; 51 51 32]+repmat(offset,3,1);
arrowEnds = [21 51 51; 51 16 51; 51 51 81]+repmat(offset,3,1);
arrow(arrowStarts,arrowEnds,5,40,40);
text('Position',[22 52 48]+offset,'String','x','FontSize',12);
text('Position',[50 17 49]+offset,'String','y','FontSize',12);
text('Position',[46.5 51 81.5]+offset,'String','z','FontSize',12);
% Create the equation text:
text('Position',[51 47 28],'FontName','Bookman','FontSize',8,...
'HorizontalAlignment','center',...
'String',{'(x^2+^9/_4y^2+z^2-1)^3-x^2z^3-^9/_{80}y^2z^3=0'; ...
'-3 \leq x,y,z \leq 3'});
% Create the large-type text:
hI = text('Position',[4 52 69.5],'String','I',...
'FontAngle','italic','FontName','Trebuchet MS',...
'FontSize',116,'FontWeight','bold');
hM = text('Position',[80.5 50 42.5],'String','Math',...
'FontAngle','italic','FontName','Trebuchet MS',...
'FontSize',116,'FontWeight','bold');
% Create an anti-aliased version of the figure too (the larger
% fonts need some adjustment to do this... not sure why):
set(hI,'Position',[4 52 68],'FontSize',86);
set(hM,'Position',[80.5 50 41],'FontSize',86);
myaa;
set(hI,'Position',[4 52 69.5],'FontSize',116);
set(hM,'Position',[80.5 50 42.5],'FontSize',116);
set(gcf,'Name','Anti-aliased image');
end
A very elegant solution is given by #gnovice. I though I extend it by adding the other elements to replicate the figure pointed by the OP. I also added some cool animations!
% volume data
[X,Y,Z] = meshgrid(linspace(-3,3,101));
F = -X.^2.*Z.^3 - (9/80).*Y.^2.*Z.^3 + (X.^2 + (9/4).*Y.^2 + Z.^2 - 1).^3;
% initialize figure
hFig = figure('Menubar','none', 'Color','w');
pos = get(hFig, 'Position');
set(hFig, 'Position', [pos(1)-0.15*pos(3) pos(2) 1.3*pos(3) pos(4)]);
% initialize axes
hAxes = axes('Parent',hFig, 'DataAspectRatio',[1 1 1], ...
'XLim',[30 120], 'YLim',[35 65], 'ZLim',[30 75]);
view(-39,30);
axis off
% Fill the inside of the mesh with an isosurface to
% block rendering of the back side of the heart
patch(isosurface(F,-1e-3), 'FaceColor','w', 'EdgeColor','none')
hidden on % hidden surface removal
% contours in the y-z plane
for iX = [35 38 41 45 48 51 54 57 61 64 67]
plane = reshape(F(:,iX,:), [101 101]);
cData = contourc(plane, [0 0]);
xData = iX.*ones(1,cData(2,1));
line(xData, cData(2,2:end), cData(1,2:end), ...
'Color','r', 'Parent',hAxes)
pause(.1)
end
% contours in the x-z plane
for iY = [41 44 47 51 55 58 61]
plane = reshape(F(iY,:,:), [101 101]);
cData = contourc(plane, [0 0]);
yData = iY.*ones(1,cData(2,1));
line(cData(2,2:end), yData, cData(1,2:end), ...
'Color','r', 'Parent',hAxes)
pause(.1)
end
% contours in the x-y plane
for iZ = [36 38 40 42 44 46 48 50 52 54 56 58 60 62 64 66 69 71]
plane = F(:,:,iZ);
cData = contourc(plane, [0 0]);
startIndex = 1;
if size(cData,2) > (cData(2,1)+1)
startIndex = cData(2,1)+2;
zData = iZ.*ones(1,cData(2,1));
line(cData(1,2:(startIndex-1)), cData(2,2:(startIndex-1)), zData, ...
'Color','r', 'Parent',hAxes)
end
zData = iZ.*ones(1,cData(2,startIndex));
line(cData(1,(startIndex+1):end), cData(2,(startIndex+1):end), zData, ...
'Color','r', 'Parent',hAxes)
pause(.1)
end
% text
props = {'FontWeight','bold', 'FontAngle','italic', 'FontSize',100};
pause(.2)
text(7,50,70, 'I', props{:})
pause(.5)
text(80,50,43, 'Math', props{:})
pause(.2)
% xyz axes
line([20 80], [50 50], [52.5 52.5], 'Color','k')
line([50 50], [20 80], [52.5 52.5], 'Color','k')
line([50 50], [50 50], [30 80], 'Color','k')
text(20,50,50, 'x')
text(48,20,50, 'y')
text(45,50,80, 'z')
drawnow
% equation
props = {'FontSize',10, 'Interpreter','latex'};
text(20,65,30, '$(x^2+9/4y^2+z^2-1)^3 - x^2z^3-9/80y^2z^3=0$', props{:});
text(30,45,30, '$-3 \leq x,y,z \leq 3$', props{:});
drawnow
(The above GIF file was created using GETFRAME and IMWRITE).
This code plots the shaded surface:
% volume data
step = 0.05;
[X,Y,Z] = meshgrid(-3:step:3, -3:step:3, -3:step:3);
F = (-(X.^2).*(Z.^3)-(9/80).*(Y.^2).*(Z.^3))+((X.^2)+(9/4).*(Y.^2)+(Z.^2)-1).^3;
% shaded surface
isosurface(X,Y,Z,F,0)
lighting phong
axis equal
view(-39,30)
set(gcf, 'Color','w')
colormap flag
We could instead plot the wireframe only:
% volume data
step = 0.05;
[X,Y,Z] = meshgrid(-3:step:3, -3:step:3, -3:step:3);
F = (-(X.^2).*(Z.^3)-(9/80).*(Y.^2).*(Z.^3))+((X.^2)+(9/4).*(Y.^2)+(Z.^2)-1).^3;
% wireframe
patch(isosurface(X,Y,Z,F,0), 'FaceColor','w', 'EdgeColor','b')
daspect([1 1 1])
view(3)
axis tight equal
set(gcf, 'Color','w')