ofxDabSpring is an openFrameworks addon for simulating mass-spring-damper systems. The addon is available here.
Simulation
The simulation can handle mass points and springs in any dimension. The dimension is a template parameter. For convenience, one-dimensional, two-dimensional, and three-dimensional versions of the classes are provided as typedefs. The Eigen library instead of the OpenGL Mathematics library GLM is used for matrix and vector operations. The reason for this is the ability of Eigen to handle vectors and matrices of arbitrary dimensions.
The addon is currently fairly minimal in that it only simulated conventional springs. It is planned to extend the addon to also work with springs that possess not only a rest length but also a rest angle or a rest direction.
Programming Tutorial
Classes
The addon defines the following classes:
- dab::spring::MassPoint
- dab::spring::Spring
- dab::spring::EulerSolver
- dab::spring::LeapFrogSolver
- dab::spring::Simulation
dab::spring::MassPoint
This class defines a mass point. A mass point stores its mass, position, velocity, a cumulative force, and pointers to the springs it is associated with. Mass points can be instantiated by passing the mass and their position as arguments to the constructor. Two mass points for a two-dimensional simulation space can be created as follows:
dab::spring::MassPoint<2>* mMP1;
dab::spring::MassPoint<2>* mMP2;
mMP1 = new dab::spring::MassPoint<2>( 1.0, { 10.0, 50.0 } );
mMP2 = new dab::spring::MassPoint<2>( 1.0, { 100.0,30.0 } );
dab::spring::Spring
This class defines a spring. A spring stores its actual length, rest length, direction, stiffness, damping, and pointers to the two mass points it is associated with. Springs can be instantiated by passing two mass points as argument to the constructor. The other spring properties can either also be passed as arguments to the constructor of set via specific member functions afterwards.
// continuation from previous code block
dab::spring::Spring<2>* mSP;
mSP = new dab::spring::Spring<2>( mMP1, mMP2 );
mSP->setRestLength(50);
dab::spring::EulerSolver
This class computes numerical integration based on the Euler method. The class possesses a solve function that is called directly from within the simulation. Other than that, the class possesses a function to set the size of the time step used for numerical integration. The EulerSolver class is instantiated as Singleton.
dab::spring::EulerSolver& solver = dab::spring::EulerSolver::get();
solver.setTimeStep(0.1);
dab::spring::LeapFrog
This class computes numerical integration based on the Leapfrog method. The class declares the same member functions as the EulerSolver class. The LeapFrog class is also instantiated as Singleton
dab::spring::LeapFrogSolver& solver = dab::spring::LeapFrogSolver::get();
solver.setTimeStep(0.1);
dab::spring::Simulation
This class stores the mass points and springs and provides functions to calculate the forces acting on the mass points due to the springs’ restitution and damping behaviours. To calculate the new positions and velocities of mass-points and the new lengths and directions of springs, the simulation class relies for numerical integration on the previously mentioned solver classes.
A simulation is instantiated as Singleton. Springs are added to the simulation using the “addSpring” method. Mass points that are part of springs are automatically added to the simulation when their associated springs are added.
// continuation from previous code block
dab::spring::Simulation<2u>& springSim =dab::spring::Simulation<2>::get();
springSim.addSpring(mSP);
The simulation progresses by repeatedly calling its “updateLength”, “updateDamping”, “solve”, and “update” functions.
// continuation from previous code block
dab::spring::Simulation<2>& springSim = dab::spring::Simulation<2>::get();
dab::spring::LeapFrogSolver& solver = dab::spring::LeapFrogSolver::get();
springSim.updateLength();
springSim.updateDamping();
springSim.solve(solver);
springSim.update();
A minimalistic example of a mass-spring damping simulation is available as example that is part of the addon.