## Rotate CC Sphere around any axis

Scripting for After Effects

### Rotate CC Sphere around any axis

Hey guys,
the options for rotating a CC Sphere are pretty limited.
A quick example: Think of a grid mapped to the sphere, so it looks like lines for longitude and latitude and do some z rotation. Now rotating around the north-south-axis is not possible anymore, the y-rotation does something different.
I know I could achieve this particular effect animating the grid effect, but that's not the point. I just wanted to demonstrate, how it is not possible to freely choose your rotation axis.

As the math of an expression that lets you choose a rotation axis will be pretty sophisticated I wanted to ask, if this has been done this before orif someone is a mathematical genius and wants to help. Regards,
Julian

Edit: After some further research I think, you'd propably have to use quaternions or rotation matrices as intermediate steps for the conversion to different rotation axis.
illuminatus

Posts: 141
Joined: 02/22/2009, 11:30 am

### Re: Rotate CC Sphere around any axis

When you rotate the sphere, the plugin's axis are also rotated. Off-hand I don't think it's possible to
achieve what you're looking for with only expressions applied to the three rotation parameters since
each one will need to reference the other two.

I would apply Motion Tile and Transform before the CC Sphere and then animate the Tile Center parameter of Motion Tile for the X and Y rotation, and animate the Rotation parameter of the Transform effect for the Z rotation.
robbie26

Posts: 5
Joined: 08/26/2009, 3:09 pm

### Re: Rotate CC Sphere around any axis

Hey,
I can't move the map around (Motion Tile), because I use inverse polar coordinates to avoid mapping-related distortion. I could of course use the CycoreFX Sphere Utilites, if could affort them...
What I meant was using 3 extra angle controls and computing the XYZ-rotation for the CC Sphere from those values. I know that the axes are also rotated, that's why it's such a complicated computation.

Greetings,
Julian
illuminatus

Posts: 141
Joined: 02/22/2009, 11:30 am

### Re: Rotate CC Sphere around any axis

Hey guys,
I did it and wrote/ported some code to do what I want. Turns out it is also pretty useful to avoid gimbal lock.

With the following code you can do anything rotation-related to a CC Sphere:
- Choose the order of axis to rotate
- Choose if you want to rotate the axis with the sphere or leave them static
- Rotate it around a custom defined axis
- Parent the rotation of a sphere to a master sphere
- Rotate the a sphere, even though it is linked to a camera

As I found some forum posts over at Creative Cow, where some people apparently had the same problem, I decided to publish it here and added some explanations. Feel free to comment on them, if something is not quite understandable.

As I can't upload a .h or .txt file, I will just paste it in here.

Regards,
Julian

