• Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
    Dedicated community for Japanese speakers
  • 한국 커뮤니티
    Dedicated community for Korean speakers
Exit
0

Full auto-orient via expression

Enthusiast ,
Mar 27, 2013 Mar 27, 2013

Copy link to clipboard

Copied

Hi!

I'd like a 3D Null to get the orientation of 3D a layer. (without parenting, it would be too easy )

I've tried a lot of solutions, through orientation, rotations, both combined... some results are close, but never perfect.

If I'm right, orientation is computed before rotations...

I think the simplest way would be with auto-orientation, or the lookAt() expression on orientation, wich gives me correct X and Y orientations, and then get an expression on zRotation to get a "full" orientation.

But what would it be?

How can I get the Z orientation in layer space?

Any idea will be appreciated!

François

TOPICS
Expressions

Views

10.2K

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines

correct answers 1 Correct answer

Community Expert , Mar 28, 2013 Mar 28, 2013

Try this oreientation expression:

L = thisComp.layer("other layer");

u = fromWorldVec(L.toWorldVec([1,0,0]));

v = fromWorldVec(L.toWorldVec([0,1,0]));

w = normalize(fromWorldVec(L.toWorldVec([0,0,1])));

sinb = clamp(w[0],-1,1);

b = Math.asin(sinb);

cosb = Math.cos(b);

if (Math.abs(cosb) > .0005){

  c = -Math.atan2(v[0],u[0]);

  a = -Math.atan2(w[1],w[2]);

}else{

  a = (sinb < 0 ? -1 : 1)*Math.atan2(u[1],v[1]);

  c = 0;

}

[radiansToDegrees(a),radiansToDegrees(b),radiansToDegrees(c)]

Dan

Votes

Translate

Translate
Enthusiast ,
Mar 27, 2013 Mar 27, 2013

Copy link to clipboard

Copied

It seems I asked a bit quickly...

Here's the solution in case somebody needs it:

for orientation:

a = thisComp.layer("master").toWorld([0,0,0]);

b = thisComp.layer("master").toWorld([0,0,100]);

lookAt(a,b)

then for zRotation:

a = fromWorld(thisComp.layer("master").toWorld([0,0,0]));

b = fromWorld(thisComp.layer("master").toWorld([100,0,0]));

c = a-b;

radiansToDegrees(Math.atan2(c[1],c[0]))+180;

Hope it can help!

François

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Enthusiast ,
Mar 28, 2013 Mar 28, 2013

Copy link to clipboard

Copied

Aaaaargh !

Nope, it's not perfect, even if it's close to the result...

Does anyone have a solution?

Dan?

Thanx.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Mar 28, 2013 Mar 28, 2013

Copy link to clipboard

Copied

Try this oreientation expression:

L = thisComp.layer("other layer");

u = fromWorldVec(L.toWorldVec([1,0,0]));

v = fromWorldVec(L.toWorldVec([0,1,0]));

w = normalize(fromWorldVec(L.toWorldVec([0,0,1])));

sinb = clamp(w[0],-1,1);

b = Math.asin(sinb);

cosb = Math.cos(b);

if (Math.abs(cosb) > .0005){

  c = -Math.atan2(v[0],u[0]);

  a = -Math.atan2(w[1],w[2]);

}else{

  a = (sinb < 0 ? -1 : 1)*Math.atan2(u[1],v[1]);

  c = 0;

}

[radiansToDegrees(a),radiansToDegrees(b),radiansToDegrees(c)]

Dan

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Enthusiast ,
Mar 28, 2013 Mar 28, 2013

Copy link to clipboard

Copied

Hi Dan!

Once again, you saved someone's day...

Thanx a lot!

François

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Enthusiast ,
Mar 29, 2013 Mar 29, 2013

Copy link to clipboard

Copied

Hey Dan,

the expression works perfectly, as long as the "Master" isn't stretched.

But when it's stretched, I get a slight difference between "Master" and "Follower".

The difference becomes huge when one of "Master"'s dimension is close to 0...

It's strange that there's no function similar to "lookAt()", but giving back the 3 dimensions.

Do you think there is a way to get the exact orientation?

Thanx a lot!

François

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Mar 29, 2013 Mar 29, 2013

Copy link to clipboard

Copied

I can't seem to replicate that behavior, but it might help to normalize all the vectors (not sure why I didn't do that in the first place):

L = thisComp.layer("other layer");

