Animating with Bezier Curves

(Check out on GitHubGist)

illustration by Antony Hare

I recently tackled the issue of building a Bezier Curve class from the ground up as I was developing my new animation engine Kinieta. It was an interesting little journey that I would like to share with you here.

The Problem of Interpolation

Interpolation here refers to the act of passing from one value to another through all the intermediary values between them. Depending on how it is done, interpolating between 0 and 1 might mean passing to 0.1, 0.2, 0.3 … 0.9 and finally 1. All animations are based on interpolation as the act of animating is the act of passing from one point on the screen to another, or from one color value to another as time goes by. For the moment, let’s say that we are interested in interpolating between 2 CGPoints that will represent the center of a UIView.

This relationship can be captured in an equation where one axis represents time and another the value being changed.

p1 and p2 are the coordinates between which are moving and t1 and t2 are the start and end time points. What we would do next is to normalise t to be between 0 and 1 by saying

t = tc / (t2 - t1)

where tc is the current timestamp

This is an example of Linear Interpolation where for equal distances on the x axis we will get equal distances on the y axis. We usually learn this at school in the form of f(x) = ax + b, but this is a description of a line rather than line segment. When interpolating between two points we need a way to start from one and finish in the other rather than keep going. The basic equation used is for points P0 and P1 for variable t between the values of 0 and 1 is:

x(t) = (1 – t) • P0x + t • P1x

y(t) = (1 – t) • P0y + t • P1y

or compacted for simplicity as:

P(t) = (1 – t) • P1 + t • P2

Each point P can be multiplied by a floating point value. In Swift for example we will have:

This is a great start but it visually means that our object being animated will start and stop rather abruptly, while If you think of a real world object, as for example a car, one will notice that it needs some time to accelerate until it reaches its maximum speed. This relationship between time and distance travelled can be captured with the following equation.

If you imagine the t value moving towards t2 then p will stay close to p1 for most of the duration until it will finally snap to p2 at the last few moments. This is also referred to as ease-in in “animation-land”. There are many ways to capture this relationship and back in the good (or really bad depending on your p.o.v) old days of Flash, Robbert Penner‘s name came first when thinking of equations to be used in animations. The problem with these was (and still is) that they are fixed to a particular degree of easing for each type (in, out and in-out) and it is rather hard to create a custom ease that will please your designer’s eyes.

Enter Bezier!

While working at Citroen, French engineer Pierre Bezier developed a way to describe curves that would later become widely used in Computer Graphics as Bezier Curves. Anyone who has used popular produces like Adobe Illustrator and Photoshop or 3D software like Cinema 4D, Maya and 3D Studio Max knows about them at least intuitively.

Bezier curves are the mathematical expression of a curve that passes close but not from various points called control points. They look like this:

and like this!

B(t) = (1 – t)• P0 + 3(1-t)2t • P1 + 3(1-t)t• P2 + t• P

or this… 😉

As you can see, the bezier curve expressed has a start at (0.0, 0.0) and end at (1.0, 1.0) so all you need to define are the 2 intermediary points p1 and p2. This will help simplify are conception of the curve. Now, all we need to do is visit this interactive application and get some values to plug in according with how we want…

Now, the code above is not optimised at all, but shows the equation verbatim which helps with understanding. Let’s look at how we can add a massive boost to our code by pre-calculating the values rather than solving the equation in real time.

Precalculating Values into a Lookup Table

There is a practical fact about animating visual elements in a computer screen that we can use to our advantage: screen resolution. Any moves that are smaller than 1 pixel will not be visible, so the maximum amount of new values that will ever be generated by the equation above is the maximum number of pixels that the item will be moving: the screen size.

What we can therefore do is to pre-calculate these values for every new bezier curve and use them as a lookup table. First we are going to pre-calculate the constants once, as they are common for all bezier curves…

The steps argument defines how many values we want to pre-calculate with the max being the screen size.

Lastly then we can pre-calculate the actual values into the Bezier.swift class file.

Example Use

Here is how a bezier could be used to animate a UIView between two points:

Now, the Animator class coded above is not to be used in production, it is only meant as a quick example of how a Bezier Curve could be used for animation.