Products & Services Solutions Academia Support User Community Company

Learn more about MATLAB   

GUI for Animating a 3-D View

About the 3-D Animation Example

This example GUI has 3-D axes in which the Earth spins on its axis. It accepts no inputs, but it reads a matrix of topographic elevations for the whole Earth. To the user, it looks like this.

Animated globe GUI

The GUI provides controls to:

GUI-building techniques illustrated in this example include:

In addition, two of the callbacks illustrate how to construct and manipulate the appearance of 3-D views.

View and Run the 3-D Globe GUI

If you are reading this document in the MATLAB Help browser, you can access the example FIG-file and M-file by using either of two sets of links listed below. If you are reading this on the Web or in PDF form, go to the corresponding section in the MATLAB Help Browser to use the links.

If you intend to modify the layout or code of this GUI example, first save copies of its M-files and FIG-files to your current folder. (You need write access to your current folder to do this.) Follow these steps to copy the example files to your current folder, and then open them:

  1. Click here to copy the files to your current folder.

  2. Type guide globegui or click here to open the FIG-file in GUIDE.

  3. Type edit globegui or click here to open the M-file in the Editor.

You can view the properties of any component by double-clicking it in the Layout Editor to open the Property Inspector for it. You can modify either the figure, the M-file, or both. Then you can save the GUI in your current folder using File > Save as from GUIDE. This saves both files, allowing you to rename them, if you choose.

To just inspect the GUI in GUIDE and run it, follow these steps instead:

  1. Click here to add the example files to the MATLAB path (only for the current session).

  2. Click here to run the globegui GUI.

  3. Click here to display the GUI in the GUIDE Layout Editor (read only).

  4. Click here to display the GUI M-file in the MATLAB Editor (read only).

Designing the GUI

The GUI contains:

The Property Inspector was used to customize the uicontrols and text by:

In the GUIDE Layout Editor, the GUI looks like this.

GUIDE layout for animated globe GUI

The GUI includes three uipanels that you can barely see in this figure because they are entirely black. Using uipanels helps the graphic functions work more efficiently.

The axes CreateFcn (axes1_CreateFcn) initializes the graphic objects. It executes just once no matter how many times the GUI is opened.

The Spin button's callback (spinstopbutton_Callback), which contains a while loop for rotating the spherical surface, conducts the animation.

The two sliders allow the user to change light direction during animation and function independently, but they query one another's value because both parameters are needed to specify a view.

The Show grid check box toggles the Visible property of the graticule surface object. The axes1_CreateFcn initializes the graticule and then hides it until the user selects this option.

The Spin button's callback reads the Make movie check box value to accumulate movie frames and saves the movie to a MAT-file when rotation is stopped or after one full revolution, whichever comes first. (The user must select Make movie before spinning the globe.)

The following sections describe the interactive techniques used in the GUI.

Summary of globegui Functions

GUIDE generates the following create functions and callbacks in the globegui GUI. Most of these functions are customized for this application. If you are reading this page in the MATLAB Help Browser, click any function name to scroll to it in the Editor.

FunctionFunction Behavior
globegui Initializes the GUI's framework (not customized)
globegui_OpeningFcnInitializes the GUI's handles structure (not customized)
globegui_OutputFcnSpecifies what is written to the Command Window (not customized)
spinstopbutton_CallbackExecutes and halts animation, creates movie file
quitbutton_CallbackExits the GUI by closing the figure
spinstopbutton_CreateFcnAssigns button label strings and saves to handles structure
axes1_CreateFcnInitializes axes and colormap, creates surface and light objects, displays globe
sunazslider_CallbackChanges azimuth of light source as user moves slider
sunazslider_CreateFcnInitializes light source azimuth slider (not customized)
sunelslider_CallbackChanges elevation of light source as user moves slider
sunelslider_CreateFcn Initializes light source elevation slider (not customized)
showgridbutton_CallbackToggles the graticule grid visibility
movie_checkbox_CallbackToggles capturing and saving a movie of the animation
movie_checkbox_CreateFcn Initializes the movie button and saves its value

Alternating the Label of a push Button

The top right button, initially labeled Spin, changes to Stop when clicked, and back to Spinclicked a second time. It does this by comparing its String property to a pair of strings stored in the handles structure as a cell array. Insert this data into the handles structure in spinstopbutton_CreateFcn, as follows:

function spinstopbutton_CreateFcn(hObject, eventdata, handles)
handles.Strings = {'Spin';'Stop'};
guidata(hObject, handles); 

The call to guidata saves the updated handles structure for the figure containing hObject, which is the spinstopbutton push button object. GUIDE named this object pushbutton1. It was renamed by changing its Tag property in the Property Inspector. As a result, GUIDE changed all references to the component in the GUI's M-file when the GUI was saved. For more information on setting tags, see Identifying the Axes in the previous example.

The handles.Strings data is used in the spinstopbutton_Callback function, which includes the following code for changing the label of the button:

str = get(hObject,'String');
state = find(strcmp(str,handles.Strings)); 
set(hObject,'String',handles.Strings{3-state}); 

The find function returns the index of the string that matches the button's current label. The call to set switches the label to the alternative string. If state is 1, 3-state sets it to 2. If state is 2, it sets it to 1.

Interrupting the Spin Callback

If the user clicks the Spin/Stop button when its label is Stop, its callback is looping through code that updates the display by advancing the rotation of the surface objects. The spinstopbutton_Callback contains code that listens to such events, but it does not use the events structure to accomplish this. Instead, it uses this piece of code to exit the display loop:

if find(strcmp(get(hObject,'String'),handles.Strings)) == 1
    handles.azimuth = az;
    guidata(hObject,handles);
    break
end

Entering this block of code while spinning the view exits the while loop to stop the animation. First, however, it saves the current azimuth of rotation for initializing the next spin. (The handles structure can store any variable, not just handles.) If the user clicks the (now) Spin button, the animation resumes at the place where it halted, using the cached azimuth value.

When the user clicks Quit, the GUI destroys the figure, exiting immediately. To avoid errors due to quitting while the animation is running, the while loop must know whether the axes object still exists:

while ishandle(handles.axes1)
    % plotting code
    ...
end

You can write the spinstopbutton_Callback function without a while loop, which avoids you having to test that the figure still exists. You can, for example, create a timer object that handles updating the graphics. This example does not explore the technique, but you can find information about programming timers in Using a MATLAB Timer Object in the MATLAB Programming Fundamentals documentation.

Making a Movie of the Animation

Selecting the Make movie check box before clicking Spin causes the application to record each frame displayed in the while loop of the spinstopbutton_Callback routine. When the user selects this check box, the animation runs more slowly because the following block of code executes:

filming = handles.movie;
    ...
if ishandle(handles.axes1) && filming > 0 && filming < 361
    globeframes(filming) = getframe; % Capture axes in movie
    filming = filming + 1;
end

Because it is the value of a check box, handles.movie is either 0 or 1. When it is 1, a copy (filming) of it keeps a count of the number of frames saved in the globeframes matrix (which contains the axes CData and colormap for each frame). Users cannot toggle saving the movie on or off while the globe is spinning, because the while loop code does not monitor the state of the Make movie check box.

The ishandle test prevents the getframe from generating an error if the axes is destroyed before the while loop finishes.

When the while loop is terminated by the user, the callback prints the results of capturing movie frames to the Command Window and writes the movie to a MAT-file:

if (filming)
    filename = sprintf('globe%i.mat',filming-1);
    disp(['Writing movie to file ' filename]);
    save (filename, 'globeframes')
end   

The file name of the movie ends with the number of frames it contains. Supposing the movie's file name is globe360.mat, you play it with:

load globe360
axis equal off
movie(globeframes)

The playback looks like this.

Animated globe played back from a movie MAT-file

To see the spinstopbutton_Callback code in globegui.m in the MATLAB Editor, click here.

Graphics Techniques

To learn more about how this GUI uses Handle Graphics to create and view 3-D objects, read the following sections:

Creating the Graphic Objects

The axes1_CreateFcn function initializes the axes, the two objects displayed in it, and two hgtransform objects that affect the rotation of the globe:

Data for these two objects are rectangular x-y-z grids generated by the sphere function. The globe's grid is 50-by-50 and the graticule grid is 8-by-15. (Every other row of the 15-by-15 grid returned by sphere is removed to equalize its North-South and East-West spans when viewed on the globe.)

