Monday, August 07, 2006

Lesson 5: Modulus with If/Else

Here's a real example that came up recently that showcases the beauty of expressions. This example involves creating charts that are driven with Expression sliders. In doing so, we'll find some interesting quirks that we'll solve with expressions, that otherwise would be almost impossible to achieve.

What we are going to create is a bar chart. The area of the bar is going to be driven by an expression slider, as well as the data label. The example that I am taking this from is a 3D bar chart that I created for some sales presentations for WDIV NBC.

For our example here, make a simple solid that is 50x300 and a simple piece of text that says anything.. mine says "value".


Now, create a Null (Layer>New>Null Object). To this Null, apply the effect: Expression Controls> Slider Control. Your layers should look kind of like this:


Despite the fact that a Null is invisible, Expression Controls have absolutely no visible effect on their own. They exist only to serve as "controllers" for us to use exclusively with Expressions. The Slider Control is an keyframeable number slider providing number values for you to use with Expressions.

What we want to do is have the Expression Slider drive the Y scale of the solid, and the text label. The easiest way to do this is to utilize the "pickwhip". I've purposely waited until the fifth lesson to even discuss this, as to not make you too dependent on it. The pickwhip is the little swirly icon to the right of the parameter that appears when you create an expression. The purpose of the pick-whip is to allow you to "grab" any value from any parameter. The result is that the code that you would need to enter.

To drive the scale of the solid from the Slider, first create an expression for the scale of the solid (opt/alt click on the scale parameter). What we want is for x-scale to be left alone, and y-scale to be driven by the slider.

x = 100 ;
y = thisComp.layer("Null 1").effect("Slider Control")("Slider");
[ x , y ]

The code for "y =" is simply retrieved using the pickwhip, with a semicolon at the end, like this:


Here, I typed everything out first, before entering the code from the pick-whip, to illustrate things more clearly. Notice that I PUT THE CURSOR WHERE I WANT THE CODE TO GO. Don't forget to put a semicolon, either.

You'll see now that dragging the Expression Slider on Null 1 makes the scale of the solid change, but the anchor point is in the wrong spot and needs to be moved to the bottom (double-click the solid layer and move the anchor to the bottom). After moving the anchor, you'll have to reposition your solid, as you'll notice that it has moved.


Next, the type needs to be driven by the slider, too. Twirl open the type layer, and locate the "Source Text". Create an expression for this and drag its pickwhip to the slider as well.


What you'll see is that the bar and data label now work in sync, driven by an expression. GREAT! But, there's a couple problems. First, try this: animate the slider value from 0 to 50.

The value of the data label is reflecting the extreme accuracy of the Expression Slider by showing a great number of decimal values. We want to round that off.

Let's learn a new term:

Math.round(value)

Simply put, Math.round rounds what is inside the parantheses to the nearest whole number.

In our example, declare the code that we got via the pickup to be equal to a variable, like this:
sliderVal = thisComp.layer("Null 1").effect("Slider Control")("Slider");.

At this point, we could round this by using "Math.round(v)". However, what that would do is round v to the nearest integer, but we should get this to tenths as we should show a little more accuracy. To do this, we need to use a common Javascript technique thata goes like this:

Math.round(value*10)/10

Within Javascript/Expressions, this does exactly the trick. Multiplying the number to be rounded by 10, then dividing by 10 will round the number to tenths. Pretty cool.

But notice one more problem, and this stumped me for a bit. On integer numbers, there are no tenths. Javascript has no reason to show them whatsoever, as "25.0" and "25" are identical to a computer. But, aesthetically, we would want our numbers to ALWAYS be in tenths, rather than jump around from tenths to whole numbers and back again. You'd think there would be a very easy way to do this, but there really isn't.

To figure out how to do this, we need to learn about one more thing: "modulus". Modulus is an "operator", just like plus, minus, divide, and multiply ( + - / *). Modulus is expressed with the following term:
%

What this does is return the remainder after dividing two numbers. For example:

17 % 5

This would return "2". 17 /5 = 3 with a remainder of 2.

10 % 5

This would return "0". 10 /5 = 2 with a remainder of 0.

SO, let's think out our decimal problem in plain English:

If the value of the expression slider is integer,

then we need to add a ".0" to the end of the number.

otherwise,

leave the number alone.

So, we have our variable, I'll use 'sliderVal' in this case:

v = thisComp.layer("Null 1").effect("Slider Control")("Slider");

We need to check to see if this number is an integer or not. I did this by doing this:

check = v % 1

I declare the variable "check" to be equal to the remainder of y divided by 1. If check is "0" the number is integer, if not the number is non-integer. See where we are going with this? Let's cut to the chase and show the code:

sliderVal = thisComp.layer("Null 1").effect("Slider Control")("Slider"); v = Math.round(sliderVal*10)/10;
check = v % 1

if (check == 0){
"" + v + ".0"
}else{
v
}

OHMIGOSH, you might be saying. Don't panic.

First, we have a variable with a bunch code that we got by using the pickwhip. Don't even worry at this point what it all means:

sliderVal = thisComp.layer("Null 1").effect("Slider Control")("Slider");
Then, we round sliderVal to tenths:

v = Math.round(sliderVal*10)/10;
Next, we test to see if we have a number that is integer (with no decimal) or non-integer (with a decimal). We do this by declaring a variable to be equal to the remainder of the result of v divided by 1, which is expressed as:

check = v % 1
Next, think back to the format of if/else:

if (condition){
result1
}else{
result2
}

The condition we are checking for is "if (check == 0)".

You may be asking, what's with the "=="? In Javascript/Expressions, when we declare something, like "x = 5", we obviously use a single equals sign. When we are comparing two different values, like "if (x == 5)", then we use two equals signs.

If the condition is true, then we move to result1:

"" + v + ".0"

What kind of jibberish is this?

Here, we are working with text characters. Just like we can add "5+5" with numbers, with text we can add 15 + ".0" to create 15.0 Keeping this in mind, using v + ".0" would add a ".0" to the v. That all makes sense. What's with the double-quotations ""?

In Javascript, when we are adding characters together (like the ".0") we need to say right up front that we are going to be adding characters (not numbers) together. Being that Expressions start at the left and move to the right, on the leftmost part of the expression, we need to to start right off with some empty type "" and then continue adding our type together.

Remember, Expressions are efficient and do not want to display a ".0" so we are just ADDING the text of a ".0" on the end of numbers that do not have it. Also, we could add any sort of characters like "$" or "%" to this as well.

Moving on, we have our result2:

v

This is simply the rounded value. We don't need to do anything to it.

Then, we close our bracket:

}
Granted, it took me about 30x as long to write this tutorial than to come up with the logic and expression to fix the little chart anomaly. So, if this seems overly complicated for something simple, it really isn't. The code is small, works perfectly and isn't that hard to wrap your head around.

Thanks for reading! Check back soon for another lesson in Expressions.