Is there an explicit form for cubic Bézier curves?

(See edits at the bottom)

I'm trying to use Bézier curves as an animation tool. Here's an image of what I'm talking about:

Example of a Bézier curve

Basically, the value axis can represent anything that can be animated (position, scaling, color, basically any numerical value). The Bézier curve is used to control the speed at which the value is changing as well as it start and ending value and time. In this graphic, the animated value would slowly accelerate to a constant speed, then decelerate and stop.

The problem is, that Bézier curve is defined with parametric equations.

$f_x(t):=(1-t)^3p_{1x} + 3t(1-t)^2p_{2x} + 3t^2(1-t)p_{3x} + t^3p_{4x}$

$f_y(t):=(1-t)^3p_{1y} + 3t(1-t)^2p_{2y} + 3t^2(1-t)p_{3y} + t^3p_{4y}$

What I need is a representation of that same Bézier curve, but defined as value = g(time), that is, y = g(x).

I've tried solving for t in the x equation and substituting it in the y equation, but that 3rd degree is giving me some difficulty. I also tried integrating the derivative of the Bézier curve (dy/dx) with respect to t, but no luck.

Any ideas?

Note : "Undefined" situations are avoided by preventing the tangent control points from going outside the hull horizontally, preventing any overlap in the time axis.

EDIT : I have found two possible solutions. One uses Decasteljau's algorithm to approximate the $s$ parameter from the $t$ parameter, $s$ being the parameter of the parametric curve and $t$ being the time parameter. Here (at the bottom).

The other, from what I can understand of it, recreates a third degree polynomial equation matching the curve by solving a system of linear equation. Here. I understand the idea, but I'm not sure of the implementation. Any help?


Solution 1:

You're really looking for a cubic equation in one dimension (time).

$$ y = u_0(1-x)^3 + 3u_1(1-x)^2x + 3u_2(1-x)x^2 + u_3x^3 $$

Is all you need.

Walking $t$ at even intervals (say in steps of 0.1) takes evenly spaced points along the parametric curve.

enter image description here

So, the answer to your question is really quite simple. The parametric bezier curve provides 2 variables as the output, with only 1 variable as the input. To control an animation in time like these, that's only a 1 dimensional situation. So consider $t$ as time, and drop one variable (say drop $x$). Your animation ease curve is controlled by the $y$ value:

enter image description here

Now as $t=0,0.1..1$, you have an animation parameter that starts slowly, moves at medium speed in the middle, and slows down at the end.


Examples

Setting $u_0=0$, $u_1=0.05$, $u_2=0.25$, $u_3=1$ gives an ease-in curve (slow start, fast end)

ease-in curve

Setting $u_0=0$, $u_1=0.75$, $u_2=0.95$, $u_3=1$ gives an ease-out curve (fast start, slow end)

ease-out curve

Solution 2:

You don't want that! You want this!

'Eased' motion and 1D Beziers

You can solve a cubic, but it's tricky and probably not what you want

The fact that a cubic generally has three solutions is a big clue that 2D Beziers are powerful enough to draw totally inappropriate graphs for a single-valued function like yours.

