In this tutorial, we will extend the concept of encapsulation to include abstraction – the ability to have subpatcher logic live in a separate, reusable file that you can then use inside of any patcher you like. Once saved outside as a separate file, abstractions can be modified to use arguments to make a generic Max patch useful to your specific application. By using abstraction for your most-used programming tasks, you will be able to reuse that work for future projects without any other programming at all.
Abstractions are key to supporting your increased Max knowledge while maximizing your ability to reuse previous work. The abstraction mechanism can make your subpatches look and act like built-in Max objects, and can also accept arguments to further tune its functionality.
In our tutorial patcher, you will see that there are three different patches. In this case, each of them do the exact same thing, but at different levels of abstraction. The first section (labeled
1) is the fully-functional patch. If you turn on the
metro with the
toggle, we see that the patch tracks your cursor around the screen and draws a scaled version of your mouse movements in the
lcd display. We make use of the
bucket object in this patch; a simple example of the object is shown on the left next to the
lcd; in its most simple form, upon receiving a value,
bucket always puts out the last value it received. This makes it useful as a single-event delay object, which we use in the main patcher to construct
linesegment messages that link the current position of the mouse to its previous position.
In the case of section 1, we have a lot of logic on the screen, and it might be useful to encapsulate it into a subpatcher (as we did in the previous tutorial). However, the patcher logic that polls the mouse and scales it based on the size of our screen looks like something that could be useful elsewhere; it would be interesting to have it available for other patches without having to copy and paste from one patch to the next.
This is where abstractions come into play. An abstraction is a subpatcher that is saved as an external file, and can be used just like a standard Max object. As long as your abstraction can be found in the Max file path, you can type its name into a new object box and it will be loaded directly into your patch. The abstractions used in this tutorial are in the same folder as the patcher accessing them; a library of abstractions can easily be created by placing them in a folder (or a bunch of folders) inside the "patches" folder of your Max installation, or anywhere else Max looks for files.
The second section of our patch (labeled
2) shows an abstraction at work. Turn off the
metro for section
1, hit the space bar (which will
clear the
lcd) and turn on the
metro for section
2. You will see that the program works exactly as it did before. The “object” called
WTHITM (for "Where The Heck Is The Mouse?") is actually an
abstraction of the scaling logic from section
1. If you double-click on the
WTHITM abstraction, a new patcher window will display the contents of this abstraction. Notice that this looks very much like an encapsulation with one exception – you cannot unlock the window to edit the contents. This is because
WTHITM exists as a separate patcher
file living in the same folder as our tutorial patch.
Abstractions are meant to be shared among several patches, so you would not want to edit the contents in one patcher, since this might break its functionality in other patchers. If you do want to edit an abstraction, you need to open the abstraction file itself.
While there are several ways to open the abstraction file, we'll mention only two: We can open a New File Browser (from the File menu) and type WTHITM into to the search window and then double-click on the patcher file that appears in response to our query, or we can Alt/Option-Click on the title bar of the WTHITM patcher window - a pop-up will appear with the patcher hierarchy from the abstraction leading up to the top. If we select WTHITM, the original file being referenced will open within Max. However you choose to do it, open the WTHITM source file. You will again see the abstraction contents, but now you can edit the logic. If you change something and save the file, all patchers that use this abstraction will reflect the changes – even if the patcher is currently loaded. You can see this in action by adding a second inlet to the WTHITM abstraction and saving it. The instant that Max sees a new version of the abstraction, it reloads it and adds a new inlet to the WTHITM abstraction in your tutorial patch.
While we have the
WTHITM patch open, let’s look at the documentation that has been added to the
inlet and
outlet objects. If you open the inspector for the
inlet, you see that there are several different types of built-in documentation. The first is called
Annotation; any text placed in the annotation field will show up in the
Clue window when you hover over the object. Text placed in the second documentation field, the
Hint field, will display in a hint balloon when you hover over the
inlet object in a
locked patcher. Finally, text placed in the
Comment section doesn’t display in the patcher; rather, this text is shown as the
assistance text when you hover over the
inlet of the "object" in the higher-level patcher. It is wise to document your
inlet and
outlet objects in often-used abstractions, since it can provide simple but effective documentation for your reusable logic.
In our
WTHITM abstraction, there is no logic that needs to know about the higher level patcher. However, what if we wanted the abstraction to properly scale the output based on our
lcd object’s size? In this case, we would have to inform the abstraction of the
lcd display size, which we can do using
arguments to the abstraction.
Section
3 of the tutorial patch is similar to section
2, but the abstraction used is called
WTHITM_scaled, and includes two
arguments that represent the horizontal and vertical sizes of the
lcd object. If we open the "WTHITM_scaled.maxpat" patch (using the
Open command in the
File menu), we see something very interesting: the multiplication factors use
#1 and
#2 as placeholders for values to be provided as arguments. As you might expect,
#1 is replaced with the value of the first argument, while
#2 is replaced with the value of the second.
The use of these replaceable values (called
pound-sign arguments) is key to making abstractions that are flexible and reusable. If you would have “319.” and “239.” hard-coded into the abstraction, you could only use 320x240
lcd objects to display the mouse movements. On the other hand, if you forced the top-level patcher to perform the final scaling (as we did in section
2), you are forcing the top-level patch to duplicate work that could be easily abstracted into the lower-level patch. By using replaceable values in your abstractions, it makes the high-level patcher as simple as is practical.
By saving your logic in an abstraction, you can create modules that can be used in future work with little or no additional programming. This allows you to parlay your Max knowledge into more efficient work in the future, and will help you create programming systems that are modular and easier to maintain.
See Also
Name |
Description |
bucket |
Pass a number from outlet to outlet, out each one in turn
|