Answers.Body: <p>It's trickier than it seems if you want to do the whole thing from scratch.</p>
  
  <pre><code>//first make some shortcuts
  //choose the property to drive it - could be any property on any layer
  myproperty=transform.position
  //choose the keys that define the movement - in AE key indexes start at 1, not 0
  key1=myproperty.key(1); 
  key2=myproperty.key(2);
  //more shortcuts
  pos1=key1.value
  pos2=key2.value
  //period is the length of the cycle
  period=key2.time-key1.time
  //phase is the point in the cycle we're at
  phase= Math.abs(Math.abs(time - key1.time) % (period*2) -period);
  //avoid divide-by-zero errors
  if ( period == 0 ) { //this shouldn't happen
      pos1;
  } else {
      (pos1*(Math.abs(period-phase))+pos2* phase )/period;
  }
  </code></pre>
  
  <p>Now what we're doing with <code>phase</code> is creating a value that goes from 0 to period and back again, and then making a weighted average of the two keyframes' values. It's easier to understand if you say there are 4 frames in the cycle and the first keyframe is at zero. </p>
  
  <ul>
  <li>At frame 1, phase is abs(abs(4-1) % 8 - 4) = 1 (the <strong>%</strong> is the <a href="https://en.wikipedia.org/wiki/Modulo_operation" rel="nofollow">modulo</a>
  operator, aka the remainder). When we substitute it into the last expression, the result is <code>(pos1 * (4-1) +pos2 * 1)/4</code> or to expand it <code>3/4*pos1 + 1/4*pos2</code> - so basically <code>pos1</code> is influencing the position 3 times as much as <code>pos2</code>.  </li>
  <li>At frame2: <code>phase</code>=2
  and it works out to <code>2/4*pos1 + 2/4*pos2</code>, so half and half, so it will be the average of the two keyframes.</li>
  <li>At frame 3 <code>phase</code>=3, so it's <code>1/4*pos1 + 3/4*pos2</code></li>
  </ul>
  
  <p>The second expression is basically doing the job of the built-in linear() interpolation method. Once time > period, then the abs() function kicks in and phase heads back to the original value.</p>
  
  <p>You could also use the phase variable with the valueAtTime property of the position property: <code>transform.position.valueAtTime(phase)</code>, if you wanted a complex motion path. that way it would shuttle backward and forward along the motion path between the two keyframes.</p>
  
  <p>If you don't want no steenkin' keyframes the first few lines could be replaced thus:</p>
  
  <pre><code>pos1=[123,456]; //or for a 3d layer [123,456,789]
  pos2=[987,654];
  period=123;
  startTime=12;
  phase = Math.abs(Math.abs(time - startTime) % (period*2) -period);
  </code></pre>
  
  <p>The benefit of using keyframes is that you couple the power of expressions with the ease of the GUI.</p>
  
  <p>I can see the value in this as a learning exercise (and a couple of glasses of Islay single malt this evening made it extra challenging), but one has to ask: why not use the built-in loopOut() expression? It probably will run much faster.</p>
  
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2014-01-31T13:57:43.787Z
Answers.LastActivityDate: 2015-01-27T13:00:22.767Z
Answers.LastEditDate: 2015-01-27T13:00:22.767Z
Answers.UserDisplayName: stib
Answers.Body: <p>Darby Edelen posted an <a href="https://forums.creativecow.net/thread/2/1047221" rel="nofollow">answer on Creative Cow.</a></p>
  
  <blockquote>
    <p>You won't be able to define any spatial paths any other way, but this
    should work:</p>
  
  <pre><code>d = 5;
  fd = d * 2;
  p1 = [200, 500];
  p2 = [500, 200];
  t = (time - inPoint) % fd;
  if(t &lt;= d) linear(t, 0, d, p1, p2);
  else linear(t, d, fd, p2, p1); 
  </code></pre>
    
    <p>This starts from p1 at the beginning of the layer and runs to p2 over
    5 seconds, then goes back to p1 and begins again. If you want the
    animation to ease in to each point you could use the ease() function
    instead of linear() providing the same arguments.</p>
    
    <p>-Darby</p>
  </blockquote>
  
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2014-01-31T19:31:37.413Z
Answers.Text: Hi and thank you for your contribution. Could you please fix the link to the Creative Cow website?
Answers.UserDisplayName: Saaru Lindestøkke
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2014-01-31T13:07:46.427Z
Answers.LastActivityDate: 2015-01-27T12:56:18.963Z
Answers.LastEditDate: 2015-01-27T12:56:18.963Z
Answers.UserDisplayName: Benjamin Allison
Body: <p>So I want to do something simple: define two sets of coordinates (a "from" and a "to" position), along with a duration in frames. I want the animation to be executed, then reversed, and looped.</p>
  
  <p>I know I can set two keyframes, and use <code>loopOut("pingPong");</code> but I want to try and achieve this with a simple expression.</p>
  
  <p>I'm sure it's possible!</p>
  
Comments.ContentLicense: CC BY-SA 3.0
Comments.CreationDate: 2015-11-11T00:51:11.317Z
Comments.Text: or indeed any frame < 50 the loop would run forever.
Comments.UserDisplayName: stib
Comments.ContentLicense: CC BY-SA 3.0
Comments.CreationDate: 2014-01-31T07:18:44.400Z
Comments.Text: I don't have specifics, so I won't post an answer, but I presume part of this could be done with a While loop:
  
  `while(time < 50) { [value[0]+1,value[1]+2] ) }`
