Tutorial 22: Designing Equations
Introduction
This tutorial covers the creation of equations: mathematical statements that evaluate an expression based on input values. The key to equations in Max is the expr object, which provides a wealth of mathematical functions and a “write your own” mechanism for developing complex statements.
To this point, we have created somewhat complex mathematical processes using individual math objects (+, *, etc.). However, in many cases, we can collapse a number of objects into a single mathematical equation, eliminating a lot of unnecessary complexity in our patches. The tool used for this simplification is the expr object, which uses a specific syntax for creating an equation that accepts multiple inputs and can produce very complex results.
To open the tutorial patch please open 22mDesigningEquations.maxpat
from the the zip archive, which is available for download at the top of this page.
Overview of the drawing patch
Take a look at the tutorial patch to see the work we have ahead of us. This patch is broken into two encapsulated portions of patcher logic and a drawing routine foran lcd object, also encapsulated. Communication between the parts is accomplished using value objects and pairs of send and receive objects. To see the patch in action, turn on the metro at the left of the patcher using the green toggle box, then hit the space bar to trigger the events at the top of the patch. The result will vary from small scribbles to large-format abstract line-and-circle drawings. Each time you hit the space bar, the lcd will clear and a new, related drawing will begin to appear.
The comment box at the top of the patch shows three lines of mathematical equations:
x = sin(A*y)-z*cos(B*x);
y = z*sin(C*x)-cos(D*y);
z = E*sin(x);
These expressions compute three values (x
, y
, and z
) based on applying some simple trigonometric functions to their previous states as well as five fixed values (A
, B
, C
, D
, and E
). When plotted onto the lcd object (with z
visualized as a circle of variable size), we can see that a wide variety of patterns can be created. This equation generates something called a strange attractor - in essence an iterated function where the previous outputs of the equation combine with five variables (called coefficients) to generate a variety of shapes that exhibit chaotic behavior.
- Double-click the
Do_the_math
patcher object to open it.
Let’s examine each section of this patch. The lower-left corner of the main patch is obviously the key to making things draw – it generates bang
messages from a metro that are sent to the compute
and draw
receive objects, as well as into a subpatch that slowly varies the color of the drawing (more on this later). After the Changecolor
subpatch is evaluated, the Do_the_math
subpatch receives a bang
via its receivecompute
object. This updates the equation for the next round of drawing by triggering a number of value objects, sending their output through equations encoded in the expr objects. These then update the x
, y
, and z
values for drawing. We'll examine how these expr objects work in a moment.
- Double-click the
Draw_shape
patcher object to open it.
The send object named draw
sends bang
messages to the drawing routine in the Draw_shape
subpatch. This triggers the newly calculated x
, y
and z
values which are scaled, packed into command messages, and then put through a send object to the receive object attached to the lcd, which performs the drawing commands that were generated.
- Double-click the
Generate_coefficients
patcher object to open it.
Look at the upper left of the main patch. When the key object outputs a 32
(meaning that the space bar has been depressed), a button is used to generate a bang
message that is sent to the Generate_coefficients
subpatch This, in turn, performs seven actions in order from right to left: the generation of 5 random values (E
to A
) that are scaled from -4.0
to 4.0
, the transmission of a clear
message to the lcd object, and a reset of the value contained in the x
, y
and z
value objects. In essence, the space bar is the overall reset key for this patch, in that virtually every segment of the patch is affected when that key is hit.
Using expr for equation creation
- Double-click the
Do_the_math
patcher object to open it.
Now let’s look into the expr objects themselves. While they look complicated, the argument for each of the expr objects is a relatively simple equation. The important thing to realize is that the $
references simply refer to an incoming message, along with its message type and inlet number. So, for instance, using a reference named $f1
means that the equation should use an incoming message ($
) that is a floating-point number (f
) retrieved from inlet number 1 (1
). Once you realize that the apparent complexity of the equation is simply its reference to incoming messages, the format of the expr statement is a little more comprehensible.
The operations used in our expr statements are limited to the sin (sine function), cos (cosine) and * (multiply) functions; however, there are dozens of operations available. You can use the common math operators (+, -, * and /), logic (&&, ||), bitwise comparison (&, |) and other C language math functions such as abs, sin, cos, and pow. The documentation provides a complete list of the available functions.
In the case of our tutorial patch, the three expr objects use eight different variables (x
, y
, z
, A
, B
, C
, D
, E
) to create the three target values (x
, y
, z
). There is an interesting bit of feedback here: the target variables are not only used for the drawing routine, but are also used for the next round of computation. The five variables A
through E
only change when the space bar is hit, so they provide an anchor to the drawing routine while the ever-changing x
, y
and z
variables determine the individual line and circle drawings for each round of computation.
Adding your own equation
In order to make the drawing a little more interesting, let’s do some creative equation-making and use it to generate a constantly shifting foreground color. We are going to tap into the x
, y
and z
value objects that are updated on each bang
of the metro.
Double-click the patcher object labeled changecolor
at the bottom of the patch. We can see that the foreground color of the lcd is being altered by a trio of drunk objects that are pack ed into an frgb
message sent to the lcd. In here, we are going to use an expr object compute the exp
(e to the power of x) mathematical function. If you aren’t familiar with the properties of e, that’s OK – it’s sufficient to know that it is exponential in nature, and that the value will rapidly increase as the input value increases.
Delete the drunk objects, or disconnect them from the inlet and pack objects and move them out of the way. Create three new value objects with x
, y
, and z
as their respective arguments and connect the inlet object to their inlets.
Now create a new object and type in the following:
exprint(exp($f1)*5.)
Examining the argument/equation from the inside-out, we see that we are taking an incoming floating-point value ($f1
), using the exp
function to create a new value (exp($f1)
), multiplying it by 5.0 (*5.
) then turning it into an integer (int
). The result will be a number that is approximately in the range of color values (0-255), but is predominantly low in that number range. Copy this expr object two times, then connect the output of the three value objects (x
, y
and z
) to each of the expr objects. Connect the outlets of the expr objects into the pack, and see what happens.
When we start the metro and clear
the lcd, we will see that the drawing is colored, but tends toward the darker colors. Since the coloring is influenced by the calculated x
, y
and z
coordinates, the colors will tend to be brighter toward the extreme edges of the drawing window. This is an example of calculating drawing commands that might be difficult using traditional object-based programming, but is made quite easy using expr.
Summary
Using the expr object gives us the opportunity to create complex mathematical and logical equations without having to resort to large object programming segments. The huge number of operations that can be performed, in combination with the ability to define and use multiple inputs, allows for interesting results that might otherwise be cumbersome to create.