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.
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)
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.
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
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.
Now delete the last plane added.
And again. Or can I? I only added two slices and I am now up to deleting the third!
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.
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
We start off with the function declaration, followed by lines 2-21 for the help including an example.
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 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.
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.
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
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.
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