7
\$\begingroup\$

I am trying to create an interpolate function for an animation library to achieve a tweening effect between frames.

I want this to work with Bezier curves. I have created a jsFiddle (here) of my progress so far.

I am trying to create a linear tween using this bezier definition:

{
    p0: new Vector(0,0), //Start point
    p1: new Vector(0,0), //Control point 1
    p2: new Vector(1,1), //Control point 2
    p3: new Vector(1,1)  //End point
}

I have implemented the interpolation function from this tutorial.

var u = 1 - t;
var tt = t*t;
var uu = u*u;
var uuu = uu * u;
var ttt = tt * t;

var p = p0.multiply(uuu);
p = p.add(p1.multiply(3 * uu * t));
p = p.add(p2.multiply(3 * u * tt));
p = p.add(p3.multiply(ttt));

The problem I am having is that when I run the function, the animation does not appear to be linear, but rather like 'ease-in-out'.

Ideally I would like the linear animation to work the same as this CSS transition.

Can anybody see why it is not animating in a linear fashion?

\$\endgroup\$
1

4 Answers 4

6
\$\begingroup\$

After playing with this function more. I realise that the bezier control points are acting similar to magnets.

If I spread the control points so that they are positioned along a straight line with equal distance between, then the animation works as expected

{
    p0: new Vector(0,0), //Start point
    p1: new Vector(.333,.333), //Control point 1
    p2: new Vector(.666,.666), //Control point 2
    p3: new Vector(1,1)  //End point
}
\$\endgroup\$
3
  • \$\begingroup\$ I see what you mean. However when I set the first control point to the end point and the second control point to the start point I get an 'inverse' ease-in-out effect - See here. \$\endgroup\$
    – Jackson
    Oct 10, 2015 at 9:56
  • \$\begingroup\$ Sorry for the math error in my earlier comment. The vector from the start point to the first control point is one third of the initial velocity of the interpolated point, where the unit of time is the interpolation duration. Similarly, the vector from the last control point to the end point is one third of the final velocity. So, spacing the control points equally at 1/3 and 2/3 along the line does give you constant linear velocity for the interpolation, as you desire. This looks to hold for other orders of Bézier too: multiply the initial or ending vector by the order to get the velocity. \$\endgroup\$
    – DMGregory
    Oct 10, 2015 at 13:26
  • \$\begingroup\$ Didn't notice this answer when I made mine. You are completely correct that colinear control points make a linear interpolation. Good job on finding the solution! \$\endgroup\$
    – Alan Wolfe
    Oct 10, 2015 at 20:43
5
\$\begingroup\$

I usually solve this using Catmull-Rom splines. Instead of using control points, you simply specify the two points between which you'd like to interpolate, as well as a "previous" point and a "next" point. If the 4 points all lie on the same line and are evenly spaced, the interpolation will be linear.

catmull-rom spline

In the image above, there are four points p0, p1, p2 and p3. The following line of C++ code will interpolate between p1 and p2, where t is in the range [0...1]:

return p1 + 0.5*t*(p2-p0 + t*(2*p0-5*p1+4*p2-p3 + t*(3*(p1-p2)+p3-p0)));

You can interpolate any kind of value type, as long as the type supports addition and multiplication by a scalar.

If the values do not lie on a line, or are not spaced evenly, the catmull-rom interpolation will generate a smooth curve. Interpolation will be non-linear:

catmull-rom curve

If there is no "previous" or "next" point (e.g. at the start or end of a series of curve segments), use the following equations:

p0 = 2 * p1 - p2;
p3 = 2 * p2 - p1;
\$\endgroup\$
3
  • \$\begingroup\$ Thanks for the reply. This looks like a good interpolation function which I will look into. \$\endgroup\$
    – Jackson
    Oct 10, 2015 at 10:09
  • \$\begingroup\$ In your last bit of code if there is no previous or next point, would that translate to Unity as is? Is this simply multiplying each vector component by 2, then subtracting the other vector? I'm just trying to visualize what this actually is doing. Thanks. \$\endgroup\$ Oct 28, 2015 at 13:35
  • \$\begingroup\$ @Doug.McFarlane - The two formulas simply "reflect" points. In the case of p0, for example, picture a vector from p1 to p2 and then multiply it by -1. The result is p0 = p1 + (p1 - p2) = 2 * p1 - p2. Doing this, we maintain proper direction and spacing, resulting in a more coherent interpolation. You could also set p0 to p1, but then you clearly see an ease-in effect on the interpolation. \$\endgroup\$
    – Paul Houx
    Oct 29, 2015 at 16:20
2
\$\begingroup\$

Bezier curves are inherently for making smooth curves. Linear interpolation is simpler. To linearly interpolate two points, you can simply apply the following formula for time t in [0,1]:

interpolatedPoint = startPoint * (1 - t) + endPoint * t

When t=0, the interpolatedPoint is exactly equal to the startPoint. Similarly, when t=1, the interpolatedPoint is exactly equal to the endPoint. When t is in between 0 and 1, it is somewhere on the line between the two points.

I forked and modified your jsfiddle to clarify: http://jsfiddle.net/kevinAlbs/3em6br3b/1/

\$\endgroup\$
1
  • \$\begingroup\$ Thanks for your answer. I would prefer the animation to be flexible so that it can be both linear or curved using a cubic bezier curve. \$\endgroup\$
    – Jackson
    Oct 10, 2015 at 9:54
2
\$\begingroup\$

Why are you using bezier curves to do a linear interpolation? You could just use a linear interpolation to do linear interpolation and not use curves at all.

If the reason is that you want to have a more general purpose curve based interpolation system and you are trying to get it to do a linear interpolation for a specific usage case, there is a way.

Basically, the control points need to be points on a line. For instance the control points 0, 0.333, 0.666, 1.0 would give you a linear interpolation between 0 and 1, when using a cubic bezier curve to do it.

Again though, if you want linear interpolation, there is no reason to use a curve.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .