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 for each message it receives. When the output reaches , the next will reset the output to . The arguments we've provided here to the counter
provide the minimum
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
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
values of the counter
• If you have three
arguments, they set the direction
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
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 then hit the first button
, we see that the output is the number – 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 message) will reset the value to the minimum: in this case.
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 to , giving us steps total. The drawing X/Y location is calculated with the %
(modulo) and /
(division) objects, breaking up our lcd object into a x grid. If we look at the output of the counter
and the %
objects, we can see that the system works by creating two loops from to , one going quickly (from the %
object) and the other incrementing each time the first loop has repeated (the /
object). The values are multiplied by (to get the pixel location for the top-left location), then have 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 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 and are mapped to the range of to – this is determined by the arguments provided to the object. If you change the input number
box, you will see that a value of yields an output of , a value of will give us a value of , and a value of will give us a result of . 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
will translate into an output of , while a value of will give you a value of . The arguments are used to determine the mathematical expression used for conversion.
The section of the drawing patch labeled 2
to draw a shape based on sine wave calculations. A counter
object produces output ranging from to the maximum value (set using the two number
boxes to the right of the metro
object). This is scaled from to (2*pi), then sent to a sin
object, which calculates the sine
of the input value. This produces a sine wave that varies from to in range. This value is sent to another scale
object which maps this range to the integer range to – the size range of the 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 message (using prepend
) and then sent to the lcd
Clear the lcd
using the message
box containing the 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 ) will move the line
from the starting value of to the target value ( ) over milliseconds. Clicking on it again will show no change, because the current value (which remains at ) 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 ( ) will move the result of the line
back to over milliseconds (5 seconds). Clicking on the first box will move us back to .
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 is sent as the first message. Since there is no time pairing, the current value out of the line
immediately jumps to . Immediately afterward, the pair is sent, causing the output to slide from to 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 mechanism of the lcd
object to do some procedural drawing. When the message is turned on (with the 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 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 ( ms) timeframe. This gives the results a more organic feel by causing them to interpolate
over time. Secondly, we use the cartopol
tesian to pol
ar) 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
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 command at fixed positions ( and ). The last three arguments to set the color (black: ). 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 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.
Count the bang messages received, output the count
Map an input range of values to an output range
Output numbers in a ramp from one value to another
Cartesian to Polar coordinate conversion