Code: Select all
`/**** EulerAngles.h - Mess around with rotation angles (of CC Sphere) ****//* Julian Krick, 2011 *//* Euler angles conversion ported, original by Ken Shoemake (Graphics Gems, 1993) <-- Appearently a very awesome guy! *//* - Convert Euler angles to/from matrix   - Support for 24 angle schemes   - Rotate around custom axis   - Parent multiple spheres *//*#################################################################  ########## Scroll down for the part you have to edit  ###########  #################################################################*//*** Order type constants, constructors, extractors ***/    /* There are 24 possible conventions, designated by:    */    /*     o EulAxI = axis used initially          */    /*     o EulPar = parity of axis permutation          */    /*     o EulRep = repetition of initial axis as last       */    /*     o EulFrm = frame from which axes are taken       */    /* Axes I,J,K will be a permutation of X,Y,Z.       */    /* Axis H will be either I or K, depending on EulRep.   */    /* Frame S takes axes from initial static frame.       */    /* If ord = (AxI=X, Par=Even, Rep=No, Frm=S), then       */    /* {a,b,c,ord} means Rz(c)Ry(b)Rx(a), where Rz(c)v       */    /* rotates v around Z by c radians.             */FLT_EPSILON = 1e-20;EulFrmS = 0;EulFrmR = 1;function EulFrm(ord) { return ord&1; };EulRepNo = 0;EulRepYes = 1;function EulRep(ord) { return (ord>>1)&1; };EulParEven = 0;EulParOdd = 1;function EulPar(ord) { return (ord>>2)&1; };EulSafe = [0,1,2,0]; //EulSafe = "/000/001/002/000/";EulNext = [1,2,0,1]; //EulNext = "/001/002/000/001/";function EulAxI(ord) { return EulSafe[(ord>>3)&3]; };function EulAxJ(ord) { return EulNext[EulAxI(ord)+(EulPar(ord)==EulParOdd)]; };function EulAxK(ord) { return EulNext[EulAxI(ord)+(EulPar(ord)!=EulParOdd)]; };function EulAxH(ord) { return (EulRep(ord)==EulRepNo)?EulAxK(ord):EulAxI(ord); };    /* EulGetOrd unpacks all useful information about order simultaneously. */    /* EulOrd creates an order value between 0 and 23 from 4-tuple choices. */function EulOrd(i,p,r,f){   return ((((((i)<<1)+(p))<<1)+(r))<<1)+(f);}    /* Static axes */EulOrdXYZs =   EulOrd(0,EulParEven,EulRepNo,EulFrmS);EulOrdXYXs =   EulOrd(0,EulParEven,EulRepYes,EulFrmS);EulOrdXZYs =   EulOrd(0,EulParOdd,EulRepNo,EulFrmS);EulOrdXZXs =   EulOrd(0,EulParOdd,EulRepYes,EulFrmS);EulOrdYZXs =   EulOrd(1,EulParEven,EulRepNo,EulFrmS);EulOrdYZYs =   EulOrd(1,EulParEven,EulRepYes,EulFrmS);EulOrdYXZs =   EulOrd(1,EulParOdd,EulRepNo,EulFrmS);EulOrdYXYs =   EulOrd(1,EulParOdd,EulRepYes,EulFrmS);EulOrdZXYs =   EulOrd(2,EulParEven,EulRepNo,EulFrmS);EulOrdZXZs =   EulOrd(2,EulParEven,EulRepYes,EulFrmS);EulOrdZYXs =   EulOrd(2,EulParOdd,EulRepNo,EulFrmS);EulOrdZYZs =   EulOrd(2,EulParOdd,EulRepYes,EulFrmS);    /* Rotating axes */EulOrdZYXr =   EulOrd(0,EulParEven,EulRepNo,EulFrmR);EulOrdXYXr =   EulOrd(0,EulParEven,EulRepYes,EulFrmR);EulOrdYZXr =   EulOrd(0,EulParOdd,EulRepNo,EulFrmR);EulOrdXZXr =   EulOrd(0,EulParOdd,EulRepYes,EulFrmR);EulOrdXZYr =   EulOrd(1,EulParEven,EulRepNo,EulFrmR);EulOrdYZYr =   EulOrd(1,EulParEven,EulRepYes,EulFrmR);EulOrdZXYr =   EulOrd(1,EulParOdd,EulRepNo,EulFrmR);EulOrdYXYr =   EulOrd(1,EulParOdd,EulRepYes,EulFrmR);EulOrdYXZr =   EulOrd(2,EulParEven,EulRepNo,EulFrmR);EulOrdZXZr =   EulOrd(2,EulParEven,EulRepYes,EulFrmR);EulOrdXYZr =   EulOrd(2,EulParOdd,EulRepNo,EulFrmR);EulOrdZYZr =   EulOrd(2,EulParOdd,EulRepYes,EulFrmR);function Eul_(ai, aj, ah, order){    // x = 0, y = 1, z = 2, w = 3    ea = ai; ea = aj; ea = ah;    ea = order;    return ea;}/* Construct matrix from Euler angles (in radians). */function Eul_ToHMatrix(ea){   M = [[0,0,0],[0,0,0],[0,0,0]];    //double ti, tj, th, ci, cj, ch, si, sj, sh, cc, cs, sc, ss;    //int i,j,k,h,n,s,f;   // Get Eul_Ord   o=ea&31; // take order, kill higher bits   f=o&1;   o>>=1;   s=o&1;   o>>=1;   n=o&1;   o>>=1;   i=EulSafe[o&3];   j=EulNext[i+n];   k=EulNext[i+1-n];   h=s?k:i;    if (f==EulFrmR) { t = ea; ea = ea; ea = t;}    if (n==EulParOdd) { ea = -ea; ea = -ea; ea = -ea;}    ti = ea;     tj = ea;   th = ea;    ci = Math.cos(ti); cj = Math.cos(tj); ch = Math.cos(th);    si = Math.sin(ti); sj = Math.sin(tj); sh = Math.sin(th);    cc = ci*ch; cs = ci*sh; sc = si*ch; ss = si*sh;    if (s==EulRepYes) {   M[i][i] = cj;     M[i][j] =  sj*si;    M[i][k] =  sj*ci;   M[j][i] = sj*sh;  M[j][j] = -cj*ss+cc; M[j][k] = -cj*cs-sc;   M[k][i] = -sj*ch; M[k][j] =  cj*sc+cs; M[k][k] =  cj*cc-ss;    } else {   M[i][i] = cj*ch; M[i][j] = sj*sc-cs; M[i][k] = sj*cc+ss;   M[j][i] = cj*sh; M[j][j] = sj*ss+cc; M[j][k] = sj*cs-sc;   M[k][i] = -sj;    M[k][j] = cj*si;    M[k][k] = cj*ci;    }    // We don't need a fourth row and column without quaternions M=M=M=M=M=M=0.0; M=0.0;    return M;}/* Convert matrix to Euler angles (in radians). */function Eul_FromHMatrix(M, order){    ea = [0,0,0,0];    // int i,j,k,h,n,s,f;   // Get Eul_Ord   o=order&31; // take order, kill higher bits   f=o&1;   o>>=1;   s=o&1;   o>>=1;   n=o&1;   o>>=1;   i=EulSafe[o&3];   j=EulNext[i+n];   k=EulNext[i+1-n];   h=s?k:i;    if (s==EulRepYes) {   sy = Math.sqrt(M[i][j]*M[i][j] + M[i][k]*M[i][k]);   if (sy > 16*FLT_EPSILON) {       ea = Math.atan2(M[i][j], M[i][k]);       ea = Math.atan2(sy, M[i][i]);       ea = Math.atan2(M[j][i], -M[k][i]);   } else {       ea = Math.atan2(-M[j][k], M[j][j]);       ea = Math.atan2(sy, M[i][i]);       ea = 0;   }    } else {   cy = Math.sqrt(M[i][i]*M[i][i] + M[j][i]*M[j][i]);   if (cy > 16*FLT_EPSILON) {       ea = Math.atan2(M[k][j], M[k][k]);       ea = Math.atan2(-M[k][i], cy);       ea = Math.atan2(M[j][i], M[i][i]);   } else {       ea = Math.atan2(-M[j][k], M[j][j]);       ea = Math.atan2(-M[k][i], cy);       ea = 0;   }    }    if (n==EulParOdd) {ea = -ea; ea = - ea; ea = -ea;}    if (f==EulFrmR) {t = ea; ea = ea; ea = t;}    ea = order;    return ea;}// Multiply matrices A and B (A*B) - Keep in mind, that you have to do B*A, if you want to do the rotation of A firstfunction multMatrices(A, B){   C = [[0,0,0],[0,0,0],[0,0,0]];   for(i = 0; i < 3; i++) // row i      for(j = 0; j < 3; C[i][j++] = sum) // column j         for(sum = k = 0; k < 3; k++)            sum += A[i][k] * B[k][j];   return C;}// Create matrix for rotation by phi around the axis specified by surface normal n = [x,y,z]function rotMatrixFromAxis(n, phi){   c = Math.cos(phi);   cc = 1 - c;   s = Math.sin(phi);   return [[n*n*cc + c,   n*n*cc - n*s,   n*n*cc + n*s],      [n*n*cc + n*s,   n*n*cc + c,   n*n*cc - n*s],      [n*n*cc - n*s,   n*n*cc + n*s,   n*n*cc + c]];}/*############################################################  ########## Here begins the code you have to edit ###########  ############################################################### Preparation ### (necessary) - Include the file (you can also just paste everything) in each expression for each rotation with      \$.evalFile("path"); - Then add:      outAngs[x] * 180 / Math.PI   (where x is 0,1 or 2 for X-,Y- or Z-rotation of the CC Sphere) - In the code below, get the 3 angles phi, theta and psi from some angle controls (convert to radians!)### Change Order of Rotation Axes ### (necessary) - Why this is useful? You can avoid gimbal lock and animate rotation around each axis (XYZ) in every position. - Choose the order of the axes you want to rotate around by editing the inAngs - Thereby phi, theta, psi are applied in that order, but to different axes - You can also use one axis two times, like XZXr - The r or the s determines if the axes are rotated with the sphere or if they are static### Rotation Around a Custom Axis ### (additional) - You can also rotate around an axis specified by a 3D-layer:   n = thisComp.layer("rotaxis").toWorldVec([0,0,1]); // Get the surface normal of the layer (interpret the little blue arrow as a vector)   n = -n; n = -n; // invert X- and Y-rotation, due to the different rotation controls of a layer and CC Sphere (if you wanted to pickquip all 3 rotation of a layer directly to a CC Sphere, you'd also have to use the negative values of xRotation and yRotation)   R2 = rotMatrixFromAxis(n, alpha); - The rotation axis is thereby standing vertically on the layer plane - You can use the zRotation of your axis layer as the rotation angle alpha (in radians!)### Do Multiple Successive Rotations ### (additional) - If you do multiple rotations, remember that the order in which they are applied matters! - To apply two different rotations (represented by the matrices R1 and R2), do this:   R_result = multMatrices(R2, R1); // The rotation to be done second is the first argument! - You could, for example, bring your sphere into position and then animate a rotation around a custom axis:   inAngs = [phi,theta,psi,EulOrdYXZr];   R = Eul_ToHMatrix(inAngs);   n = thisComp.layer("rotaxis").toWorldVec([0,0,1]);   n = -n; n = -n;   R2 = rotMatrixFromAxis(n, alpha); // You need to get alpha from somewhere, of course (radians!)   R = multMatrices(R2, R);   outAngs = Eul_FromHMatrix(R2, EulOrdXYZr);### Parenting Two Spheres ### (additional) - Say you have a master sphere (the earth) and an object moving on the earth (slave sphere). - You can now parent the rotation of the slave to the master. - So you can place your object somewhere on the earth and it follows as you rotate the earth - This is basically just doing two rotations, first the master, then the slave rotation:   inAngs = [phi,theta,psi,EulOrdXYZr]; // slave rotation   R = Eul_ToHMatrix(inAngs);   inAngs = [Mphi,Mtheta,Mpsi,EulOrdXYZr]; // master rotation (e.g. pickquip from master CC Sphere effect)   MR = Eul_ToHMatrix(inAngs);   R = multMatrices(MR, R);   outAngs = Eul_FromHMatrix(R, EulOrdXYZr);### Good Luck! ###By the way: You can apply the whole thing to layers, too! Although you can achieve almost everything with some parenting, it's useful for avoiding gimbal lock, if you don't want to mess around with additional layers or Nulls just working as parents.*/// Get angle controls (feel free to name differently) in radiansphi = effect("phi")("Angle") * Math.PI / 180;theta = effect("theta")("Angle") * Math.PI / 180;psi = effect("psi")("Angle") * Math.PI / 180;inAngs = [phi,theta,psi,EulOrdXYZr]; // Transform angles in given format to matrixR = Eul_ToHMatrix(inAngs);outAngs = Eul_FromHMatrix(R, EulOrdXYZr); // the output is always XYZr, because CC Sphere works with that format`

