package liquid;

import java.awt.geom.Rectangle2D;
import java.lang.InterruptedException;
import java.util.Random;
import javax.swing.JFrame;
import lombok.extern.java.Log;
import org.jbox2d.collision.shapes.CircleShape;
import org.jbox2d.collision.shapes.PolygonShape;
import org.jbox2d.common.Vec2;
import org.jbox2d.dynamics.Body;
import org.jbox2d.dynamics.BodyDef;
import org.jbox2d.dynamics.BodyType;
import org.jbox2d.dynamics.FixtureDef;
import org.jbox2d.dynamics.World;

@Log
public class Launcher {

    private static final float DT = 1f / 30f; // seconds
    private static final int V_ITERATIONS = 8;
    private static final int P_ITERATIONS = 3;
    private static final float WIDTH = 50f;
    private static final float HEIGHT = 70f;
    private static final float THICKNESS = 1f;
    private static final Vec2 GRAVITY = new Vec2(0, -10);
    private static final Rectangle2D VIEW =
        new Rectangle2D.Float(WIDTH / -2, HEIGHT / -2, WIDTH, HEIGHT);

    /* Balls */
    private static final int BALLS = 20;
    private static final float BALL_DENSITY = 1f;
    private static final float BALL_FRICTION = 0.1f;
    private static final float BALL_RESTITUTION = 0.6f;

    public static void main(String[] args) {
        /* Fix for poor OpenJDK performance. */
        System.setProperty("sun.java2d.pmoffscreen", "false");

        World world = new World(GRAVITY, true);
        Viewer viewer = new Viewer(world, VIEW);
        JFrame frame = new JFrame("Fun Liquid");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(viewer);
        frame.setResizable(false);
        frame.pack();
        frame.setVisible(true);

        /* Set up the containment box. */
        buildContainer(world);

        /* Add a ball. */
        Random rng = new Random();
        for (int i = 0; i < BALLS; i++) {
            addBall(world,
                    (rng.nextFloat() - 0.5f) * WIDTH,
                    (rng.nextFloat() - 0.5f) * HEIGHT);
        }

        final int dtms = (int) (DT * 1000.0);
        while (true) {
            world.step(DT, V_ITERATIONS, P_ITERATIONS);
            viewer.repaint();
            try {
                Thread.sleep(dtms);
            } catch (InterruptedException e) {
                log.warning("interrupted");
            }
        }
    }

    private static void buildContainer(World world) {
        BodyDef def = new BodyDef();
        PolygonShape box = new PolygonShape();
        Body side;

        def.position = new Vec2(WIDTH / 2, 0);
        box.setAsBox(THICKNESS / 2, HEIGHT / 2);
        world.createBody(def).createFixture(box, 0f);

        def.position = new Vec2(-WIDTH / 2, 0);
        box.setAsBox(THICKNESS / 2, HEIGHT / 2);
        world.createBody(def).createFixture(box, 0f);

        def.position = new Vec2(0, HEIGHT / 2);
        box.setAsBox(WIDTH / 2, THICKNESS / 2);
        world.createBody(def).createFixture(box, 0f);

        def.position = new Vec2(0, -HEIGHT / 2);
        box.setAsBox(WIDTH / 2, THICKNESS / 2);
        world.createBody(def).createFixture(box, 0f);
    }

    private static void addBall(World world, float x, float y) {
        BodyDef def = new BodyDef();
        def.position = new Vec2(x, y);
        def.type = BodyType.DYNAMIC;
        CircleShape circle = new CircleShape();
        circle.m_radius = 1f;
        FixtureDef mass = new FixtureDef();
        mass.shape = circle;
        mass.density = BALL_DENSITY;
        mass.friction = BALL_FRICTION;
        mass.restitution = BALL_RESTITUTION;
        world.createBody(def).createFixture(mass);
    }
}

@@ -108,16 +108,147 @@
 Random;%0A
+import java.util.concurrent.Executors;%0Aimport java.util.concurrent.ScheduledExecutorService;%0Aimport java.util.concurrent.TimeUnit;%0A
 import j
@@ -266,16 +266,16 @@
 JFrame;%0A
-
 import l
@@ -297,16 +297,35 @@
 va.Log;%0A
+import lombok.val;%0A
 import o
@@ -1497,21 +1497,19 @@
         
-World
+val
  world =
@@ -1543,22 +1543,19 @@
         
-Viewer
+val
  viewer 
@@ -2146,46 +2146,163 @@
     
-final int dtms = (int) (DT * 1000.0);%0A
+val exec = Executors.newSingleThreadScheduledExecutor();%0A        exec.scheduleAtFixedRate(new Runnable() %7B%0A                public void run() %7B%0A            
     
@@ -2328,24 +2328,36 @@
             
+       
+     
 world.step(D
@@ -2388,16 +2388,28 @@
 TIONS);%0A
+            
         
@@ -2446,13 +2446,17 @@
     
-try %7B
+        %7D
 %0A   
@@ -2472,142 +2472,74 @@
     
-Thread.sleep(dtms);%0A            %7D catch (InterruptedException e) %7B%0A                log.warning(%22interrupted%22);%0A            %7D%0A        %7D
+%7D%0A            %7D, 0L, (long) (DT * 1000.0), TimeUnit.MILLISECONDS);
 %0A   

Use an executor instead of a dumb loop.