Tutorial 2: Create a Matrix
What's a Matrix?
This tutorial chapter demonstrates some ways to manage numerical data in a matrix.
If you are in doubt about what a matrix is, please first read the chapter titled What is a Matrix? in the Topics section. To summarize here, a matrix is a scheme for storing and modifying large sets of numerical data, by placing the values in a virtual grid. By storing data in a matrix, we can easily identify a particular value by its location in the grid, and we can modify many values at once by referring to all or part of a matrix.
In the previous tutorial chapter, we used the jit.window object to open a window and display the contents of a matrix as colored pixels. The matrix being displayed was from the jit.movie object, an object that continually fills its matrix with the current frame of a QuickTime video. The fact that jit.window was displaying a video, however, was just because that happened to be the contents of the matrix it was being told to display; but in fact any numerical values in a matrix can be visualized similarly. The example patch for this tutorial will show an even simpler example that should help strengthen your understanding of a matrix, the central idea of Jitter.
The jit.matrix object
The jit.matrix object simply creates a matrix, a storage space in memory. We can then store and retrieve numerical values in the matrix, and we can use other objects to print the values or display them visually. The arguments for jit.matrix are an optional argument (not included in this example), the (how many values will be stored in each cell of the matrix), the of data (how many bytes to use to represent each number), and then the (short for "dimensions", describing how large the matrix will be). We use the brackets to indicate that this is not the actual word you type in as an argument, but rather a description of the meaning of the argument. The object in this example will create a matrix with 1 plane (one number in each matrix location), using the char data type (single-byte values), with dimensions 16x12 (which equals 192 cells). From this we can deduce that the matrix is capable of holding 192 individual numerical values, with each value ranging from 0 to 255 (that's the range of a single byte).
We've connected a button object to the inlet of jit.matrix. You'll recall that Jitter objects send out their matrix name when they receive a message in their left inlet, so this button will permit us to trigger jit.matrix to send out its matrix name (in the form of a message).
The jit.print object
Beneath the jit.matrix object there is another new Jitter object, jit.print. This object accepts a matrix name (i.e., a message) in its inlet, and formats the values of the matrix—the sheer number of which can be pretty unwieldy—for printing in the Max Console. It prints the formatted values directly to the Max Console, much like Max's print object, and then passes the matrix name out its left outlet in a message (in the normal manner for Jitter objects).
In the Max Console you will now see the text jit.print object, so we can see that that is what came out of the outlet of jit.matrix. Because we didn't choose a name for that matrix (we didn't provide a name as the first typed-in argument to jit.matrix), jit.matrix assigned a name of its own choosing. It tries to generate a unique name that's not likely to be used elsewhere, so it usually chooses something strange like " ". In this case we don't really care what the name is, but it does tell the jit.print object what matrix of data to format for printing.. The word shows us that this was printed by the
Below that, you will see all of the values in this particular matrix, formatted neatly in 16 columns and 12 rows. Those came from jit.print. They're all right now, because we haven't placed anything else in the matrix yet.
Setting and querying values in a matrix
In the previous chapter we saw how an entire matrix could be filled automatically with frames of color data from a movie. It is also possible to place numerical values in specific individual cells of the matrix, and retrieve values from specific locations. The objects immediately above jit.matrix in this example show a few messages you can use for setting and getting values in a matrix.
You can store values in a particular matrix location with the jit.matrix would set the value of the very first cell of the matrix (i.e., the cell in the upper-left corner) to 127. This is because we number the cell coordinates in each dimension starting with 0 and going up to 1 less than the extent of that dimension. So, in this matrix, locations in the x dimension are numbered from 0 to 15 and locations in the y dimension are numbered 0 to 11. Thus, the cell in the lower right corner would be described with the cell coordinates .message. The syntax for doing so is: . For example, the message to our
(If there were more than one plane in this matrix, we could set the values in a particular plane of a cell, or in all planes of the cell. However, this matrix has only one plane, so we'll leave that for another time.)
In your Patcher window you may have noticed a change in the black rectangular region. The upper-left and lower-right corners of it have changed.
This region is a user interface object called jit.pwindow. In the Add Object palette it appears like this:
When you click this object in the Add Object palette or drag it into the Patcher window, it creates a small rectangular object. (You can drag the grow bar in the lower-right corner of the object to adjust its dimensions.) This object is pretty much equivalent in function to the jit.window object, but it displays matrix data inside the Patcher window, rather than in a separate window.
So, here we see quite literally the display of numerical values (in this case, char data in the range from 0 to 255) as color. Because there is only one plane in this matrix we're displaying, the display is monochrome—that is, greyscale. The 0 values are totally black, and the other values are some level of gray, up to 255 which is totally white. Thus, the 255 value at cell 15, 11 is displayed as white, and the 127 value at 0, 0 is displayed as a 50% gray, halfway between black and white.
You might say, "That's all very well, but it will be pretty tedious to fill a large matrix this way." And you'd be right. But of course Max allows us to write another part of the program that will automate the process.
Filling a matrix algorithmically
When the uzi object receives a (from the button labeled "fill" in the main Patcher window) it quickly counts from 1 to 12 out its right outlet and sends a for each count out its left outlet. Those s trigger the uzi object, so that it sends out numbers from 1 to 16 each time. We subtract 1 from these numbers so that they actually go from 0 to 11 and from 0 to 15, and we use the resulting numbers as y and x positions in the matrix. For each of the 12 y positions, the uzi object specifies all the x positions, and then uses those numbers in a mathematical expression (in expr) to calculate the value to be stored at that position. These numbers are sent out the outlets, and are used to create well-formed messages in the main patch, just as we did by hand earlier.
The mathematical expression here is relatively unimportant. It could be any formula that generates an interesting pattern of data. In this case we have chosen a formula that will produce a sinusoidal gradation of brightness in each column, and the will cause the overall brightness of the columns to increase from left to right (i.e., as x increases).
Even for a small 16x12 matrix like this, it's tough for us to perceive a trend in numerical data just by looking at a printout of numbers in the Max Console. However, the display in the jit.pwindow gives us a very clear and immediate idea of how the values vary within the matrix. This demonstrates one of the benefits of visualization of numerical data.
You can no doubt imagine other ways to fill a matrix algorithmically in Max, and in fact we'll demonstrate other ways in later tutorial chapters.
Other messages to jit.matrix
There are many other messages understood by jit.matrix, more than we can demonstrate fully here. On the right side of the Patcher we show a couple of other handy messages for filling a jit.matrix instantly with all the same values. The message to jit.matrix sets all its values to 0, and the message (the word followed by a value) sets every position in the matrix to that value.
We also demonstrate the jit.matrix to send the cell coordinates and value of that position out its right outlet.message. The word followed by a location in the matrix (x and y positions) will cause
In future tutorial chapters you will see various ways to use values retrieved from a matrix.
The jit.matrix object creates a storage space for a named matrix of data, with whatever dimensions, planes, and data type you specify. This matrix can be filled with data from another Jitter object (such as jit.movie), or by messages such as to set the value in all cells or to set a specific cell. You can use an algorithm elsewhere in the patch to fill the matrix according to a formula or a set of rules.
To get the data in a specific cell, you can use the jit.print object to format the matrix data and print it. To see the matrix data displayed as colors, use the jit.pwindow object. This is similar to the use of the jit.window object demonstrated in Tutorial 1.message. To see all of the numerical data printed out in the Max Console, use the
In this tutorial we viewed numerical data that we generated ourselves, rather than digital video as in the preceding chapter. The principle of storage is the same in both cases. Whether a matrix is used to store color information for each pixel from a frame of digital video, or abstract numbers which we wish to view as color, the numbers in both chapters are stored in a two-dimensional matrix and are easily displayed in a jit.window or jit.pwindow.
|Video and Graphics Tutorial 5: Jitter Matrix Exploration Part 1|