PS: It's beautiful, isn't it? illuminatus

Posts: 141
Joined: 02/22/2009, 11:30 am

### Re: Rotate CC Sphere around any axis

Hi Julian,
I want to a slow rotation around the fixed axis but i want to be able to rotate the sphere with normal controls for XYZ-rotation on top of that. I've tried to use your code but i think I'm missing something. I've created a null object named "rotaxis". I've added your code in XYZ rotation on CC Sphere. I didn't get any expression error but rotaxis layer is not controlling shpere object. Can you please look at my file (cs3).

https://dl.dropboxusercontent.com/u/7526694/rotaxis.aep
dellium

Posts: 12
Joined: 05/30/2011, 5:07 am

### Re: Rotate CC Sphere around any axis

Hey dellium,

to achieve 2 rotations "sort of at once" (that means combining 2 different rotational directions together) you have to compute a kind of "resulting rotation" that spits out the values you can then put into the XYZ-rotation controls via expressions.
Fortunately, this code (which is not a script, but an expression - just a very long one) can do both of that: combine 2 rotations and create a rotation based on a freely chooseable axis and a rotation angle around that axis.

First of all, I created a shy text layer that contains all the basic code, so you can "include" it at the start of the expression with
Code: Select all
`eval(thisComp.layer("code").text.sourceText.value);`

