Loren on the Art of MATLAB

Turn ideas into MATLAB

A Way to Create Reusable Tools

There are a lot of ways to create reusable tools and widgets in lots of programming languages, including MATLAB. Today I
want to illustrate creating a reusable visualization tool that can then be incorporated into a more complex visualization
tool. Several of us (Scott, Mike, and I) at The MathWorks developed this example for the MATLAB World Tour that took place earlier this year. I even posted some pictures from some of the events.

 

Contents

Here's a GUI Using the Tool

Here I show the tool embedded in a GUI (graphical user interface). There's a slider that allows the user to choose a viewing
plane through the x-axis of the 3-D volume.

[x,y,z,v] = flow();
volvisGUI(x,y,z,v)

Code Architecture

The code is split into two files, the actual GUI, in volvisGUI.m and the viewer that shows planes slicing through the volume, in volumeVisualization.m. Let's first look at a skeleton of the code for volumeVisualization.

dbtype volumeVisualizationPseudo
1     function s = volumeVisualizationPseudo(x,y,z,v)
2     %volumeVisualizationPseudo   Pseudo-code for volumeVisualization.
3     
4     %   Copyright 2007 The MathWorks, Inc.
5     
6     %% Initialize
7     initDisplay(x,y,z,v)
8     
9     %% Nested Functions
10        %% Add slice plane
11        function addSlicePlane(xLoc)
12        end
13        %% Delete Slice plane
14        function deleteLastSlicePlane()
15        end
16        %% Initialize DIsplay
17        function initDisplay(x,y,z,v)
18        end
19    
20    % Export structure
21    s.addSlicePlane = @addSlicePlane;
22    s.deleteLastSlicePlane = @deleteLastSlicePlane;
23    s.xMin = min(x(:));
24    s.xMax = max(x(:));
25    end

Initialization

In this code, you can see that the figure is initialized, and a structure is returned. This structure contains the minimum
and maximum values possible for a plane along the x-axis as well as function handles to two functions, one to add a slice
plane, and one to delete the last slice plane. The two function handles are the functionality I may then use if I want to
build on top of this visualization.

Let me try out the engine now. First I create the visualization.

close all
s = volumeVisualization(x,y,z,v)
s = 
           addSlicePlane: @volumeVisualization/addSlicePlane
    deleteLastSlicePlane: @volumeVisualization/deleteLastSlicePlane
                    xMin: 0.1000
                    xMax: 9.9000

Next, let me add and remove some slices.

s.addSlicePlane(5)

s.addSlicePlane(3.2)

Now delete the last plane added.

s.deleteLastSlicePlane()

And again.

s.deleteLastSlicePlane()

And again. Or can I? I only added two slices and I am now up to deleting the third!

s.deleteLastSlicePlane()

The Engine Code

Whew! No seg-fault or other crash. Let's look at the actual code to see what's going on. I'll discuss it in segments.

dbtype volumeVisualization
1     function s = volumeVisualization(x,y,z,v)
2     %volumeVisualization Engine for choosing y-z planes to view.
3     %   s = volumeVisualization(X,Y,Z,V) returns a structure 
4     %   containing information about the visualization of volume
5     %   data described by X,Y,Z,V.  The fields are:
6     %          addSlicePlane -- function handle to add a slice
7     %                           plane at location x
8     %   deleteLastSlicePLane -- function handle to delete the
9     %                           last slice plane added
10    %                   xMin -- minimum x location for a plane
11    %                   xMax -- maximum x location for a plane
12    %
13    %      Example:
14    %      [x,y,z,v] = flow;
15    %      s = volumeVisualization(x,y,z,v);
16    %      s.addSlicePlane(3.7)
17    %      s.addSlicePlane(7.5)
18    %      pause
19    %      s.deleteLastSlicePlane()
20    %      pause
21    %      s.deleteLastSlicePlane()
22    
23    %   Copyright 2007 The MathWorks, Inc.
24    
25    %% Store handles to the various planes
26    %initialize handle to axis
27    %initialize handle to slice plane
28    hAxis = [];         
29    hSlicePlanes = [];  
30    
31    %% Create data for generic slice through yz-plane
32    [yd,zd] = meshgrid(linspace(min(y(:)),max(y(:)),100), ...
33        linspace(min(z(:)),max(z(:)),100));
34    
35    %% Plot the volume initially
36    initDisplay()
37    
38    %% Nested Functions
39        function addSlicePlane(xLoc)
40        %addSlicePlane   Add a slice plane xLoc.
41            xd            = xLoc*ones(size(yd));
42            newSlicePlane = slice(hAxis, x, y, z, v, xd, yd, zd);
43            hSlicePlanes   = [ hSlicePlanes, newSlicePlane ];
44            set(newSlicePlane,'FaceColor'      ,'interp',...
45                              'EdgeColor'      ,'none'  ,...
46                              'DiffuseStrength',.8       );
47        end
48    
49        function deleteLastSlicePlane()
50        %deleteLastSlicePlane Delete the last slice plane added.
51            if ~isempty(hSlicePlanes)
52                delete(hSlicePlanes(end));
53                hSlicePlanes = hSlicePlanes(1:end-1);
54            end
55        end
56    
57        function initDisplay()
58        %initDisplay  Initialize Display.
59    
60            % Draw back and bottom walls
61            if isempty(hAxis) || ~ishandle(hAxis)
62                hAxis = gca;
63                hold on;
64            end
65            hx = slice(hAxis, x, y, z, v, ...
66                max(x(:)),       [],       []) ;
67            hy = slice(hAxis, x, y, z, v, ...
68                [],       max(y(:)),       []) ;
69            hz = slice(hAxis, x, y, z, v, ...
70                [],              [],min(z(:))) ;
71    
72            % Make everything look nice
73            set([hx hy hz],'FaceColor','interp',...
74                'EdgeColor','none')
75            set(hAxis,'FontSize',18,'FontWeight','Bold');
76            xlabel('X');ylabel('Y');zlabel('Z')
77            daspect([1,1,1])
78            axis tight
79            box on
80            view(-38.5,16)
81            colormap (jet(128))
82        end
83    
84    s.addSlicePlane = @addSlicePlane;
85    s.deleteLastSlicePlane = @deleteLastSlicePlane;
86    s.xMin = min(x(:));
87    s.xMax = max(x(:));
88    
89    end