u = normalize(fromWorldVec(L.toWorldVec([1,0,0])));

v = normalize(fromWorldVec(L.toWorldVec([0,1,0])));

w = normalize(fromWorldVec(L.toWorldVec([0,0,1])));

sinb = clamp(w[0],-1,1);

b = Math.asin(sinb);

cosb = Math.cos(b);

if (Math.abs(cosb) > .0005){

  c = -Math.atan2(v[0],u[0]);

  a = -Math.atan2(w[1],w[2]);

}else{

  a = (sinb < 0 ? -1 : 1)*Math.atan2(u[1],v[1]);

  c = 0;

}

[radiansToDegrees(a),radiansToDegrees(b),radiansToDegrees(c)]

Dan

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Enthusiast ,
Mar 31, 2013 Mar 31, 2013

Copy link to clipboard

Copied

Thanx Dan,

From what I see, the result of the expression isn't "linear".

If I rotate my "Master", the "Follower" will turn a bit less untill it reaches 90°, then turn more untill 180°, and so on...

Any idea?

Cheers,

François

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Apr 01, 2013 Apr 01, 2013

Copy link to clipboard

Copied

I'm not seeing that behavior, and I'm sorry to say that I don't understand why you are.

Dan

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Enthusiast ,
Apr 01, 2013 Apr 01, 2013

Copy link to clipboard

Copied

Do you mind if I send you an .aep project?

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Apr 01, 2013 Apr 01, 2013

Copy link to clipboard

Copied

That would be fine.

Dan

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Enthusiast ,
Apr 01, 2013 Apr 01, 2013

Copy link to clipboard

Copied

Here it is!

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Apr 01, 2013 Apr 01, 2013

Copy link to clipboard

Copied

You may have to post it somewhere--it doesn't look like it made it through.

Dan

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Enthusiast ,
Apr 01, 2013 Apr 01, 2013

Copy link to clipboard

Copied

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Apr 01, 2013 Apr 01, 2013

Copy link to clipboard

Copied

So are you talking about when you turn the Master's Scale expression on? As I recall, the orientation expression is derived from a simplified 3D rotation transform the assumes uniform scale. I think the correction I put in for non-uniform scaling is probably not correct/adequate and the original derivation of the expression needs to be revisited with non-uniform scaling in mind. I've got my original notes somewhere, but it could be a chunk of work and I'm not sure I have the time to dive into it.

Dan

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Enthusiast ,
Apr 02, 2013 Apr 02, 2013

Copy link to clipboard

Copied

You've done a lot for me already!

Now I have something working perfectly for uniform scale, and you told me where the issue comes from, I don't think I can complain

Thank you so much!

François

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
May 08, 2013 May 08, 2013

Copy link to clipboard

Copied

Hi François and Dan,

    If I may jump in, I wrote to Dan about this last year and I used the following function to setup the rotation calculation in the expressions, based on Dan's first version on a Creative Cow forum:

function getOrientation(u, v, w)

{

u = normalize(u);

v = normalize(v);

w = normalize(w);

sinb = w[0];

b = Math.asin(sinb);

if (Math.abs(Math.cos(b)) > .0005)

{

c = -Math.atan2(v[0],u[0]);

a = -Math.atan2(w[1],w[2]);

}

else

{

a = ( (sinb < 0) ? -1 : 1 )*Math.atan2(u[1],v[1]);

c = 0;

}

return [radiansToDegrees(a), radiansToDegrees(b), radiansToDegrees(c)];

}

Then, calling it this way worked even with non-uniform scales as far as I can tell:

u = layer.toWorldVec([1,0,0]);

v = layer.toWorldVec([0,1,0]);

w = layer.toWorldVec([0,0,1]);

getOrientation(u, v, w);

I haven't checked these expressions for a while now, but you can see them in action in Elementary by using the Copy button: it creates a copy of the layer that is parented via expressions and I think I remember it worked properly, I can check if you want.

Kevin

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Enthusiast ,
May 08, 2013 May 08, 2013

Copy link to clipboard

Copied

Hi Kevin!

Thanx you for sharing this expression, it works great!!

I'm impressed

Thanx,

François

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
May 08, 2013 May 08, 2013

Copy link to clipboard

Copied

    Well Dan did most of the work, I only changed a couple things.

    Just to make sure, did this work fine with non-uniform scales? I remember I tested it last year but you know, I may have missed a situation where it didn't work and unfortunately I can't use AE right now.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Enthusiast ,