The eval() function executes the code in the string given to it. That way the actual expressions don't get so long.

Then I pickquipped the Z-rotation of the "rotaxis" layer and put this value (in radians) into the variable "angle". The code after that is just copied from one example in the code I posted above, to create a rotation matrix based on the angle "alpha" and the Z-axis arrow (the blue arrow) of the "rotaxis" Null object as rotation axis.
Code: Select all
`alpha = thisComp.layer("rotaxis").transform.zRotation / 180 * Math.PI;n = thisComp.layer("rotaxis").toWorldVec([0,0,1]);n = -n; n = -n;R = rotMatrixFromAxis(n, alpha);`

Now we just convert the rotation matrix to Euler angles again and pick the right one (X, Y or Z) from the resulting "outAngs" array.
Code: Select all
`outAngs = Eul_FromHMatrix(R, EulOrdXYZr);outAngs * 180 / Math.PI; // This is for X-rotation`

You can find this setup in the file below.

Now we get to combining 2 rotations. The alterations in the expressions are not very complicated. Add 3 angle controls to the sphere layer. You can name them phi, theta and psi (like they are named in the code below), but this is of course arbitrary. Then you copy the code below into the 3 expressions (in the CC Sphere effect) before the "outAngs" variable (so before the last two lines).
Code: Select all
`phi = effect("phi")("Angle") * Math.PI / 180;theta = effect("theta")("Angle") * Math.PI / 180;psi = effect("psi")("Angle") * Math.PI / 180;inAngs = [phi,theta,psi,EulOrdXYZs]; R2 = Eul_ToHMatrix(inAngs);R = multMatrices(R2, R);`