Help

We start off with the function declaration, followed by lines 2-21 for the help including an example.

Variable Initialization

We next initialize variables to hold the handle to the axes in which the data are plotted and another array for the handles
to the slice planes. They start off empty. We also make an array of values for the y and z data for a generic slice. And
then we initialize the plot, by calling the nested function initDisplay. You can see that we plot several slices along different directions and alter aspects of the plot such as colormap, aspects
of the axes, etc.

Finally, we set up a structure to return the minimum and maximum values for the slices along the x-axis and function handles
to two functions, for adding and deleting slices. These function handles refer to nested functions in the file volumeVisualization.m and I will discuss these functions in the next section.

The Main Work

The main work for this application is done after the initialization and is embodied in the two nested functions addSlicePlane and deleteLastSlicePlane.

When I call addSlicePlane, I insert a plane at a particular x-location. The function creates data for the slice plane, instantiates a slice, makes
it pretty by altering some color and shading properties, and adds the handle to the latest slice to the list of slice handles.
As simple as that.

Calling deleteLastSlicePlane again manages the list of slice planes by deleting the last element on the list from the plot, and removing it from the list
of slice planes. If that list is empty, the function essentially does nothing, which is why it had the good graces to not
error out on me earlier.

The GUI

I am now poised to use this M-file to create the initial GUI I showed. That GUI has a slider that maps to positions for planes
along the x-axis. When a user selects a value, the slider callback simply deletes the last slice plane and adds a new one
at the chosen location. Here's the code.

dbtype volvisGUI
1     function volvisGUI(x,y,z,v)
2     % volvisGUI Interactive volume visualization.
3     %
4     %   Example:
5     %   [x,y,z,v] = flow;
6     %   volvisGUI(x,y,z,v)
7     
8     %   Copyright 2007 The MathWorks, Inc.
9     
10    %% Initalize visualization
11    figure;
12    s = volumeVisualization(x,y,z,v);
13    s.addSlicePlane(s.xMin);
14    
15    %% Add uicontrol
16    hSlider = uicontrol(...
17        'Units','normalized', ...
18        'Position',[.75 .05 .2 .05], ...
19        'Style','slider', ...
20        'Min',s.xMin, ...
21        'Max',s.xMax, ...
22        'Value',s.xMin, ...
23        'Callback',@updateSliderPosition);
24    
25    %%
26        function updateSliderPosition(varargin)
27            s.deleteLastSlicePlane();
28            x = get(hSlider,'Value');
29            s.addSlicePlane(x);
30        end
31    
32    end
33    

Modular Programming Architecture

I've demonstrated here a programming architecture, using nested functions, where you can create individual tools, and incorporate
them into larger applications.

I have the visualization engine and I showed how to use it both in a stand-alone way as well as how to incorporate it into
a GUI. It now can operate as a tool ready to incorporate in other applications. For example, I could have chosen it to power
an animation. Or I could add other degrees of freedom in a GUI by supplying a way to choose other colormaps perhaps.

The Files

You can find the running files for this example on the File Exchange at MATLAB Central.

How might you use such ideas for creating tools you can reuse and share easily? Post your thoughts here.

Published with MATLAB® 7.4

|
  • print
  • send email

Comments

To leave a comment, please click here to sign in to your MathWorks Account or create a new one.