Comments.UserDisplayName: nchpmn
Comments.ContentLicense: CC BY-SA 3.0
Comments.CreationDate: 2014-01-31T12:49:58.517Z
Comments.Text: The while loop would be executed every single frame. The above expression would run forever on frame 0, because for that frame time will always = 0
Comments.UserDisplayName: stib
Tags: <after-effects><after-effects-expressions>
Title: After Effects expression - define two positions, tween back and forth
UserDisplayName: Benjamin Allison
ThreadText: # After Effects expression - define two positions, tween back and forth
  
  (Asked by: username_0 on 2014-01-30)
  
  So I want to do something simple: define two sets of coordinates (a "from" and a "to" position), along with a duration in frames. I want the animation to be executed, then reversed, and looped.
  
  I know I can set two keyframes, and use `loopOut("pingPong");` but I want to try and achieve this with a simple expression.
  
  I'm sure it's possible!
  
  **Tags:** after-effects, after-effects-expressions
  
  > **Comments:**
  > **username_2**: I don't have specifics, so I won't post an answer, but I presume part of this could be done with a While loop:  `while(time < 50) { [value[0]+1,value[1]+2] ) }`  
  > **username_1**: The while loop would be executed every single frame. The above expression would run forever on frame 0, because for that frame time will always = 0  
  > **username_1**: or indeed any frame < 50 the loop would run forever.  
  
  ---
  
  ## 2 answers
  
  ### Answer by username_0 on 2014-01-31. Score: 3
  
  Darby Edelen posted an [answer on Creative Cow.](https://forums.creativecow.net/thread/2/1047221)
  
  > You won't be able to define any spatial paths any other way, but this
  > should work:
  >
  > ```
  > d = 5;
  > fd = d * 2;
  > p1 = [200, 500];
  > p2 = [500, 200];
  > t = (time - inPoint) % fd;
  > if(t <= d) linear(t, 0, d, p1, p2);
  > else linear(t, d, fd, p2, p1); 
  >
  > ```
  >
  > This starts from p1 at the beginning of the layer and runs to p2 over
  > 5 seconds, then goes back to p1 and begins again. If you want the
  > animation to ease in to each point you could use the ease() function
  > instead of linear() providing the same arguments.
  >
  > -Darby
  
  > **Comments:**
  > **username_3**: Hi and thank you for your contribution. Could you please fix the link to the Creative Cow website?  
  
  ---
  
  ### Answer by username_1 on 2014-01-31. Score: 2
  
  It's trickier than it seems if you want to do the whole thing from scratch.
  
  ```
  //first make some shortcuts
  //choose the property to drive it - could be any property on any layer
  myproperty=transform.position
  //choose the keys that define the movement - in AE key indexes start at 1, not 0
  key1=myproperty.key(1); 
  key2=myproperty.key(2);
  //more shortcuts
  pos1=key1.value
  pos2=key2.value
  //period is the length of the cycle
  period=key2.time-key1.time
  //phase is the point in the cycle we're at
  phase= Math.abs(Math.abs(time - key1.time) % (period*2) -period);
  //avoid divide-by-zero errors
  if ( period == 0 ) { //this shouldn't happen
      pos1;
  } else {
      (pos1*(Math.abs(period-phase))+pos2* phase )/period;
  }
  
  ```
  
  Now what we're doing with `phase` is creating a value that goes from 0 to period and back again, and then making a weighted average of the two keyframes' values. It's easier to understand if you say there are 4 frames in the cycle and the first keyframe is at zero.
  
  * At frame 1, phase is abs(abs(4-1) % 8 - 4) = 1 (the **%** is the [modulo](https://en.wikipedia.org/wiki/Modulo_operation)
    operator, aka the remainder). When we substitute it into the last expression, the result is `(pos1 * (4-1) +pos2 * 1)/4` or to expand it `3/4*pos1 + 1/4*pos2` - so basically `pos1` is influencing the position 3 times as much as `pos2`.
  * At frame2: `phase`=2
    and it works out to `2/4*pos1 + 2/4*pos2`, so half and half, so it will be the average of the two keyframes.
  * At frame 3 `phase`=3, so it's `1/4*pos1 + 3/4*pos2`
  
  The second expression is basically doing the job of the built-in linear() interpolation method. Once time > period, then the abs() function kicks in and phase heads back to the original value.
  
  You could also use the phase variable with the valueAtTime property of the position property: `transform.position.valueAtTime(phase)`, if you wanted a complex motion path. that way it would shuttle backward and forward along the motion path between the two keyframes.
  
  If you don't want no steenkin' keyframes the first few lines could be replaced thus:
  
  ```
  pos1=[123,456]; //or for a 3d layer [123,456,789]
  pos2=[987,654];
  period=123;
  startTime=12;
  phase = Math.abs(Math.abs(time - startTime) % (period*2) -period);
  
  ```
  
  The benefit of using keyframes is that you couple the power of expressions with the ease of the GUI.
  
  I can see the value in this as a learning exercise (and a couple of glasses of Islay single malt this evening made it extra challenging), but one has to ask: why not use the built-in loopOut() expression? It probably will run much faster.
  
  
  ---
  
  