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

Expressions: Returning the width Value of a Layer

Engaged ,
Dec 15, 2022 Dec 15, 2022

Copy link to clipboard

Copied

Hi Everybody,

 

I found this script by Dan Ebberts in another community thread:

 

layer = app.project.activeItem.layer(1);

rect = layer.sourceRectAtTime(0,true);

alert(rect.width);

 

So that's straight forward enough...under which transform of my precomp do I place this expression? 

 

I'm trying to create a set of layer comps with different widths evenly spaced in a row, so I need to first figure out how to getWidth(); essentially.  

 

TOPICS
Expressions , How to

Views

1.3K

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 2 Correct answers

Community Expert , Dec 15, 2022 Dec 15, 2022

Maybe a simple example would help. Assume you have a bunch of layers (could be of different sizes) that aren't scaled (scale = 100%) and the anchor points are at the center of the layers. You also have a layer named "Controls" that has a slider control named "Gap" that is set to the horizontal gap you want between your layers/precomps. If you apply this position expression to each of the layers/precomps they will arrange themselves into a row, starting in the upper left corner of the comp and mo

...

Votes

Translate

Translate
Community Expert , Dec 17, 2022 Dec 17, 2022

This turned out to be quite the adventure, but I did learn some stuff. To keep things from getting completely out of control, I did have to make a few assumptions. One is that the anchor points are centered. For layers where it isn't centered , you can just apply this anchor point expression (especially handy for text layers):

r = sourceRectAtTime(time,false);
[r.left + r.width/2, r.top + r.height/2]

I also assumed that the layers are all the same height. If they're not (or if you want to specif

...

Votes

Translate

Translate
LEGEND ,
Dec 15, 2022 Dec 15, 2022

Copy link to clipboard

Copied

This is not an expression, it's a script snippet. If you just want to plot the width you apply something like this to the source text property of a text layer:

 

thisComp.layer("XYZ").sourceRectAtTime()[0]

 

Mylenium 

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
Engaged ,
Dec 15, 2022 Dec 15, 2022

Copy link to clipboard

Copied

Nevermind, I found a tutorial that covers sourceRectAtTime(). Thanks!

 

https://www.youtube.com/watch?v=CVliDoNgoCg

I created the most comprehensive intro to After Effects, ever. Join Launch Into After Effects today: https://www.jakeinmotion.com/launch-into-after-effects sourceRectAtTime() is a crazy useful expression, but there isn't a great explanation of what it exactly does, or how to wrangle it to do what

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
Engaged ,
Dec 15, 2022 Dec 15, 2022

Copy link to clipboard

Copied

Actually none of this has actually answered my question.

 

My question is where to I put this snippet, code, etc. for precomps?

 

There's no variables for precomps that denote width.

 

There's just anchor position, scale (%, not size), rotation, and opacity.

 

Do I have the wrong idea just putting this basic script?

 

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 ,
Dec 15, 2022 Dec 15, 2022

Copy link to clipboard

Copied

Precomps do have a width attribute:

So using something like this will give you the width:

thisComp.layer("Precomp 1").width

or, if the precomp layer has been scaled, something like this:

L = thisComp.layer("Precomp 1");
L.width * L.scale[0]/100

 

 

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
Engaged ,
Dec 15, 2022 Dec 15, 2022

Copy link to clipboard

Copied

LOL, excellent, thank you. Again where do I put this script? Like, which stop watch do I alt-click to get the little emergency triangle to pop up and paste the script?

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 ,
Dec 15, 2022 Dec 15, 2022

Copy link to clipboard

Copied

That depends on what you're trying to control with it. If you're calculating where to position that precomp layer, it would be part of the position expression for that precomp layer. If it's to position another precomp layer that needs to account for the positioning of other layers, it would be part of the expression for that layer. In any case, I'd guess it will end up being part of a much more complex position expression.

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
Engaged ,
Dec 15, 2022 Dec 15, 2022

Copy link to clipboard

Copied

I just want to know where to put the sourceRectAtTime code. The guy's exampe I posted above uses text layers. I am not. 

 

I'm tying to determine the width of an image in a precomp. I'm not using a Shape Layer so I don't have a SIZE attribute to hit the stop watch. Where do I put that code for precomps?

 

Can anybody please tell me? I've been trying to trace/alert the size of a precomp all day long.

 

 

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
Engaged ,
Dec 15, 2022 Dec 15, 2022

Copy link to clipboard

Copied

Okay, I think I found the correct answer. You can't. To use sourceRectAtTime(); It has to be a shape layer or text layer. 

 

