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

Organizing items in an Array using expressions

Community Expert ,
Feb 21, 2021 Feb 21, 2021

I've been trying several approaches to this problem. I have a bunch of rectangles on a shape layer. Each one has a different width. I want to stack them from top to bottom by adjusting their Rectangle/Transform/Anchor Point Y value based on their width. 

 

I've set up variables for the width of each rectangle and I've tried several approaches to calculating the size of each layer. One has been to calculate the percentage of the width of the current layer against the width of the total of all rectangles. I'm using sliders to control the width of the rectangles and have added another slider that adds up the values from all of the other sliders. That expression looks like this:

 

 

 

ctrl = thisComp.layer("Controller Null");
ttl = ctrl.effect("Total Slider")("Slider");
yel = (ctrl.effect(1)("Slider") / ttl);
blu = (ctrl.effect(2)("Slider") / ttl);
red = (ctrl.effect(3)("Slider") / ttl);
grn = (ctrl.effect(4)("Slider") / ttl);

rank = blu;

 

 

 

This will return a decimal value from 0 to 1 for the rank variable that accurately shows the percentage of the blu rectangle to the total.

 

Where I'm getting stuck is turning that value into a ranking from one to 4. I've tried a bunch of sort options from Javascript websites, and I've tried a bunch of if/else arguments but I can't figure out how to return 2 for blu if blu is 100, grn is 20, red is 150, and yel is 200. If I fiddle around enough to get that to work everything falls apart if red is 75. blu either changes to 3 or stays 2 until I increase blu to something close to 100.

 

 

EDIT:

I kept fiddling but still no joy. The if/else route just isn't working and it could get very long. 

 

x = - content("Green Bar").content("Rectangle Path 1").size[0] / 2;
grnBar = content("Green Bar").content("Rectangle Path 1").size[0];
redBar = content("Red Bar").content("Rectangle Path 1").size[0];
bluBar = content("Blue Bar").content("Rectangle Path 1").size[0];
yelBar = content("Yellow Bar").content("Rectangle Path 1").size[0];
totalLen = grnBar + redBar + bluBar + yelBar;

v1 = grnBar / totalLen; // returns percentage of total width
v2 = redBar / totalLen;
v3 = bluBar / totalLen;
v4 = yelBar / totalLen;


/*
compare the values of all 4 v# values and return the
position of this layer as an integer from 1 to 4
*/

if (v1 < v2 && v1 < v3 && v1 < v4){
	rank = 1;
}
else if (v1 > v2 && v1 < v3 && v1 < v4){
	rank = 2;
}
// 
[x, rank]; 

 

There must be some kind of a sort operator that will work in AE that could return an integer corresponding to the relative size of the rectangles. It's going to take more of an expert than I am. Maybe Dan Ebberts or Mathias Moehl would have an idea.

 

This one has me stumped.

TOPICS
Expressions
1.7K
Translate
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

Valorous Hero , Feb 22, 2021 Feb 22, 2021

How's this, Rick. I placed this into the Shape Layer's Transform>Position prop.
I essentially used the sort and indexOf Methods

// declare and get my width
myWidth = thisLayer.sourceRectAtTime().width;

 

// declare sourceRectAtTime for layersOfInterest - use for getting width of layersOfInterest
a=thisComp.layer("Red").sourceRectAtTime();
b=thisComp.layer("Green").sourceRectAtTime();
c=thisComp.layer("Blue").sourceRectAtTime();
d=thisComp.layer("Yellow").sourceRectAtTime();

 

// declare array made up

...
Translate
Community Expert , Feb 22, 2021 Feb 22, 2021

Thanks, Roland Kahlenberg.  I knew it was something about sorting an array that I was not catching. It was the sorted = stack.sort(function(a, b){return b-a}); line I couldn't figure out.

 

I'm using a single shape layer and driving the width of each rectangle with JSON data so my final expression looks like this for the Green Bar:

x = - content("Green Bar").content("Rectangle Path 1").size[0]/2;
yOfst = - content("Green Bar").content("Rectangle Path 1").size[1] * 1.25;

a= content("Green Bar").
...
Translate
LEGEND ,
Feb 21, 2021 Feb 21, 2021

You could start by nesting your conditionals. That usually works better, as secondary conditions won't even be considered if the primary one doesn't fit.

 

Mylenium

Translate
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 ,
Feb 22, 2021 Feb 22, 2021

Hey Rick, I believe this thread has relation to the other one in regards to the similar thing, where you stated as:
It's a fairly easy task to control the size and the position of a shape layer rectangle by driving each value with a simple expression tied to a slider. 

