package physics;

import java.util.HashMap;
import java.util.Map;
import javafx.scene.Node;
import layout.PhysLayout;
import org.jbox2d.common.Vec2;
import org.jbox2d.dynamics.Body;
import org.jbox2d.dynamics.World;

/**
 * Manage a JBox2D simulation of multiple JavaFX nodes. Nodes have no collision,
 * and are moved by applying forces (from mechanical springs and forcefields).
 *
 * @author Christoph Burschka &lt;christoph@burschka.de&gt;
 */
public class Box2DSpringSimulation {

    private final PhysLayout layout;
    private final Map<Node, Body> bodies;
    private final World world;
    private double friction;

    public Box2DSpringSimulation(PhysLayout layout) {
        this.layout = layout;
        bodies = new HashMap<>();

        // New zero-gravity world:
        world = new World(new Vec2(0, 0));

        layout.getNodes().stream().forEach((node) -> {
            // TODO: Create each body.
            // (with zero volume to avoid collision)
            Body body = new Body(null, world);
            bodies.put(node, body);
        });
    }

    /**
     * Applies spring force between a and b.
     *
     * @param a
     * @param b
     * @param spring
     */
    private void applySpring(Body a, Body b, Spring spring) {
        Vec2 pA = a.getWorldCenter();
        Vec2 pB = b.getWorldCenter();
        Vec2 force = spring.getForce(pA, pB);
        a.applyForceToCenter(force);
        b.applyForceToCenter(force.negate());
    }

    private void applyFriction(Body a) {
        Vec2 v = a.getLinearVelocity();
        a.applyForceToCenter(v.mul((float) -friction));
    }

    private void applyAllForces() {
        layout.getAllConnections().stream().forEach((e) -> {
            applySpring(bodies.get(e.getKey().getA()), bodies.get(e.getKey().getB()), e.getValue());
        });
        bodies.values().stream().forEach((a) -> {
            applyFriction(a);
        });
    }
}

@@ -54,19 +54,67 @@
 va.util.
-Map
+HashSet;%0Aimport java.util.Map;%0Aimport java.util.Set
 ;%0Aimport
@@ -637,16 +637,58 @@
  world;%0A
+    private final Set%3CForceField%3E fields;%0A
     priv
@@ -826,16 +826,50 @@
 Map%3C%3E();
+%0A        fields = new HashSet%3C%3E();
 %0A%0A      
@@ -2019,16 +2019,152 @@
 ion(a);%0A
+            fields.stream().forEach((field) -%3E %7B%0A                a.applyForceToCenter(field.force(a.getWorldCenter()));%0A            %7D);%0A
         

Support adding force fields to Box2DSpringSimulation.