It can't be a precomp. I've looked at over a dozen tutorials and they all pertain to text layers, NOT precomps or images. 

 

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 ,
Dec 15, 2022 Dec 15, 2022

Copy link to clipboard

Copied

sourceRectAtTime() won't give you the non-zero alpha extents of an image or precomp layer, but it will give you the size, which wouldn't be what you need if the precomp size doesn't match that of the image inside it. But if it does match, the precomp's width and height attributes would work just as well.

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 ,
Dec 15, 2022 Dec 15, 2022

Copy link to clipboard

Copied

Maybe a simple example would help. Assume you have a bunch of layers (could be of different sizes) that aren't scaled (scale = 100%) and the anchor points are at the center of the layers. You also have a layer named "Controls" that has a slider control named "Gap" that is set to the horizontal gap you want between your layers/precomps. If you apply this position expression to each of the layers/precomps they will arrange themselves into a row, starting in the upper left corner of the comp and moving right, with a gap between layers. Clearly, to create a wall, you need something more complex that this--you need logic for how the expression decides it's the end of a row and it needs to start a new row, for example. Also, if the layers/precomps are scaled, it needs to account for that. But this should give you the idea of what's possible and how you might go about it.

gap = thisComp.layer("Controls").effect("Gap")("Slider");
upperLeft = [0,0]; // start in upper left corner of comp
for (i = 1; i < index; i++){
  L = thisComp.layer(i);
  if (L.name == "Controls") continue; // skip the controls layer
  upperLeft += L.width + gap;
}
upperLeft + [width,height]/2

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
Engaged ,
Dec 15, 2022 Dec 15, 2022

Copy link to clipboard

Copied

Dan, this is awesome. Very local reference nothing outside of "layer" and with a couple comments. That's a huge help. I should be able to chew on this for a while to test things out and get these rusty brains of mine running. Thanks so much.

 

One more question if I might, Myllenium posted a script in the other post I made:

 

https://community.adobe.com/t5/after-effects-discussions/spacing-an-array-of-precomps/td-p/13421473

 

What does ...

 

