Here’s a problem. A fairly straightforward problem. We have a layer and we want it to orbit some point, but we want it to stay upright (meaning we don’t want the rotation to be affected).
The Simple (and Potentially Messy) Solution
The first solution is pretty simple. Make a null, place the null exactly where you want your layer to orbit around, parent your layer to the null, and rotate that null, baby! And to make sure the layer stays upright, we negate the null’s rotation by simply adding an expression into the orbiting layer’s Rotation property. We pick-whip the null’s Rotation property and multiply it by -1. It’ll look something like this:
Easy peasy! And if you want to be able to animate the rotation of your layer, you can add the “value” to your expression. (More on adding value.) Like so:
I should note: adding the parentheses around the first bit isn’t entirely necessary. Order of operations, don’t ya know. Math. Ever heard of it!? (You disgust me.)
So, why is this thing “potentially messy”? I’ll tell you why. I’ll tell you EXACTLY why…. once I’m finished building up the suspense….
I’ve started thinking about the use of expressions in big networks of layers. My hesitation to use the above solution stems from the fact that it takes two layers to work– so, you’re doubling the layer count of any comp made of a bunch of these layers. Also, you couldn’t really use this solution for something like the Center property of a Circle Effect (or similar effect that has a Center property). There’s an expression for that.
The More Math-y Solution
The second solution to this problem is based on the cyclical nature of Math.sin() and Math.cos(). Both return values between -1 and 1, but the wizardry happens in the fact that these waves are offset in such a way– when given 0 as an input– Math.sin() returns a 0, and Math.cos() returns a 1. Illustrated below.
If we pass a rotation value into these two expressions (using an Angle Control), we can mimic the effect of rotating a center null. Let’s add an Angle Control to our layer, and call it driver. We’ll also need to add a Slider Control, and it will be called size. And here’s the expression we’ll put into the Position property of our layer:
I’ll pick it apart piece by piece. We first set up our variables for the Expression Controls (size and driver). size is going to function as the radius of the circle around which our layer will be moving. Because Math.cos() and Math.sin() use radians to calculate their values, we have to convert the degrees from our Angle Control (driver) to radians. The next part– where we set up h and v– is where the magic happens.
The stuff in variable h is responsible for how the layer is moving horizontally. For h we feed our driver values into Math.sin(). Variable v is only responsible for how the layer moves vertically. For this, we’re feeding the values of driver into the Math.cos() and multiplying that by negative one. (The layer will move in the opposite direction of driver, if we don’t have the negative here.)
Before we multiply our expression by size, we get a layer–as we drag our driver control– that travels in a circle which has a radius of 1. Controlling the circle’s radius is as simple as multiplying by size. When size is 100, the circle has a radius of 100, for example.
To put it all together, we’re just dumping h and v in a Position array (where h is our x property and v is our y), multiplying by our size…. and then….!?!?! What is this? Adding value? What the fudge-factory, Steve!? The circle we’re traveling around, when we add value, is centered AT THAT LOCATION. So, when we punch 960 and 540 into the x and y Position properties (respectively), we get a circle, centered at [960,540], with a radius of whatever size happens to be. And another fun thing: we can parent this layer to another layer without the fear of it getting all whacky. Also, your layer’s Anchor Point, Scale, and Rotation properties will act just how you’d expect them to. There’s nothing crazy going on. (So, calm yourself. You’re making me nervous.)
Go forth! Make circles. Get dizzy.
EXTRA 3D FUN-ZONE
Because each dimension in our expression is controlled discretely, it’s absolutely possible to dump h and v into a Position array that has the third dimension (after you make your layer a 3D layer, of course). You could feed v into the z property of the array to have the layer orbit in the fashion of something like Saturn’s rings. “…but, Steve, there’s no up or down in space!” you exclaim in a stupid, whine-y voice. Fine. It’s like a hula hoop. Get over yourself. The expression would look like this:
You’ll notice we just have to include a zero to keep the place of the dimension that isn’t using Math.sin() or Math.cos(). In the above case, the y-dimension will be 0. And the same rules apply as before. All of your other Transform properties will function like normal. Parenting won’t give you any unexpected results. Remember: if you find that your driver values are making your layer orbit in the wrong direction (as seen in the gif above), you can remove the negative on Math.cos().
Away with you. (My Gist link for this expression: https://gist.github.com/thatsmadden/d31503aa3b4e03a76d3f)