In this tutorial, we will be using several new objects to perform
procedural drawing with the
lcd object. The
counter object will provide enumerated step processing, the
line object will allow us to create smoothed output, and the
scale object will allow us to change the range of output. Finally, we will introduce a few mathematical objects that will help us create some common shapes that require simple trigonometry.
We often use programmatic and procedural constructs to perform tasks in our programs; these can present new opportunities for graphic display, but can also be used any time we need to count through a set of numbers, create a numerical trajectory, or scale values. Whether used for audio visualization, controller feedback or just an interesting bit of art, procedural code can serve as both a useful tool and an inspiration.
Take a look at the tutorial patcher. This patch has a few areas that help us understand some new objects, and another large drawing patch. At the top-left is a small patch that demonstrates the
counter object. When we click on the left
button, the object produces numeric output that increments by
1 for each
bang message it receives. When the output reaches
9, the next
bang will reset the output to
0. The arguments we've provided here to the
counter provide the
minimum and
maximum numbers for the output. When the object hits its maximum, it will loop back again to the minimum number. This allows us to use
counter to create
loops of values.
The
counter object is somewhat unusual in that it can accept a variable number of arguments:
• If you have
one argument, it sets the
maximum value of the
counter.
• If you have
two arguments, they set the
minimum and
maximum values of the
counter.
• If you have
three arguments, they set the
direction,
minimum and
maximum values of the
counter.
Our test patch for
counter includes an additional
button (colored blue) and two
number boxes to set values of the object. This is a great opportunity to use the
assistance on inlets to find out what they do. If we
unlock the patch and hover over the third inlet (which has both a
button and
number box connected), the assistance says “Resets Counter to Number on Next Clock”. This lets us change the current value of the
counter while it is running.
If we change the
number box to
5 then hit the first
button, we see that the output is the number
5 – regardless of what the previous value was. This is how we set the state of the
counter on the fly. Clicking on the second (blue)
button (which sends the
bang message) will reset the value to the minimum:
0 in this case.
The
number box on the right allows us to change the maximum value on the fly. This would be useful for drawing situations where you might need to change the size of the drawing area, or when you are sequencing an effect and need to change the number of steps. When we change this value and repeatedly hit the
button, we see that the new maximum value is honored.
The drawing patch has a section labeled
1; this is where we use the
counter object to procedurally draw in the
lcd object. When we start the
metro (using the connected
toggle object), we see that the patch begins drawing small circles in the
lcd, with the color gradually changing as it progresses. When the drawing reaches the bottom of the
lcd, it restarts drawing at the top.
Four different
counter objects are used in this patch; one to set the drawing location and size, and three others to change the color. Understanding the leftmost
counter is the most important: it counts from
0 to
767, giving us
768 steps total. The drawing X/Y location is calculated with the
% (modulo) and
/ (division) objects, breaking up our lcd object into a
24x
24 grid. If we look at the output of the
counter and the
% and
/ objects, we can see that the system works by creating two loops from
0 to
23, one going quickly (from the
% object) and the other incrementing each time the first loop has repeated (the
/ object). The values are multiplied by
10 (to get the pixel location for the top-left location), then have
10 added (to get the pixel location for the bottom-right). This sets up a lcd-filling grid of circles for our program to draw.
The three other
counter objects cycle through three different number ranges to give us an ever-changing color palette. These
counter objects use the three-argument initialization, where the initial
2 forces the counter to move upward to the maximum, then downward to the minimum. This is what causes the drawing to slowly change colors as it draws. Clicking the
button labeled "reset" will restart the simulation from the beginning of the loop.
If we return back to the top-left of the patcher, the second small example shows the use of the
scale object. This object allows you to set a range of input values and a range of output values; the object will then correctly
scale the values so that they are mapped into the new number range. In our example, input values between
0 and
100 are mapped to the range of
-1.0 to
1.0 – this is determined by the arguments provided to the object. If you change the input
number box, you will see that a value of
0 yields an output of
-1.0, a value of
50 will give us a value of
0.0, and a value of
100 will give us a result of
1.0. These numbers are mapped in a linear fashion across the range we provide.
If you choose to exceed the input range, you will find that the output continues to use the calculated range effect, thereby allowing you to exceed the expected range without failing. Hence, a value of 150 will translate into an output of 2.0, while a value of -100 will give you a value of -3. The arguments are used to determine the mathematical expression used for conversion.
The section of the drawing patch labeled
2 uses
scale to draw a shape based on sine wave calculations. A
counter object produces output ranging from
0 to the maximum value (set using the two
number boxes to the right of the
metro object). This is scaled from
0.0 to
6.283185 (2*pi), then sent to a
sin object, which calculates the
sine of the input value. This produces a sine wave that varies from
-1.0 to
1.0 in range. This value is sent to another
scale object which maps this range to the integer range
0 to
319 – the size range of the
lcd object. This is used to provide the X locations of the line. A similar set of functions are used to set the Y location. These numbers are then inserted into a list (using
pack, prepended with the
lineto message (using
prepend) and then sent to the
lcd.
Clear the
lcd using the
message box containing the
clear message. Turn on the
metro using the
toggle, and watch the result. The result is a sine-based drawing over the entire
lcd face. You can set the maximum values of the procedure by altering the
number boxes that are attached to the
counter objects. Note that the
number boxes change not only the parameters of the
counter objects, but also the maximum input range expected by the
scale objects immediately below (the third inlet for the
scale object determines the maximum input range expected).
The two
counter objects, when set to different maximum points, create different interlocking sine waves that draw across the
lcd. These curves are called
lissajous patterns and simulate what happens when out-of-phase oscillators are fed into the X and Y inputs of an oscilloscope. This is a great example of using simple math functions to create complex drawings.
Going back to the top-left of the patch, the third example gives us a chance to see the
line object in action. The
line object takes a pair of incoming values (configured as a value/time pairing) and slowly moves from one value to the next. If a single value is received, it jumps immediately to that value. However, when a second value is provided, it is treated as the number of milliseconds to move to that new value.
Clicking on the first
message box (labeled
100 1000) will move the
line from the starting value of
0 to the target value (
100) over
1000 milliseconds. Clicking on it again will show no change, because the current value (which remains at
100) is the same as the target value (it has nowhere to go). The line object is “state aware”, meaning that it understands its current value, and will adjust its action based on the state of that value.
Clicking on the second
message box (
0 5000) will move the result of the
line back to
0 over
5000 milliseconds (5 seconds). Clicking on the first box will move us back to
100.
The third
message box is a little more interesting. It uses a special function of the
message box to create multiple messages; when a
comma separates values, they are treated as separate message, and will function as if two messages were sent down the same patch cord immediately after one another. In this case, the value
20 is sent as the first message. Since there is no time pairing, the current value out of the
line immediately jumps to
20. Immediately afterward, the
50 2500 pair is sent, causing the output to slide from
20 to
50 over 2.5 seconds. We can also use the
number box to immediately jump the
line to a new value; since no timing value is provided, it too will cause the current state to jump to the entered value.
The section of the drawing patch labeled
3 is the most complex procedure we’ve yet seen. It depends on the
idle mechanism of the
lcd object to do some procedural drawing. When the
idle message is turned on (with the
idle 1 message), the
lcd object outputs the current mouse location relative to the
lcd object's canvas when you move your mouse over it. These X/Y coordinates are sent as a list out the second outlet of the
lcd.
The light blue patchcord out of the
lcd connects our mouse coordinates to an
unpack object back at the top. These values are sent into our drawing code, which creates a set of values used to draw a pair of round shapes that follow the cursor. The
line object is used to slow the changes in the shapes, and causes them to slowly "zero in" on the current cursor location. Turn on the
idle message with the
toggle box, then mouse around the
lcd object to see how it reacts.
There are two very important things in this procedure. First, the use of the
line object to slew the output values over a one second (
1000 ms) timeframe. This gives the results a more organic feel by causing them to
interpolate over time. Secondly, we use the
cartopol (
cartesian
to polar) object to change the X/Y ("Cartesian") position of the cursor into a Distance/Angle ("Polar") pair of coordinates, useful for drawing contents in a circular domain. Once the polar coordinates are calculated, they are scaled to match the size of the circular drawing area, then used as part of a complex, multi-message
message box to create the two “eyes” found in the middle of the
lcd object.
The
message box at the bottom of the code sends four drawing commands in sequence to the
lcd. The first two draw two background circles using the
paintoval command at fixed positions (
100 100 140 140 and
180 100 220 240). The last three arguments to
paintoval set the color (black:
0 0 0). The second two messages use the values from the
pack object to draw green "pupils" on the eyes. The four values provide the starting and ending angle for the pupils using the
paintarc message.
Understanding all of the functionality of this procedure may be difficult, but it is worth it – this is another example of using some mathematical programming to create a complex and life-like end result. Simple realtime graphics are made out of building blocks of procedural code just like this; taking a set of input values and scaling them to your needs is critical for many applications in Max.
We’ve seen some fairly complex programming that provides controlled drawing functionality within an
lcd object by creating loops (
counter), scaling input values to output values (
scale), and interpolating numbers over time (
line). We’ve also worked with a few objects that compute trigonometry. By using these new objects in combination, we can assert a lot of procedural control over what we want to accomplish in our programs.
See Also
Name |
Description |
counter |
Count the bang messages received, output the count
|
scale |
Map an input range of values to an output range
|
line |
Output numbers in a ramp from one value to another
|
sin |
Sine function
|
cartopol |
Cartesian to Polar coordinate conversion
|