May 08, 2013 May 08, 2013

Copy link to clipboard

Copied

Yes, it works perfectly with non-uniform scales.

The only exception is when you have X (or Y) negative, with Y (or X) positive. Then you have to make Z negative, and everything works fine.

Of course, scale values have to be non-null, but this is the case as soon as you compute something...

Thanx to you and Dan!

François

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
May 08, 2013 May 08, 2013

Copy link to clipboard

Copied

Ah, I don't think I tried negative scales.

It makes sense ... a negative scale on only one or two axes is going to invert the XYZ basis. I don't think that's a problem with the getRotation function though, you should leave it like that. And I don't think it's enough to invert Z if X and Y have oposite scales: you'll have the same problem with a different combinations of positive/negative scales, no?

We'd just need to make sure that the vectors u, v and w follow the rules u ^ v = w and v ^ w = u. The problem for me right now is: how can we detect a negative scale? I usually calculate the scale along e.g. X like this:

 

1/length(toWorldVec([1, 0, 0]));

... because this way it takes the scale of the parents into account, but the sign of the scale will disappear because of the "length" function.

If we can find the sign of the scale along each axis we'll just have to do:

  

u = layer.toWorldVec([1,0,0])*signX;

v = layer.toWorldVec([0,1,0])*signY;

w = layer.toWorldVec([0,0,1])*signZ;

getOrientation(u, v, w);

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Enthusiast ,
May 09, 2013 May 09, 2013

Copy link to clipboard

Copied

Well, I don't know any way to get scale's sign in World space (I don't think it's possible..)

But a loop can give us the sign of the parent(s) , and so of our layer :

if (layer.parent) {getParent's scale signs}

if (layer.parent.parent) {getParent's parent's scale signs}and so on...

And yes, it works with just turning Z negative.

In fact, the product of signs have to be positive.

So: Z *= sign(X*Y); and X*Y*Z will always be positive.

I think it's easier and more reliable than trying to get something close to a scale's sign in World's space, cos it would mean calculating from camera view...

Untill someone smart finds something else 😉

Cheers,

François

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
May 09, 2013 May 09, 2013

Copy link to clipboard

Copied

I agree with the loop, I can't find any other way.

But I still don't think what you're doing will work: what if the scale is [100, 100, -100] for example? It's still going to use a wrong XYZ basis for the calculation and your workaround for the X/Y scales won't do anything. I may be wrong though, tell me if I'm completely mistaken.

What I'll try is "prévenir plutôt que guérir" and recalculate the scale using a while loop, then multiply the u, v and w vectors I send to the getRotation functions by the computed scales' signs. This way 1) the parented object will have the same scale as it's parent and 2) the getRotation function should get a proper XYZ basis.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
May 09, 2013 May 09, 2013

Copy link to clipboard

Copied

So here's what I tried:

- scale expression

{

  var layer = thisComp.layer(  /* name of the parent layer */  );

  var scl = [1, 1, 1];

  while (layer != null)

  {

    for (var j=0; j<3; j++)

    {

      scl *= layer.scale/100;

    }

    try

    {

      layer = layer.parent;

    }

    catch (err)

    {

      layer = null;

    }

  }

  scl*100;

}

- orientation expression

{

  function sign(x)

  {

    return x>=0  ?  1 : -1;

  }

  function getOrientation(u, v, w)

  {

      // still the same thing

  }

  layer = thisComp.layer(  /* name of the parent layer */  );

  u = layer.toWorldVec([1,0,0])*sign(scale[0]);

  v = layer.toWorldVec([0,1,0])*sign(scale[1]);

  w = layer.toWorldVec([0,0,1])*sign(scale[2]);

  getOrientation(u, v, w);

}

I haven't tested it that much but the idea seems fine to me. In the scale expression note that I start the loop from the parent layer because in my case I want my parented layer to have the exact same scale.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Enthusiast ,
May 09, 2013 May 09, 2013

Copy link to clipboard

Copied

I was about to share mine, you won!

I didn't write it the same way, but got the same result...

We're coming back to the problem we had with Dan's expression.

The vector is computed from the distorted layer, and I don't think we can really compensate (your solution is quite close though!)

In my case, it's not a big deal as I go through Nulls for what I need, and I can use their Z scale as I wish. So it's really easy to make it work...

Cheers,

François

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines