Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MATLAB: Drawing atop a surface plot

I'm plotting an R^2 to R function in MATLAB as a surface plot, which I colormap and view from above.

surf(X, Y, data);
colormap(jet);
colobar;
view(2);

It produces (with some additional code) something like

enter image description here

though the true nature of the function (for the purpose of understanding this question) is better observed from an angle like:

enter image description here

I want to plot a circle atop my original plot (seen from above). Something like...

enter image description here

I can't seem to achieve this however, since plotting in-a-plane elements on plots makes them appear on the x-y axis, which is covered by my surface plot. For example, calling

circle_pos = [ +1 +1; -1 -1; -1 +1; +1 -1;]
circle_rad = 0.2 * ones(4,1);
viscircles(circle_pos, circle_rad);

after my surface plot results in no visible circles when viewed from the top. Zooming and rotating reveals these circles were plotted on the x-y plane, and so are invisible from above.

enter image description here

How do I plot my circles on top of the surface plot, so that they are visible from above? A similar issue arises when plotting text atop the surface, but is remedied by specifying a z position value just above the underlying functions z value. There doesn't seem to be any way to specify the z position of these graphical elements.

like image 663
Anti Earth Avatar asked Oct 15 '16 08:10

Anti Earth


1 Answers

There may not be a direct way to specify the z position of the objects returned by viscircles, but in general there is (most of the time) a way to modify properties and position of any graphic object afterwards.


Method 1: modifying circles after creation.

If you plan to do modifications of a graphic object, the first thing to do is always to retrieve its handle. So in your case, you would have to call viscircles by specifying a return value (which will contain the handle you want).:

hg = viscircles(circle_pos, circle_rad);

I do not have the Image Processing Toolbox so I do not have access to the viscircles function. However I read from the documentation that the handle returned is an hggroup. An hggroup is simply a container containing one or more handles of more primitive graphic objects. In this case the hggroup contains the handles of 4 lines (your 4 circles).

The easiest way to transform all the objects in an hggroup is to use a hgtransform object. We will define a Translation transformation and the hgtransform will apply it to the 4 circles (all the children of the hggroup).

To define the translation, we will use a makehgtform object.

Here we go:

ht = hgtransform ;      % create the transform object
set(hg,'Parent',ht) ;   % make it a "parent" of the hggroup

zc = max(max(Z)) ;  % Find by how much we want to translate the circles on the Z axis
Tz = makehgtform('translate',[0 0 zc]) ;   % create the TRANSLATION transform

set(ht,'Matrix',Tz)     % apply the transformation (translation) to the hggroup/hgtransform

Done, your 4 circles should now be on top of your surface. Note that you can specify any other values for zc (not only the max of the surface).


Method 2: DIY

In case you do not want to be reliant on the image processing toolbox, or if you do not have it at all, it is relatively easy to create circles in a 3D space by yourself.

Here is a function which will create circles in a way comparable to viscircles but it also let you specify an optional z coordinate for the circle centre positions.

code for circles_3D.m:

function hg = circles_3d( pos , rad , varargin )

% get current axes handle and hold state
ax = gca ;
holdState = get(ax,'NextPlot') ;    % save state to reinstate after function
set(ax,'NextPlot','add') ;          % equivalent of "hold off"

tt = linspace(0,2*pi) ;
hg = hggroup(ax) ;
for k = 1:numel(rad)

    c = pos(k,:) ;
    r = rad(k) ;
    x = c(1) + r.*cos(tt) ;
    y = c(2) + r.*sin(tt) ;
    z = zeros(size(x)) ;
    if numel(c)==3 ; z = z + c(3) ; end

    plot3(hg,x,y,z,varargin{:}) ;
end

set(ax,'NextPlot',holdState) ; % restore axes hold state

You can now call this function instead of viscircles. I used the varargin parameter to transfer any line property to the circles created (so you can specify the Color, LineWidth, and any other typical parameter you like.

For the sake of an example, I need to recreate a surface comparable to your, with 4x "zero" poles distributed around the maxima:

pc = 0.5 ;  % pole centers
pw = 0.05 ; % pole widths

% surface definition
[X,Y] = meshgrid(-5:.1:5);
R = sqrt(X.^2 + Y.^2) + eps ;
Z = sin(R)./R;
% zero surface values around the defined poles
[idxPoles] = find(abs(X)>=pc-pw & abs(X)<=pc+pw & abs(Y)>=pc-pw & abs(Y)<=pc+pw ) ;
Z(idxPoles)= 0 ;
% display
hs = surf(X,Y,Z) ; shading interp

Which produces: 3d surf

Now you can simply get your circles with the circles_3D function:

zc = max(max(Z)) ;
circle_pos = [ pc pc zc ; -pc -pc zc ; -pc +pc zc ; +pc -pc zc ] ;
circle_rad = 0.2 * ones(4,1);
h = circles_3d( circle_pos , circle_rad , 'Color','r','LineWidth',2) ;

and get: enter image description here


Note that I made this function so it also return an hggroup object containing your lines (circles). So if you want to move them later, apply the same trick than in the first part of the answer.

like image 156
Hoki Avatar answered Nov 15 '22 07:11

Hoki