The axes x-, y-, and z-limits are set to [-1.02 1.02]. Because the graphic objects are unit spheres, this leaves a little space around them while constraining all three axes to remain the same relative and absolute size. The graticule grid is also enlarged by 2%, which is barely enough to prevent the opaque texture-mapped surface of the globe from obscuring the graticule. If you watch carefully, you can sometimes see missing pieces of graticule edges as the globe spins.

Texturing and Coloring the Globe

Code in the axes1_CreateFcn sets the CData for the globe to the 180-by-360 (one degree) topo terrain grid by setting its FaceColor property to 'texturemap'. You can use any image or grid to texture a surface. Specify surface properties as a struct containing one element per property that you must set, as follows:

props.FaceColor= 'texture';
props.EdgeColor = 'none';
props.FaceLighting = 'gouraud';
props.Cdata = topo;
props.Parent = hgrotate;
hsurf = surface(x,y,z,props);
colormap(cmap)

The surface function plots the surface into the axes. Setting the Parent of the surface to hgrotate puts the surface object under the control of the hgtransform that spins the globe (see the illustration in Orienting the Globe and Graticule). By setting EdgeColor to 'none', the globe displays face colors only, with no grid lines (which, by default, display in black). The colormap function sets the colormap for the surface to the 64-by-3 colormap cmap defined in the code, which is appropriate for terrain display. While you can use more colors, 64 is sufficient, given the relative coarseness of the texture map (1-by-1 degree resolution).

Plotting the Graticule

Unlike the globe grid, the graticule grid displays with no face colors and gray edge color. (You turn the graticule grid on and off by clicking the Show grid button.) Like the terrain map, it is a surfaceplot object; however, the mesh function creates it, rather than the surface function, as follows:

hmesh = mesh(gx,gy,gz,'parent',hgrotate,...
        'FaceColor','none','EdgeColor',[.5 .5 .5]);
set(hmesh,'Visible','off')

The state of the Show grid button is initially off, causing the graticule not to display. Show grid toggles the mesh object's Visible property.

As mentioned earlier, enlarging the graticule by 2 percent before plotting prevents the terrain surface from obscuring it.

Orienting the Globe and Graticule

The globe and graticule rotate as if they were one object, under the control of a pair of hgtransform objects. Within the figure, the HG objects are set up in this hierarchy.

HG hierarchy for the example

The tilt transform applies a rotation about the x-axis of 0.5091 radians (equal to 23.44 degrees, the inclination of the Earth's axis of rotation). The rotate transform initially has a default identity matrix. The spinstopbutton_Callback subsequently updates the matrix to rotate about the z-axis by 0.01745329252 radians (1 degree) per iteration, using the following code:

az = az + 0.01745329252;
set(hgrotate,'Matrix',makehgtform('zrotate',az));
drawnow                  % Refresh the screen

Lighting the Globe and Shifting the Light Source

A light object illuminates the globe, initially from the left. Two sliders control the light's position, which you can manipulate whether the globe is standing still or rotating. The light is a child of the axes, so is not affected by either of the hgtransforms. The call to light uses no parameters other than its altitude and an azimuth:

hlight = camlight(0,0);

After creating the light, the axes1_CreateFcn adds some handles and data that other callbacks need to the handles structure:

handles.light = hlight;
handles.tform = hgrotate;
handles.hmesh = hmesh;
handles.azimuth = 0.;
handles.cmap = cmap;
guidata(gcf,handles);

The call to guidata caches the data added to handles.

Moving either of the sliders sets both the elevation and the azimuth of the light source, although each slider changes only one. The code in the callback for varying the elevation of the light is

function sunelslider_Callback(hObject, eventdata, handles)

hlight = handles.light;                   % Get handle to light object
sunaz = get(handles.sunazslider,'value'); % Get current light azimuth
sunel = get(hObject,'value');             % Varies from -72.8 -> 72.8 deg
lightangle(hlight,sunaz,sunel)            % Set the new light angle

The callback for the light azimuth slider works similarly, querying the elevation slider's setting to keep that value from being changed in the call to lightangle.

Further Graphic Explorations

You can enhance the presentation of globegui in various ways, including:

  


Recommended Products

Includes the most popular MATLAB recorded presentations with Q&A sessions led by MATLAB experts.

 © 1984-2009- The MathWorks, Inc.    -   Site Help   -   Patents   -   Trademarks   -   Privacy Policy   -   Preventing Piracy   -   RSS