The first time I implemented a spring model, it fascinated me for hours. The effect itself is amazingly realistic, and its implementation is fairly simple. Fortunately, I found a lot of articles, references, and source code to help with my research. Nevertheless, as I went further down the road, I noticed that in most cases these references limited to the standard applications of spring models -- string, cloth and jelly.
This article reviews the implementation of a spring model from its simplest form to more sophisticated applications, taking the subject a step beyond the material available in most references.
Before modeling springs with the computer, you need to understand the basic principles of springs from your classic physics book.
As you compress or extend a spring, it creates a force that is opposed to direction fo the force that you are applying. This force is mathematically equated by the formula:
Dx = xc-xi
Where F is the resultant force, k is the spring coefficient, and Dx the distance from its inertial position (xc = current distance, and xi = distance at the inertial position).
Mathematically, you can think of a spring as being two points separated from a distance x. This is its inertial position. In other words, if you don't move these points, no force will be generated because Dx = 0. If you try to compress the distance between these points the Dx will be negative, generating a positive force (expansion). If you try to separate these points the Dx will be positive, generating a negative force (compression). The picture below illustrates these cases.
-/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\- Pb (Dx
= 0, F = 0)
Pa -/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\- Pb (Dx < 0, F > 0)
Pa -/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\- Pb (Dx > 0, F < 0)
Springs (using "ascii art")
The k coefficient is called the elasticity coefficient, and weights the spring's final force. In other words, a spring with bigger k is stiffer because it creates a larger force from its inertial state. Conversely, a spring with a smaller k is more flexible because it creates a smaller force from its inertial state.
Spring Force Implementation
Implement the spring force is a direct application of the first two equations. In other words, first compute the distance of the spring's extremities (Pa and Pb). This is done through Pythagoras' theorem. Once you have the distance, you subtract from the inertial distance getting the Dx. Then, multiply this scalar value to the k coefficient and finally use the inverse of this value to compute the force.
As simple as it sounds, the implementation in C code has its details. First, the distance Dx, and the k coefficient are scalars, but the force itself is a vector. Therefore, it requires a direction, which can be from Pa to Pb, or from Pb to Pb. As of right now, the code below always use the direction Pb to Pa, or mathematically Fdir = Pb-Pa.
As the force's direction is normalized, you can use the distance you already computed for the Dx for the normalization. You must be careful is to avoid division by zero when normalizing the vector. This is a nasty condition and it usually does not happen, but you have to do something about it in case it does. You have two options in this particular case: assign a random force to the points, or null out the force. Theoretically, the force should be big enough that as the points get closer they will never occupy the same space. Therefore, it seems to be a good option to randomly assign a massive force in this case. However, this can cause undesirable artifacts, because some points inside the spring model might fly a part with extreme velocity when compared with others. Although nulling out seems wrong, it doesn't cause any major problems and preserves the spring's stability. The code below shows the implementation; it's a little redundant for sake of clarity. I null out the force in case of potential division by zero.
Listing 1: Computing A Spring's Force
#define SPRING_TOLERANCE (0.0000000005f)
(VECTOR *force, VECTOR *pa, VECTOR *pb, float k, float delta_distance)
float dx = pb->x-pa->x;
float dy = pb->y-pa->y;
float dz = pb->z-pa->z;
distance = (float) sqrt ((dx*dx)+(dy*dy)+(dz*dz));
(distance < SPRING_TOLERANCE)
force->z = 0.0f;
force_dir.y = dy;
force_dir.z = dz;
force_dir.x /= distance;
force_dir.y /= distance;
force_dir.z /= distance;
delta = distance-delta_distance;
intensity = k*delta;
force->x = force_dir.x*intensity;
force->y = force_dir.y*intensity;
force->z = force_dir.z*intensity;