What it does is, it computes a rotation matrix based on the 3 angles, whereby phi, theta and psi are rotation angles for X, Y and Z respectively. The two rotation matrices are then multiplied to combine the rotations.

Two important things happening here:
- EulOrdXYZs (in the inAngs array) determines how the angles are interpreted, XYZ means in XYZ order (first rotate around the X-axis, then around Y, then around Z) and the s means that the rotation axes remain static and are not themselves effected by previous rotations. Almost any combination (like XZXr - rotation around X, then Z, then X again, which is not the same like the first X, because the X-axis itself has been rotated, because of the r) is possible.
- R = multMatrices(R2, R); means that the rotation R (the rotation based on the Null) is done first and the rotation R2 is done after that.

Now it is rather complicated to wrap your head around what is happening when you apply multiple rotations after one another - especially when the axes around which to rotate have already been moved by previous rotations. The setup described above should work for your purposes, as the rotation around the axis defined by the Null is done first, it is not affected by the second rotation. As this second rotation works with static rotation axes, it is not affected by the first rotation.
Feel free to experiment and swap the rotation order or use moveable (r) rotation axes or a different order of rotation axes by changing the two lines of code discussed above. However the most important thing is to understand what is going on with the rotations and how they incluence each other. You can just fiddle around a bit and learn that by trial and error.

Hope you can solve your problem now!
Attachments rotaxis.aep
illuminatus

Posts: 141
Joined: 02/22/2009, 11:30 am

### Re: Rotate CC Sphere around any axis

That's solved my problem. Thank you so much for your help.
dellium

Posts: 12
Joined: 05/30/2011, 5:07 am

### Re: Rotate CC Sphere around any axis

Hello!
I'm trying to make a pac-man head with sticky tooth with 2 CC spheres (head and jaw).