if (index != 1)
{
xRef=thisComp.layer(index-1);

 

...do? In terms of conditional logic? What does this index !=1 and/or index-1 mean or looking to equate? What is it checking for?

 

Thanks again!

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 ,
Dec 15, 2022 Dec 15, 2022

Copy link to clipboard

Copied

if(index != 1) basically says "unless this layer is layer #1 in the layer stack, do this... "

thisComp.layer(index-1) says "the layer in the layer stack above this one..."

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
Engaged ,
Dec 15, 2022 Dec 15, 2022

Copy link to clipboard

Copied

Excellent, thanks so much 🙂

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
Engaged ,
Dec 16, 2022 Dec 16, 2022

Copy link to clipboard

Copied

Hi Mr. Ebberts, I just want to thank you once again for the code sample you gave me above. It really saved the week. I could tried my best and not come up with a solution that set varying objects in an array that were easliy updateable. I'm not entirely sure why there's a loop in the layer. In a OOP setup, that loop would be placed in the root and refer to the set of objects beneath, not placed in the object layer and repeated for every iteration. That I will have to figure out.

 

Anyway, super stoked. Thank you so much for the push. Really made a difference. 

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 ,
Dec 17, 2022 Dec 17, 2022

Copy link to clipboard

Copied

This thread and this other thread made me think about and come up with an expression that would automatically place the first layer in a comp in the upper left corner of the comp, then stack each layer to the right with some padding. 

 

This is my expression:

xPad=20;
yPad = 20;
s = scale * .01;
i = index;
tLyr = sourceRectAtTime();
w = s[0] * tLyr.width/2;
h = s[1] * tLyr.height/2;

if (i == 1){
	[w + xPad/2, h + yPad/2];
}
else{
	lW = s[0] * tLyr.width/2;
	lH = tLyr.height/2;
	ref = thisComp.layer(i - 1);
	rScl = ref.scale * .01;
	refBox = ref.sourceRectAtTime();
	refW = rScl[0] * refBox.width / 2;
	refH = refBox.height / 2;
	x = refW + xPad;
	y = refH + yPad;
	ref.position + [x + lW, 0];
}

The comp uses sourceRectAtTime() instead of just width. I also added in scale compensation. My goal is to make it work with shape layers, text layers, footage or solid layers, and even nested comps.

 

So far, the expression works perfectly. The next thing I would like to do is look at the position of the first layer that moves outside the composition frame to the right and shift that layer and all layers that follow below the top row created by the if/else section of my expression. When the next row is filled, I'd like the layers to drop down once again.

 

I'm kind of lost. Adding an if (current layer x position is greater than comp width) (move down below the first row}. If anyone knows how to write code that creates multiple rows, it is Dan Ebberts. I would appreciate any pointers. I'd appreciate the feedback. When I get a little more time to fiddle with this, I'll start a new thread specifically dealing with automatically creating multiple layers of rows. 

 

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 ,
Dec 17, 2022 Dec 17, 2022

Copy link to clipboard

Copied

When putting something like this together, you have to first define all your assumptions. Are you assuming that all layers have their anchor points centered? Are all the layers the same height? If not, is a row's height determined by its tallest layer? Or do you have a scale expression that makes them all the same height? I think, for the OP's video wall, you could assume constant height, but not width. Also, is the last layer in a row the last one that fits fully within the comp, or the last one that's at least partly visible in the comp?

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 ,
Dec 17, 2022 Dec 17, 2022

Copy link to clipboard

Copied

I am assuming the anchor points are all in the center. Because the Lef and Top values are always zero with layers, they would have no influence on layer position, but they could be used to compensate for paragraph justification and baseline shift and could also fix shape layer transforms. 

 

The first step is to create a left justified grid with rows no longer than the comp width and all layers the same height. If I change the expression for the second row, I can achieve this by modifying the Y in the else expression value to look at the height of a layer in the first row. I'd love to know how to automate that.

 

Carried to the extreme, the expression, or the script, would create a grid of different-sized layers with the same padding around each layer. For the graphic, I am visualizing, creating a grid of layers with the same height and consistent vertical padding would be sufficient.

 

If all the layers were 3D, you could set up a camera to move through the grid and pick out different layers to focus on. A couple of years ago, I created a similar graphic in Illustrator by hand. In AE, the final comp would look something like this. It's kind of a crude demo. 

RickGerard_0-1671317303905.png

 

 

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 ,
Dec 17, 2022 Dec 17, 2022

Copy link to clipboard

Copied

This turned out to be quite the adventure, but I did learn some stuff. To keep things from getting completely out of control, I did have to make a few assumptions. One is that the anchor points are centered. For layers where it isn't centered , you can just apply this anchor point expression (especially handy for text layers):

r = sourceRectAtTime(time,false);
[r.left + r.width/2, r.top + r.height/2]

I also assumed that the layers are all the same height. If they're not (or if you want to specify a different height), you have to define the height with a slider named "Height" on a layer named "Controls" and they need this scale expression:

h = thisComp.layer("Controls").effect("Height")("Slider");
r = sourceRectAtTime(time,false);
s = (h/r.height)*100;
[s,s]

I also assumed that the horizontal and vertical padding are defined by sliders named "H Gap" and "V Gap" on the "Controls" layer. Note that the Controls layer can be anywhere in the layer stack. All the other layers (those participating in the grid) need this position expression:

hGap = thisComp.layer("Controls").effect("H Gap")("Slider");
vGap = thisComp.layer("Controls").effect("V Gap")("Slider");

upperLeft = [hGap, vGap]; // start in upper left corner of comp
for (i = 1; i < index; i++){
  L = thisComp.layer(i);
  if (L.name == "Controls") continue; // skip the controls layer
  r = L.sourceRectAtTime(time,false);
  if (upperLeft[0] + hGap + r.width*L.scale[0]/100 > thisComp.width){
    upperLeft = [hGap, upperLeft[1] + vGap + r.height*L.scale[1]/100];
  }
  upperLeft += [r.width*L.scale[0]/100 + hGap, 0];
}
r = sourceRectAtTime(time,false);
if (upperLeft[0] + hGap + r.width*scale[0]/100 > thisComp.width){
  upperLeft = [hGap, upperLeft[1] + vGap + r.height*scale[1]/100];
}
upperLeft + [r.width*scale[0],r.height*scale[1]]/200

Realistically, to be able to handle layers of different heights, I think you'd to do it with a script, because it would be a lot of processing to do it with expressions. Anyway, I tested this with text, shapes, solids, and precomps and it seems work well.

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 ,
Dec 18, 2022 Dec 18, 2022

Copy link to clipboard

Copied

That's truly amazing Dan Ebberts. Thank you. 

 

I have one other question for you. Is it possible to write an expression that says something like this: 

if (thisComp.layer("Controls") == does not exist){
     hGap = 20;
     vGap = 20;
}
else{
     hGap = thisComp.layer("Controls").effect("H Gap")("Slider");
     vGap = thisComp.layer("Controls").effect("V Gap")("Slider");
}

I have tried == null and isPresent(), but it looks like there is no way for AE to do something else if a layer or effects control is missing.

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 ,
Dec 18, 2022 Dec 18, 2022

Copy link to clipboard

Copied

I think this would work (not tested):

try{
     hGap = thisComp.layer("Controls").effect("H Gap")("Slider");
     vGap = thisComp.layer("Controls").effect("V Gap")("Slider");
}catch (e){
     hGap = 20;
     vGap = 20;
}

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 ,
Dec 18, 2022 Dec 18, 2022

Copy link to clipboard

Copied

Perfect. I am going to drop that code into about a dozen of my Animation presets

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 ,
Dec 18, 2022 Dec 18, 2022

Copy link to clipboard

Copied

I found a couple of bugs in the position expression. This one is better:

hGap = thisComp.layer("Controls").effect("H Gap")("Slider");
vGap = thisComp.layer("Controls").effect("V Gap")("Slider");

upperLeft = [hGap, vGap]; // start in upper left corner of comp
for (i = 1; i < index; i++){
  L = thisComp.layer(i);
  if (L.name == "Controls") continue; // skip the controls layer
  r = L.sourceRectAtTime(time,false);
  if (upperLeft[0] + r.width*L.scale[0]/100 > thisComp.width){
    upperLeft = [hGap, upperLeft[1] + vGap + r.height*L.scale[1]/100];
  }
  upperLeft += [r.width*L.scale[0]/100 + hGap, 0];
}
r = sourceRectAtTime(time,false);
if (upperLeft[0] + r.width*scale[0]/100 > thisComp.width){
  upperLeft = [hGap, upperLeft[1] + vGap + r.height*scale[1]/100];
}
upperLeft + [r.width*scale[0],r.height*scale[1]]/200

 

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 ,
Dec 18, 2022 Dec 18, 2022

Copy link to clipboard

Copied

Aarg. Still not quite right. The previous version doesn't make sure there is enough padding at the right edge of the comp, so I think this is correct (which is essentially the same as the original version):

hGap = thisComp.layer("Controls").effect("H Gap")("Slider");
vGap = thisComp.layer("Controls").effect("V Gap")("Slider");

upperLeft = [hGap, vGap]; // start in upper left corner of comp
for (i = 1; i < index; i++){
  L = thisComp.layer(i);
  if (L.name == "Controls") continue; // skip the controls layer
  r = L.sourceRectAtTime(time,false);
  if (upperLeft[0] + r.width*L.scale[0]/100 > thisComp.width - hGap){
    upperLeft = [hGap, upperLeft[1] + vGap + r.height*L.scale[1]/100];
  }
  upperLeft += [r.width*L.scale[0]/100 + hGap, 0];
}
r = sourceRectAtTime(time,false);
if (upperLeft[0] + r.width*scale[0]/100 > thisComp.width - hGap){
  upperLeft = [hGap, upperLeft[1] + vGap + r.height*scale[1]/100];
}
upperLeft + [r.width*scale[0],r.height*scale[1]]/200

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 ,
Dec 18, 2022 Dec 18, 2022

Copy link to clipboard

Copied

LATEST

Dan Ebberts, thank you so much. I appreciate all of the work you put in. I am also happy to see that I am not the only one who fiddles with expressions through four or five interactions to get them to work properly. I am so grateful for your help. 

 

Here's my final code.

try{
     hGap = thisComp.layer("Controls").effect("X Pad")("Slider");
     vGap = thisComp.layer("Controls").effect("Y Pad")("Slider");
}catch (e){
     hGap = 20;
     vGap = 20;
}

upperLeft = [hGap, vGap]; // start in upper left corner of comp
for (i = 1; i < index; i++){
  L = thisComp.layer(i);
  if (L.name == "Controls") continue; // skip the controls layer
  r = L.sourceRectAtTime(time,false);
  if (upperLeft[0] + r.width*L.scale[0]/100 > thisComp.width - hGap){
    upperLeft = [hGap, upperLeft[1] + vGap + r.height*L.scale[1]/100];
  }
  upperLeft += [r.width*L.scale[0]/100 + hGap, 0];
}
r = sourceRectAtTime(time,false);
if (upperLeft[0] + r.width*scale[0]/100 > thisComp.width - hGap){
  upperLeft = [hGap, upperLeft[1] + vGap + r.height*scale[1]/100];
}
upperLeft + [r.width*scale[0],r.height*scale[1]]/200

It does an amazing job of lining up layers. 

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