NPhysics: Solving Statics with Physics Simulations

This was my high school research project. I was 16, just starting to learn Java, and for some reason decided the best way to spend a year of evenings was building a 2D physics simulator that could also solve statics problems. Looking back, it was probably a bit much for a bachillerato project. Worth it.

The result is NPhysics: a desktop app where you draw a physical system, mark one of the forces as unknown, and the program figures out what value it needs to have to keep everything in equilibrium. Then it switches to real-time simulation mode using JBox2D so you can verify the answer by watching nothing move.


What even is statics?

Statics is the branch of mechanics that deals with systems that are not accelerating. That is not the same as systems where nothing is happening. There are usually plenty of forces, they just cancel out perfectly.

The two conditions for static equilibrium are:

  1. Sum of forces = 0 in every direction (no net linear acceleration)
  2. Sum of torques = 0 about any point (no net rotational acceleration)

A classic example: a crane holding a load. The boom of the crane has gravity pulling its tip down and the cable pulling it back up at some angle. There is also a reaction force at the base joint. The question statics answers is: given the boom geometry, the cable angle, and the load weight, how much tension does the cable need to hold everything still?

You could solve this analytically with a system of equations. A lot of statics courses teach you to do just that, by hand, with careful free-body diagrams. It works fine for simple cases. For more complex setups (chains of bodies, pulleys, springs, joints with unknown reaction directions) it gets messy fast. That is where a simulator becomes useful.


The UI: drawing a physical system

NPhysics has two modes: design and simulation.

In design mode you work on a grid. You draw polygon and circle bodies by clicking vertices. Then you connect them with the available joints:

  • Axes (revolute joints): pin two bodies to rotate around a shared point
  • Prismatic rails: constrain a body to slide along a line
  • Ropes: set a maximum distance between two anchor points
  • Springs: apply a Hooke's law force (F = -kx) between two points
  • Pulleys: link the movement of two hanging objects through a fixed wheel

You can also add water (buoyancy) and motors (an axis with nonzero torque that spins the attached body).

For each body you can set mass, density, friction, and restitution. Forces are defined visually: you click to set the origin and drag to set the direction, then type the magnitude in the properties panel. Force vectors are typed in either cartesian (x, y) or polar (magnitude, angle) form.

Once the system is set up you switch to simulation mode. The background turns black, JBox2D takes over, and everything starts obeying Newton's laws. The program draws color-coded arrows showing the forces on each body:

ColorForce type
RedUser-defined applied forces
YellowGravity
GreenReaction forces (joints, contacts)
OrangeRope tension

You can grab objects with the mouse and push them around. It also shows linear momentum vectors if you press I.


The trick: marking a force as unknown

The interesting part is the static solver. When you define a force you can check a box that says "Set this force as unknown." This tells the program to treat the magnitude of that vector as the variable to solve for.

Once you press P (or the Solve button in the properties panel), the solver runs and fills in the missing value. The result is a force magnitude that (in theory) holds the system in perfect equilibrium.

The direction of the force stays fixed. You define the angle, the program finds the magnitude.


How the solver works: binary search over a physics simulation

Here is the part I am still kind of proud of. Instead of building a symbolic equation solver, I turned the problem into a root-finding problem and used the bisection method on it.

The key insight is this: angular velocity is a monotone function of force magnitude (at least in most well-posed statics problems). If you apply too little force, the object rotates one way. Too much, and it rotates the other way. Somewhere in between it does not rotate at all. That is equilibrium.

So the algorithm in SolveJob.java does this:

  1. Start with a large interval [a, b] = [-10000, 10000].
  2. Pick the midpoint c = (a + b) / 2.
  3. Run a quick Box2D simulation with the force set to magnitude c.
  4. Read the resulting angular velocity of the target body.
  5. If angular velocity is positive, the force is too strong (or wrong direction): shrink the interval toward the other side.
  6. Repeat until |angular velocity| < epsilon.

This is the bisection method (also called Bolzano's theorem in Spanish curricula, hence the class comment in the source). Each iteration cuts the search interval in half, so convergence is logarithmic. Starting from ±10000 and targeting precision of 10^-14, it takes about 45 iterations. The actual simulation runs 10 Box2D steps per evaluation, so the whole solve takes a few hundred milliseconds at most.

Here is the core of SolveJob.java:

float a = -10000;
float b = 10000;
float c = -1;

do {
    c = (a + b) / 2f;
    float C = function(c);         // run Box2D, return angular velocity

    if (function(a) * C < 0) b = c;
    else if (function(b) * C < 0) a = c;

} while (Math.abs(function(c)) > Epsilon);

f.setForce(new Vector2(c * cos(rad), c * sin(rad)));

And function(c) creates a fresh Box2D world, applies all the known forces and the unknown force at magnitude c, steps it forward, and returns the angular velocity of the target body.


Try it: bisection in action

This is the same algorithm running on a toy problem. The curve shows how angular velocity changes with force magnitude (in NPhysics this curve is implicit, computed by running Box2D each time). Equilibrium is at zero.

This is the exact numerical trick NPhysics uses to find unknown forces. The curve represents how much an object rotates (angular velocity) when you apply a given force magnitude. Equilibrium is at zero. Press Step to watch the bisection method home in on it.

Iteration 0 | interval [-6.000, 14.000] | f(c) = —

Each step cuts the search interval in half. Within about 45 steps it converges to float precision. The real solver does the same thing, except the curve is not a smooth mathematical function but a noisy physics simulation. It works anyway because the curve is monotone (for well-posed problems).


The limitation worth knowing

The solver only works when the torque generated by the unknown force is relevant. If all forces, including the unknown, are applied at the exact same point, changing the unknown's magnitude has no rotational effect and the angular velocity stays flat regardless of the value. The bisection interval never closes.

The workaround: for multi-body problems, solve forces one at a time, starting with the ones that have the most direct torque leverage. The user guide (on the academic project page) covers this in more detail.


The simulation backend: JBox2D

The simulation itself runs on JBox2D, the Java port of Box2D. If you have ever built anything with Godot, Unity, or LibGDX, you have probably used Box2D under the hood without knowing it. It handles:

  • Rigid body dynamics (position, velocity, angular velocity)
  • Collision detection and resolution (via a BVH tree)
  • Joints (revolute, prismatic, rope, pulley, mouse)
  • Continuous collision detection for fast-moving objects

NPhysics wraps Box2D with its own layer (SimulationStage.java, SimulationPackage.java) that translates from the design-time representation (polygon actors, force components, joint definitions) into Box2D body and joint objects. Forces are applied per-step inside the Box2D world step loop.

Water buoyancy is approximated by applying an upward force proportional to the submerged area of each body, computed geometrically frame by frame.


Technical stack

The whole project uses LibGDX, which is a cross-platform Java game framework. It handles:

  • The OpenGL rendering (via SpriteBatch and ShapeRenderer for drawing polygon outlines, force arrows, and grid)
  • Scene2D for the UI (actors, stages, hit testing)
  • Input routing (touchDown, touchDragged, mouseMoved, keyDown)
  • File I/O

The UI is built entirely in Scene2D. Every interactive object in the design phase is a Scene2D Actor. The Sandbox class extends Stage and overrides the input methods to handle state-machine-based tool switching (creating points, axes, forces, ropes, etc.).

Polygon geometry (for triangulation and collision shapes) uses Earcut4j, a Java port of the mapbox/earcut triangulation library.

The project compiles with Gradle and runs anywhere Java 8 is installed. There is also a GWT export target in the html/ module for running in the browser, though it was never the primary deployment target.


A few simulations worth watching

Robotic arm: Setting up a multi-link arm with motors and solving the torque at each joint.

Motor torque calculation: A simpler single-body example showing the solver finding the exact motor torque for equilibrium.

Water simulation: Buoyancy applied to a floating body with adjustable density.


Download and source

NPhysics is written in Java and runs on Windows, Linux, and macOS. You can grab the latest release from GitHub or build from source with gradlew desktop:dist.

For the full user guide (with instructions for every joint type and tool), see the NPhysics academic project page.

The main insight I took from this project is that you do not always need a closed-form solution. Sometimes it is faster to just run the physics engine a few hundred times and do binary search on it. It felt like cheating at the time. Now I think it is just pragmatic.