// decompose the velocities of the balls into a component in the
// direction of the line between the balls (normal) and a
// perpendicular one (tangential)
// apply a central elastic collision to the normal components
// and let the tangential components be unchanged
Vector2f v1 = ball.getVelocity();
Vector2f v2 = ball2.getVelocity();
float m1 = ball.getMass();
float m2 = ball2.getMass();
Vector2f n = new Vector2f();
Vector2f.sub(ball2.getPosition(), ball.getPosition(), n);
n.normalise();
Vector2f t = new Vector2f(n.y, -n.x);
float v1_n = Vector2f.dot(v1, n);
float v2_n = Vector2f.dot(v2, n);
float v1_t = Vector2f.dot(v1, t);
float v2_t = Vector2f.dot(v2, t);
// calculate new velocities in normal direction
float u1_n = (m1 * v1_n - m2 * v1_n + 2.0f * m2 * v2_n) / (m1 + m2);
float u2_n = (m2 * v2_n - m1 * v2_n + 2.0f * m1 * v1_n) / (m1 + m2);
// transform to x- and y-components
Vector2f n1 = new Vector2f(n.x, n.y);
Vector2f t1 = new Vector2f(t.x, t.y);
Vector2f n2 = new Vector2f(n.x, n.y);
Vector2f t2 = new Vector2f(t.x, t.y);
Vector2f.add((Vector2f) n1.scale(u1_n), (Vector2f) t1.scale(v1_t), v1);
Vector2f.add((Vector2f) n2.scale(u2_n), (Vector2f) t2.scale(v2_t), v2);
ball.setVelocity(v1);
ball2.setVelocity(v2);
}