(By a single-valued function I mean one that won't ever wrap back on itself in the x-direction.)

If you're looking for an attractive interpolation of discrete values, however, 2D Beziers are very convenient, especially as they're implemented in almost all drawing packages; so indeed 2D Beziers often are used to draw graphs of single-valued functions.

In such a case, it is best to make sure that each Bezier's two control handles share their horizontal coordinate, which would always lie halfway between the horizontal coordinates of the Bezier's end points. And that will cause the horizontal coordinate to vary linearly with t, and NOT as a cubic.

That effectively limits the possibilities of a 2D Bezier to those of a 1D Bezier. It's much more appropriate for a single-valued function, and of course makes it much easier to find y in terms of x.

Consider a 1D equivalent

Not all Beziers are as much as 2D. In fact, easing (the technical name for the motion you are trying to achieve) is usually achieved with a 1D Bezier. There is even a specification in SVG for defining a custom easing on an animation, by specifying the end values and control values for a 1D Bezier.

Control values, in 1D as in 2D, are end values plus some difference value, which is related to the desired gradient of the dependent variable, with respect to the independent variable. In particular, it happens to be exactly half the desired gradient. In your case, you want a gradient of zero at both ends of the eased motion, so your control values should be the same as your end values. (This corresponds to the fact that your control handles are horizontal, giving a zero gradient of y with respect to the independent variable.)

Option 1: Construct a polynomial and step through it with constant time increments

If you have a pair of values and their gradients with respect to the independent variable, you can interpolate them with a Cartesian cubic, in the form

y = a.t^3 + b.t^2 + c.t + d

If you decide on values for more derivatives, the same method can lead you to higher orders of polynomial. So to specify end accelerations, you need the versatility of a 5th order polynomial, and to specify jerk and jounce, you need the 7th and 9th orders respectively. This adds expressiveness to the curve without requiring you to find the roots of anything!

I'll explore the method here for just the cubic version.

This is effectively a one dimensional case of what you would do to construct a 2D Bezier. Again, you are fixing a position and a gradient at each end of the curve.

For some extra background: The control handles you're used to, on a 2D Bezier, automatically determine the gradients of x and y with respect to the independent variable t. The gradients are twice the size of the horizontal and vertical components, respectively. (Scaling them together, away from this value, gives other valid curves of the same order, maintaining the tangent direction, but they would compromise the recursive interpolation properties of a Bezier and its hull.)

In a 1D case, you need to decide the gradient of y with respect to x, by whatever means is appropriate for your application. There are many algorithms you could choose for that. It is just like choosing the positions of control handles. In your case, you want a gradient of zero at both ends of the easing.

Once you've chosen your end values, you have some (fairly simple) simultaneous equations to determine the values of a, b and c.

To make things easier, let's suppose that t runs from 0 to 1. I would recommend implementing it this way; actual time values for a particular animation can be mapped to and from this time-scale as needed. So if the actual animation starts at start and lasts for duration, just use

T = start + duration.t and t = (T - start)/duration

to convert.

That way, at one end of the line, the equations for y and its gradient,

y = a.t^3 + b.t^2 + c.t + d
dy/dt = 3a.t^2 + b.t + c

simply collapse all the way down to

y = d
dy/dt = c

So from the value of the function and the decided-on gradient at t = 0, you already have two terms of the polynomial. At the t = 1 end of the motion, we find

1. y = a + b + c + d
2. dy/dt = 3a + 2b + c

and finding a and b by elimination is now easy.

Equation 2. - 2 x Equation 1. to eliminate b and calculate a
3 x Equation 1. - Equation 2. to eliminate a and calculate b

Option 2: Use in-built Bezier functionality, and just specify end/control values

This would probably be done most easily by more thoroughly investigating the easing functions available to you in the language you're using.

My personal fallback, if there is no such functionality, would be the above method of finding a suitable polynomial to step through.

But you already appear to be almost there, with your solution, so I can't resist adding what is needed, to complete it.

So you can clearly can draw a Bezier somehow. And you appear to have decided to get one coordinate of that Bezier -- again, somehow -- so then you can use it as your easing value. Okay.

Modify your particular Bezier to be linear in the horizontal axis

Certain special cases of Bezier are worth noting here.

  1. When the control points have identical x values, x will only be quadratic in t. Likewise, if they have identical y values, y will only be quadratic in t. When both components are identical (so there is only one, shared control point), the Bezier is entirely quadratic.
  2. When the shared x value is equidistant between the end points, x will only be linear in t. Likewise, if a shared y value lies halfway, y will only be linear in t. For a shared control point halfway between the end points, the Bezier is entirely linear. (The curve is straight and is traced at constant speed.)

So by setting the x coordinates of the control handles to be halfway between the x coordinates of the end points, you will construct the special case that has the parametric form

x = a.t^3 + b.t^2 + c.t + d, with a = 0, b = 0 making it LINEAR
y = e.t^3 + f.t^2 + g.t + e

Once again, you now have a linear relationship between x and t, so you can easily express y in terms of x by making a substitution in the explicit expression for y.

Because I Don't Like To Leave Anything Unanswered...

Do you still have need of a cubic solver? And can you say more about your difficulties?

A word of warning about naive equations

You definitely do want to solve the linear equation AS a linear equation. Don't be thinking that something more general must also work on lower orders of equation. Even if you write a cubic solver, it will actually break in this case.

Trying to solve a lower order equation with techniques for higher orders will tend to lead you into a division by zero. In a simpler case, consider solving the linear equation,

B.t + C = 0

with the general formula for quadratics,

t = (-B +- sqrt(B^2 - 4A.C)) / 2A, with A = 0

This gives the answers finite/zero and zero/zero, which aren't particularly useful even if you can interpret them as correct. Each is easy to understand if you watch what happens to the solutions as the value of A vanishes.

Another word of warning about cubic and quartic solvers

A lot is already available on how to do this, either symbolically or numerically. I can't add much here, to what's already available, especially with the scant clues I have about what level you're at. (You haven't said much about your progress so far.)

But the standard warnings bear repeating:

  1. Symbolic solvers are often slower at achieving the same accuracy.
  2. When you vary the terms of the polynomial smoothly, the true roots should vary smoothly, but the calculated roots often won't, being sensitive to rounding errors in your calculation. (That's mainly true of the symbolic solvers.) This is what is meant by the 'numeric stability' of your roots. You want smooth motion so this is an issue.
  3. Are you sure you want to spend performance on root finding, in the inside loop of an animation, given the huge number of explicit functions you could use for easing?

Solution 3:

Notice that if you happen to have $p_{1y}=0$, $p_{2y}=1/3$, $p_{3y}=2/3$ and $p_{4y}=1$, so that $f_y(t)=t$, then the graph you are after is actually of the inverse function of $f_x(t)$, which is going to be a mess and inevitable as complicated as the usual formula for the roots of a cubic polynomial.

That shows that the general formula necessarily has to be complicated. I doubt there is anything useful to be done...

Solution 4:

The basic required function is $y = 3x^2 - 2x^3$.

This is a smooth "ramp" that rises from $y=0$ at $x= 0$ to $y = 1$ at $x=1$.

You can then shift it and scale it to suite your needs.

This is a real-valued Bezier curve that has "control points" $\left(0, 0\right), \left(\frac{1}{3}, 0\right), \left(\frac{2}{3}, 1\right), \left(1, 1\right)$.