As you can see, its not that much simple, nor its maintainable that way, considering more data rows to be compared, thus just wanted to point that out. And this is not even animated in time !

What you could try on doing though:
1. Build an array allValues of all values [v1,v2,v3,v4] 
2. Build an array of yPos, where rankings would appear in height [y1,y2,y3,y4]
3. Build an array of layers indexes layerInd [green, red, blue, yellow]
4. Sort allValues, and re-sort layerInd array in the same indexes order as allValues has changed. 
5. Based on layer name, pick its index from resorted layerInd array and then grab its position from yPos

In other words, additional array must be created, to find out indexes differences - at least I see it somewhat this way around. 

Translate
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
Valorous Hero ,
Feb 22, 2021 Feb 22, 2021

How's this, Rick. I placed this into the Shape Layer's Transform>Position prop.
I essentially used the sort and indexOf Methods

// declare and get my width
myWidth = thisLayer.sourceRectAtTime().width;

 

// declare sourceRectAtTime for layersOfInterest - use for getting width of layersOfInterest
a=thisComp.layer("Red").sourceRectAtTime();
b=thisComp.layer("Green").sourceRectAtTime();
c=thisComp.layer("Blue").sourceRectAtTime();
d=thisComp.layer("Yellow").sourceRectAtTime();

 

// declare array made up of layersOfInterest and their width; size
myArray = [a.width,b.width,c.width,d.width];

 

// sort array in descending (large to small)order
mySortedArray = myArray.sort(function(a, b){return b-a});

 

// get my index in the sortedArray
myRank = mySortedArray.indexOf(myWidth);

 

// declare positionArray to be used to autoMagically position layersOfInterest
posArray=[[100,100],[100,200],[100,300],[100,400]];

 

// autoPosition this layer's position based on my rank in sortedArray and based on values in positionArray
posArray[myRank]

 

 


Here's a URL for the indexOf method - https://www.w3schools.com/jsref/jsref_indexof_array.asp
and this URL is for the sort method/Function - https://www.w3schools.com/jsref/jsref_sort.asp

Very Advanced After Effects Training | Adaptive & Responsive Toolkits | Intelligent Design Assets (IDAs) | MoGraph Design System DEV
Translate
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 ,
Feb 22, 2021 Feb 22, 2021

Thanks, Roland Kahlenberg.  I knew it was something about sorting an array that I was not catching. It was the sorted = stack.sort(function(a, b){return b-a}); line I couldn't figure out.

 

I'm using a single shape layer and driving the width of each rectangle with JSON data so my final expression looks like this for the Green Bar:

x = - content("Green Bar").content("Rectangle Path 1").size[0]/2;
yOfst = - content("Green Bar").content("Rectangle Path 1").size[1] * 1.25;

a= content("Green Bar").content("Rectangle Path 1").size[0];
b= content("Red Bar").content("Rectangle Path 1").size[0];
c= content("Blue Bar").content("Rectangle Path 1").size[0];
d= content("Yellow Bar").content("Rectangle Path 1").size[0];
 
stack = [a, b ,c ,d];
sorted = stack.sort(function(a, b){return b-a});
rank = sorted.indexOf(a);

[x, rank * yOfst]

The last little bit of housekeeping is to clean up the expression so content names don't matter and to throw in a position animation so the rectangles don't jump into place but ease into place. I think I will use a value at time expression to drive that. The end goal is to create an animation preset that I can apply to any shape layer with a rectangle, then duplicate the rectangle as many times as I like and end up with an animated bar graph with the highest values on top.

Translate
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
Valorous Hero ,
Feb 22, 2021 Feb 22, 2021

You're welcome, Rick. Auto-animating will be challenge. My thoughts are to use a function which dictates how often data updates and how long each bar animates before going through the cycle of updating data + animating the bar positions.

Very Advanced After Effects Training | Adaptive & Responsive Toolkits | Intelligent Design Assets (IDAs) | MoGraph Design System DEV
Translate
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
Valorous Hero ,
Feb 22, 2021 Feb 22, 2021

Another option to sorting is to start off in a Spreasheet and use its sorting feature prior to exporting a CSV or TSV dataset. BTW, Tomas knows how to build "Racing Bar Charts" - had a quick look at one of his vids on youtube a while ago.

Very Advanced After Effects Training | Adaptive & Responsive Toolkits | Intelligent Design Assets (IDAs) | MoGraph Design System DEV
Translate
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
New Here ,
Apr 05, 2023 Apr 05, 2023
LATEST

Hello, Rick!

I know this post was from a long time ago, but I'm just wondering if you already figured out a way for the position of the shape to change slowly instead of instantly. Thanks

Translate
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