How to Make Things Stop

back

Michael Herf
November 2002

I want to talk a little bit about animation.

Computer graphics people, for years, have been great at making smooth motions. You just take your favorite Bezier or Catmull-Rom cubic spline editor, move all the controls around, and you can animate anything, right?

Well, not really.

The way I see it, splines are really good at modeling of simple forces. You can push an object any which way, and if you do so exactly enough (with only piecewise-linear changes in force), you get CG-style spline-based animation.

Good so far, but stopping -- well, stopping's not so easy. In physics, that's usually the job of friction, and friction doesn't really lend itself to low-order spline simulation.

In the end, friction is proportional to the velocity of an object (and the weight, which we can usually cancel out.) And this means that we have to apply a force that is non-linear (which as I said above, cubic splines can't do.)

In fact, the only reasonable way to describe the deceleration curve for viscous friction is through exponentials. And (as we know from calculus class) Dx(e^-x) = -e^-x, and the sign flips back and forth like that through infinite differentials. An exponential force is not even so easy to approximate with a high-order spline, much less a cubic.

We'll let the polynomials rest for a while -- let's see what exponentials can do all by themselves.

A start

It took me a while to understand the problem I was solving. At first I thought I could just accelerate the usual way, and then mix in an "exponential stopping force" when I wanted it.

Of course, if you have an object moving without friction, you can always mix in an exponential to stop it, but you'll cause some discontinuities when you do.

You can just add V * (1 - e^-x) to your object motion, and things will stop more or less gracefully, but at the point you start applying the force, you'll notice a nice "jump" (as if the object has jumped into a swimming pool and must burn off velocity in a hurry.)

So what's wrong with that? Well, most of the time objects experience friction effects all the time, not just when they stop.

Continuous friction

What you really want is friction applied all the time, but in the presence of more conventional acceleration forces. I found a nice closed-form solution in my old mechanics book, under a section talking about terminal velocity.

I haven't actually worked out the math for the higher-order kinds of motions (I usually do these piecewise), but I have a nice reusable function I call a "Pulse" that does the following:

  • Applies a fixed force over an interval (a damped acceleration), and
  • Lets the exponential bleed away the velocity over a longer interval

The math was wonderfully elegant, and I've lost the derivation.

Anyway, the anticlimactic simplification of pages and pages of effort, which lets you do a damped acceleration under a constant force (and after a couple normalization steps), is just the following:

f(x) = x - (1 - exp(-x));
And then to allow friction to take over, as I said above, you just do the latter part of that expression:
f(x) = (1 - exp(-x));
These functions fit together perfectly, and you can push an object with a fixed force for a while, and watch it stop. It's like being bored in a restaurant, pushing a salt shaker across the table, trying to land on the edge of the table exactly.

Except with this function, you can always hit the edge of the table, every time.

Normalization

Since real exponentials decay forever, we'd like the exponential to decay to a point where we can call it "negligible." Now we could do this ad-hoc, or we could actually notice that physics provides for a "static friction" that takes over when velocity gets small. So we'll just call it static friction (as an excuse), clamp the movement under a certain amount, and rescale the curve to fit.

Code for the Pulse routine

This code assumes you want a reasonably-nice animation that doesn't go on too long. I found it was good for my application to let the tail go for about 8x as long as the acceleration, but you can make it longer (if you're willing to scale up the whole timescale too.)

All you do here is give the function Pulse() a number between 0 and 1, and it gives you another number between 0 and 1 back. I loosely refer to functions that do this as "Interpolators" and I like chaining them together for all sorts of mischief.

const float PulseScale = 8;		// ratio of "tail" to "acceleration"

float PulseNormalize = 1;
void ComputePulseScale();

// viscous fluid with a pulse for part and decay for the rest
static inline float Pulse_(float x)
{
	float val;

	// test
	x = x * PulseScale;
	if (x < 1) {
		val = x - (1 - exp(-x));
	} else {
		// the previous animation ended here:
		float start = exp(-1);

		// simple viscous drag
		x -= 1;
		float expx = 1 - exp(-x);
		val = start + (expx * (1.0 - start));
	}

	return val * PulseNormalize;
}

void ComputePulseScale()
{
	PulseNormalize = 1.f / Pulse_(1);
}

// viscous fluid with a pulse for part and decay for the rest
static inline float Pulse(float x)
{
	if (x >= 1) return 1;
	if (x <= 0) return 0;

	if (PulseNormalize == 1) {
		ComputePulseScale();
	}

	return Pulse_(x);
}

Wrap-up

Of course, you know, most people try to do what I've just described using numerical methods (the generic term for this in the animation world is "physics"), but sometimes you want precision and known endpoints, and these worlds should work a little better together.

I haven't actually researched this topic at all, so there may be a large body of previous work in the area. Also, generalizations to more complex sorts of acceleration would be incredibly useful.

Just watch things stop in the next CG animation you see, and you'll know it's not right. You don't have to add a full numerical physics engine to an animation system to make things stop -- I hope I've demonstrated that much.

I think the "closed-form" solutions to these sorts of problems could be incorporated as basis functions in existing animation systems without involving imprecise numerical techniques.

Now go, start stopping, or something.

This article is Copyright (C) 2002 Herf Consulting LLC. All Rights Reserved.