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 volumeVisualizationPseudo1 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 volvisGUI1 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 here on the File Exchange at MATLAB Cental.
How might you use such ideas for creating tools you can reuse and share easily? Post your thoughts here.
Get
the MATLAB code
Published with MATLAB® 7.4



The technique that you discuss here is something that collegues and I have been doing ever since nested functions and handles were introduced in Matlab 7.0. We have a fairly elaborate pseudo object-oriented replacement for GUIDE that allows for reusable widgets.
I find that GUIDE really doesn’t cut it. Writing GUIs with lots of states and data that must be shared between callbacks is frustrating, and much more complex than it should be. We developed this replacement out of shear frustration. I have heard rumors that GUIDE will be updated or replaced sometime in the future. Is there any specific information for when that will be?
Sean-
Glad you hear you found the technique useful! I don’t have any specific information about GUIDE but I do know updating it is on the plans. I think nested functions might play a part in that, but I’m not sure.
–Loren
Good to see that the “secret” is getting out…!
Like Sean, I’ve been using closures with nested functions and function handles to do ‘lightweight’/'pseudo’ OO (lacking inheritance) since their introduction in 7.0. I was inspired by similar patterns in Scheme.
I also use anonymous functions quite a bit. Incidentally, by using anonymous functions (lambda functions…) one can hack many of the examples in the first chapters of the classic, “Structure and Interpretation of Computer Programs” (http://mitpress.mit.edu/sicp/). This is a great way of getting a little taste of functional programming vs. the procedural, imperative paradigm that 99% of Matlabers probably are more used to.
A massive advantage to using closure objects can be that a function handle reference to an object (instance) method provides pass-by-reference, so, e.g., the same instance can be referenced in multiple collections. This leads to the second massive advantage: persistence. As long as there is a reference to the object its enclosed state exists and anyone who has a reference to it can use it. There is no need to use globals ever again (DEATH to globals!), except, perhaps in exceptional circumstances.
In order to realize a closure object as a ‘pure’ reference the structure returned by the main, enclosing function (effectively, the constructor) must be only a structure of function handles (or a single function handle pointing to a dispatcher). Loren’s example, volumeVisualization, breaks this a bit by putting some mutable state onto the structure, but that could be fixed easily.
All access to internal state should be through accessor methods. As a hint, a convenient, minimalist accessor providing both get and set is shown by this example:
function obj = foo % variables in the enclosing function are attributes name = ''; % provide accessors for the ones you want to be 'public' obj.name = @accessName; function out = accessName(in) if nargin, name = in; end out = name; end % other methods change state (provide behavior) ... endThen….
a = foo; a.name('I am a foo'); a.name() -> 'I am a foo' b = a; b.name() -> 'I am a foo' % reference to the same objectI’ve implemented several variants of OO frameworks based on closures that add inheritance. Even the somewhat bulky, first forms (they carried a lot of boilerplate…) were more useful than the (pardon the editorializing) mega-clunky, native ‘class’ framework in Matlab. My latest versions are far more concise and have been optimized for performance. This gives me ‘real’ OO within the Matlab, my favorite engineering bat’leth, the one that I use almost every day on my desk and in the lab.
Another neat-o-keen aspect of this approach is that classes can be defined within classes and/or multiple classes can be defined within the same .m file, rather than in the plethora of .m files within a @foo directory.
Loren’s example shows how nice it is to be able to compose objects. That way, you can cleanly encapsulate closely related responsibilities for a behavior into an individual objects and then compose several of those objects to solve a particular problem.
As a parting shot, for anyone getting into hacking OO for the first time, some excellent essays on fundamental ‘good principles’ by Robert Martin can be found at: http://www.objectmentor.com/omSolutions/oops_what.html. The OO paradigm is just another tool - one can write either beautiful or hideous OO code…
I echo Sean’s sentiments about GUIDE. Building a replacement has been on my to-do list for ages and I’m not surprised that he and his colleagues have done it.
Cheers and happy Matlab hacking!
Michael
Once matlab’s mcos oops system is released building proper oops programs/classes will be much easier. I will even say delightful. :)
I am begging to know: 1. if what we see in the matlab files will change much ie see memmapfile, 2. when will mcos be officially released.
The current oops system is very difficult to use if you want to have proper fully functional class. And trying to do it with just nested functions isn’t ideal either.
Please please oh please let us know when mcos will be available.
Micheal, you say: “All access to internal state should be through accessor methods.”
Could you explain what the actual reason is for this?
What is practically wrong with directly accessing simple data eg. a.name=’do less programming’
Wobbly, as I tried to convey, above, it’s to achieve encapsulation of the object state and to make the object pass by reference (function handles are the ONLY thing that is pass by reference in Matlab, everything else is pass by value [ok, ok, libpointers and javaArray are too, but that’s arcane juju for most folks…]). NB, the objects created with the Matlab OOPS are pass by value…
Ok, what does that mean…
In practical terms, it realizes what is shown in the second block of code, above. I.e., if a is an object then b = a refers to the *same* object, not a copy. If each field on the struct representing an object is a function handle, all copies of that struct have the same function handle values and they all point to the same thing.
Here’s a practical example of how direct access breaks this:
Modify the example, above, by adding a field, age, directly to the class structure:
function obj = foo
name = ”;
obj.age = [];
obj.name = @accessName;
function out = accessName(in)
if nargin, name = in; end
out = name;
end
end
Now, create an instance (object) of the foo class and set its attributes:
>> a = foo
a =
age: []
name: @foo/accessName
>> a.name(’Arthur Dent’); a.age = 42;
>> disp( a.name() ); disp( a.age )
Arthur Dent
42
Copy it:
>> b = a;
>> disp( b.name() ); disp( b.age )
Arthur Dent
42
Now modify the first one:
>> a.name(’Ford Prefect’); a.age = 612;
>> disp( a.name() ); disp( a.age )
Ford Prefect
612
But the second one is broken since the age is still the value it had when you did b = a :
>> disp( b.name() ); disp( b.age )
Ford Prefect
42
So, “b” is a partially-connected twin of “a” from the Island of Doctor Moreau… a misshapen creature that likely would cause heinous bugs in your code!
The way to think about stuff in the OO paradigm is as things that interact with each other purely by messages. A message to an object asks it invoke an operation that transforms its state, returns a value (which, purely speaking, itself would be an object…), or does both. You don’t want the outside world to go messin’ with an object’s state directly, just via the set of messages it can respond to. The set of valid messages defines the interface that the object presents to the world. How it does things, behind the curtain, is hidden. That’s what one means by encapsulation. (Ok, there’s more to it… see the Encyclopedia Galatica, or, wikipedia as a starting point for more…) It enables all sorts of crunchy goodness - good modularity - when applied properly.
Usually (but not always in all languages…), a message consists of the method name and its arguments. E.g. in the syntax used here the method invocation a.name(’Bart’) is sending the message ‘name’ with the argument ‘Bart’ to the object referenced by ‘a’.
In OO hackin’, when you’re creating a bunch of objects to do something useful, you usually want to have objects each have references to several of the other objects so that they can interact with each other.
E.g., A bunch of person objects (a collection of person objects)named “dude”,”biker” and “hacker” all walk into a room object. The room object already contains a collection of person objects, “boring guy”, “Alan Kay” and “hot babe”. The new guys get added to the room’s collection of persons. Each one of the new person objects wants to iterate over the collection of persons in the room (skipping themselves, unless they have multiple personalities…) to execute the “scope_em_out” method on each person in the room - i.e., each new guy wants a list of references to each person in the room collection. Then they can choose their next message, if any, to send to one or more of the other persons (”dude” and “biker” both send “hi_howyadoin?” messages to the “hot babe” and to each other while “hacker” is totally into a “talk_with_guru” message to “Alan Kay”…).
BTW, Alan Kay and the rest of the brilliant gang at Xerox Parc were the inventors of Smalltalk, a ‘pure’ object language, starting around ‘69. There’s a fun, open source version still around and kickin’, called Squeak, and some commercial versions that a few companies still quietly use to get a lot done. Smalltalk is one of the few imperative languages even more fluid to hack than Matlab since one can actually change code *while it’s executing*. A good portion of the Squeak community are educators who use it to get kids (even elementary school) into programming in a really fun environment.
That was probaby waaaaaay more than you wanted, wobbly!…
Happy Matlabing!
Michael
Thanks Michael for this insight.
Dear Loren,
I regularly use the nested function trick for numerical computation. It makes life so much easier… However I find the usually fantastic help tool a little weak on this side.
I therefore had to write a tool for that (hyperSubFunHelp on MCFE). But I think an easy to use tool for displaying help about subfunctions is definitively lacking (help function>subfunction is not really easy to use).
Regards,
Mathias
Michael Corvin said:
>I’ve implemented several variants of OO frameworks based on
> closures that add inheritance.
Michael, are you willing to share your OO framework? I would love to be able to use something like this in Matlab.
I’m quite intrigued by the idea of using nested functions to do OOP in Matlab.
The one thing I wonder about is memory usage. I don’t really understand what gets stored into a function handle. I know that a function handle “contains” all the variables in its scope, but I’m not exactly sure how. If you create an object in the way described here, consisting of a struct with numerous function handles, I’m assuming that each handle effectively “points” to the same shared workspace of variables? this would be quite cheap and acceptable, even if the data in that workspace became quite large, since there would only be one copy.
I became concerned that the reality may be more memory-wasteful, when looking at the documentation for function handles and nested functions, which says
“What in fact happens though is that MATLAB stores variables such as VExt inside the function handle itself when it is being constructed.” (see the documentation for nested functions).
This can’t be correct, right? this would lead to the situation where each function handle had its own workspace.
But on the other hand, I find it somewhat magical that each of the function handles ends up pointing to the same workspace (what I want/hope to happen). It seems then that as you create a function handle, the interpreter must check to see if a workspace with the same variables in scope exists, in which case it doesn’t duplicate the workspace, but rather “points” to the first one. Is this what happens?
Vijay-
When the function handles are constructed in the same environment (same function, workspace and during the same invocation), they DO all share the same workspace - no extra copies. That’s how they can do the near magic that they can.
–Loren
With due appreciation for Loren’s Matlab prowess, it’s the comments by Michael Corvin that are the real revelation in this thread. So, in effect, Matlab has a second, ‘backdoor’ way of creating objects? Set up a function
function obj = myQuasiObject
..
with some nested functions
function nestedFunction1
..
function nestedFunction2
..
then create structure ‘obj’, and add to it
(a) arbitrary values (these are your class properties),
(b) handles to the nested functions (these are your class methods).
Voila! Class myQuasiObject is ready to instantiate
obj = myQuasiObject;
and manipulate - with the customary ‘dot’ syntax not available with Matlab’s ‘frontdoor’ objects.
obj.nestedFunction1
Not being able to have private properties and methods is a nuisance, but inheritance seems to be alive and well: why not invoke one ‘object function’ from within another?
Is this added functionality a good thing? I think that Loren’s code - just touching on the matter, really - gained but a tiny bit of syntactic sugar from not using a ‘normal’ class, with a transparent constructor and methods. On the other hand, consider this FEX submission, a purposeful play on persistence and scope implications of the ‘backdoor objects’ approach
http://www.mathworks.com/matlabcentral/fileexchange/saveRating.do
for an example of how one can create a debugging nightmare in less than 20 lines.
My apologies: here’s the correct link.
http://www.mathworks.com/matlabcentral/fileexchange/loadFile.do?objectId=18223&objectType=FILE