Skip to main content
Known Participant
March 7, 2023
Answered

SourceRectAtTime from multiple layers

  • March 7, 2023
  • 3 replies
  • 6346 views

Hello! I have a non-trivial task, the solution of which, as it seems to me, can be useful for many motion designers.

 

The question is:

How can I make an expression for a shape rectangle ("plate") that takes the size and position of not one specific text layer (which is easy to do) but the position and size of several layers (both text and shape) and, depending on their position, would create something like a bounding box around this group of layers with a small margin that can be set through the slider. My Ae version is 2023 v 23.2.1 (build 3)

 

A few limitations:

1) the expression must take for the calculation of the bounding box all the layers that are on the layers panel above the controller layer (which is with index No. 5 in the picture that I will attach below).

2) It must take for calculations new layers that will be added above the controller layer

3) It must be applied not to the size property of the rectangle, but to the path property (this means that the Bezier rectangle, not a procedural one). This limitation exists because I do some more manipulations with the background-rectangle (beveling some corners with inTangents and outTangents)

 

I understand logically (it seems to me) how to solve this problem, but the knowledge of expressions does not yet allow me to write code, and in general I am a humanist and not a techie;)

 

This is how I see the solution to the problem logically:

 

1. The expression finds a layer with an index less than the controller layer and starts the calculation from there. That is, a cycle of the form if the index < 5 (or index of a layer with name "controller" wich will be more accurate) then we start the calculations

2. The expression finds the upper bound of the bounding box by finding the layer with the smallest coordinate on the y-axis.

3. Next, the expression finds the layer with the smallest x-coordinate to find the left border of the bounding box

4. The expression takes for the upper left corner of the bounding box the point with coordinates X from point 3 and the Y from point 2

5. This is where the problems begin. in theory, we need to add SourceRectAtTime for each layer, as well as calculate the height between them and their width. Here I do not understand how to build calculations further.

....

X. the expression adds an indent from the slider to the resulting bounding box (this is easy), we can do this through the offset path

 

I did a simpler solution to a similar problem, the width of the rectangle was adjusted to all layers simply using the width and height sliders (the anchor point was in the upper left corner of the layer), or we can use two nulls for left upper and right bottom corner to manipulate rectangle, but I just want an automated solution.

 

Here is my scene screenshot and a project file:

 

This topic has been closed for replies.
Correct answer Dan Ebberts

If you zero out the postion transform for the rectangle, I think this path expression will enclose all the layers above the controller layer:

c = thisComp.layer("controller");

for (i = 1; i < c.index; i++){
  L = thisComp.layer(i);
  r = L.sourceRectAtTime(time,false);
  ul = L.toComp([r.left,r.top]);
  lr = L.toComp([r.left+r.width,r.top+r.height]);
  if (i == 1){
    left = ul[0];
    top = ul[1];
    right = lr[0];
    bottom = lr[1];
  }else{
    left = Math.min(ul[0],left);
    top = Math.min(ul[1],top);
    right = Math.max(lr[0],right);
    bottom = Math.max(lr[1],bottom);
  }
}
p = [fromComp([left,top]),fromComp([right,top]),fromComp([right,bottom]),fromComp([left,bottom])];
createPath(p,[],[],true);

3 replies

Dan Ebberts
Community Expert
Dan EbbertsCommunity ExpertCorrect answer
Community Expert
March 8, 2023

If you zero out the postion transform for the rectangle, I think this path expression will enclose all the layers above the controller layer:

c = thisComp.layer("controller");

for (i = 1; i < c.index; i++){
  L = thisComp.layer(i);
  r = L.sourceRectAtTime(time,false);
  ul = L.toComp([r.left,r.top]);
  lr = L.toComp([r.left+r.width,r.top+r.height]);
  if (i == 1){
    left = ul[0];
    top = ul[1];
    right = lr[0];
    bottom = lr[1];
  }else{
    left = Math.min(ul[0],left);
    top = Math.min(ul[1],top);
    right = Math.max(lr[0],right);
    bottom = Math.max(lr[1],bottom);
  }
}
p = [fromComp([left,top]),fromComp([right,top]),fromComp([right,bottom]),fromComp([left,bottom])];
createPath(p,[],[],true);
lukinniAuthor
Known Participant
March 8, 2023

Thank you so much Dan!
This works exactly as I wanted!
You saved me a lot of time on routine projects where I had to adjust the bounding box manually.
Now I can add this to all of my projects.

 

Dan Ebberts
Community Expert
Community Expert
March 8, 2023

I forgot to include your offset slider for a border:

c = thisComp.layer("controller");
offset = c.effect("Offset")("Slider");

for (i = 1; i < c.index; i++){
  L = thisComp.layer(i);
  r = L.sourceRectAtTime(time,false);
  ul = L.toComp([r.left,r.top]);
  lr = L.toComp([r.left+r.width,r.top+r.height]);
  if (i == 1){
    left = ul[0];
    top = ul[1];
    right = lr[0];
    bottom = lr[1];
  }else{
    left = Math.min(ul[0],left);
    top = Math.min(ul[1],top);
    right = Math.max(lr[0],right);
    bottom = Math.max(lr[1],bottom);
  }
}
left -= offset;
top -= offset;
right += offset;
bottom += offset;
p = [fromComp([left,top]),fromComp([right,top]),fromComp([right,bottom]),fromComp([left,bottom])];
createPath(p,[],[],true);
Community Expert
March 7, 2023

It's possible. I do this kind of thing all the time. There are math functions that will give you the max width of several layers. You can combine sourceRectAtTime() properties to derive the true position of text and shape layers. A shape layer's fill is calculated, but you can add in stroke width and combine the Shape/Transform and position properties to compensate for a lot of things. 

 

My approach is to make a sketch on paper and figure out what values need to be added together and how the position of each layer relates to the other layers in the group. I also simplify things that are going to be repeated for multiple layers by declaring variables like this:

//Layers in stack
L1 = thisComp.layer(index - 3);
L2 = thisComp.layer(index - 2);
L2 = thisComp.layer(index - 1);
b1 = L1.sourceRectAtTime();
b2 = L2.sourceRectAtTime();
b3 = L2.sourceRectAtTime();
w = Math.max(b1.width, b2.width, b3.width)

This much of an expression will find the maximum width of three different layers. Combine that with top and left, and you can figure out a way to calculate the actual position of all 3 layers no matter what the baseline shift or paragraph justification is. 

 

I don't have time to go through your comp right now, but it should be a fairly simple thing to calculate the total height and width of the text and shape layers, throw in some padding, tie the position of every layer to a master layer, and throw in some padding. It will require expressions on the size, transform/Position, and position of the shape layers, the position of all text layers except the one you want to use as the master (moving that text layer moves them all), and the position of all layers except the master. 

 

If I get some time this afternoon I'll take a look at your project and see what I can do to help.

lukinniAuthor
Known Participant
March 8, 2023

Thanks Rick!
Dan came up with a solution that worked perfectly

Mathias Moehl
Community Expert
Community Expert
March 7, 2023

Pins & Boxes offers a very easy solution for boxes around multiple layers.

It can create pins on corners and/or edges of any layers and then create boxes around arbitrary collections of pins. It can also deal with parented layers properly and supports animation, i.e. you can still animate the box independently of the content.

 

 

Mathias Möhl - Developer of tools like BeatEdit and Automation Blocks for Premiere Pro and After Effects
lukinniAuthor
Known Participant
March 7, 2023

Thanks for idea, Mathias!

This seems like a good solution, but it doesn't take into account the fact that I need to be able to affect the bounding box additionally (I need to be able to chamfer some corners), among other things, this script creates a lot of extra layers, making the project heavier, and besides, this is a paid solution.
But I will keep it in mind if I fail to solve the problem manually. Thank you!

Mathias Moehl
Community Expert
Community Expert
March 7, 2023

Since the boxes are normal shape layers, you can also add rounded corners easily.

If you just need rounded corners on only some of the corners, you can work with multiple boxes with different corner radius and border size (for example
- one box with rounded corners and a left border of 20px and right border of 0px
- another box without rounded corrners and left border of 0px and right border of 20px
--> now only the left corners of the merged result have visible rounded corners

 

You can get even more fancy:

Mathias Möhl - Developer of tools like BeatEdit and Automation Blocks for Premiere Pro and After Effects