/
Launcher.java
118 lines (102 loc) · 4.08 KB
/
Launcher.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
package liquid;
import java.awt.geom.Rectangle2D;
import java.lang.InterruptedException;
import java.util.Random;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.swing.JFrame;
import lombok.extern.java.Log;
import lombok.val;
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 {
/* Solver */
private static final float DT = 1f / 30f; // seconds
private static final int V_ITERATIONS = 8;
private static final int P_ITERATIONS = 3;
/* World */
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, -20f);
private static final Rectangle2D VIEW =
new Rectangle2D.Float(WIDTH / -2, HEIGHT / -2, WIDTH, HEIGHT);
private static final long FLIP_RATE = 5000L;
/* Balls */
private static final int BALLS = 150;
private static final float BALL_RADIUS = 0.75f;
private static final float BALL_DENSITY = 1f;
private static final float BALL_FRICTION = 0.1f;
private static final float BALL_RESTITUTION = 0.4f;
public static void main(String[] args) {
/* Fix for poor OpenJDK performance. */
System.setProperty("sun.java2d.pmoffscreen", "false");
val world = new World(GRAVITY, false);
val 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 - BALL_RADIUS),
(rng.nextFloat() - 0.5f) * (HEIGHT - BALL_RADIUS));
}
val exec = Executors.newSingleThreadScheduledExecutor();
exec.scheduleAtFixedRate(new Runnable() {
public void run() {
world.step(DT, V_ITERATIONS, P_ITERATIONS);
viewer.repaint();
if (System.currentTimeMillis() / FLIP_RATE % 2 == 0) {
world.setGravity(GRAVITY.negate());
} else {
world.setGravity(GRAVITY);
}
}
}, 0L, (long) (DT * 1000.0), TimeUnit.MILLISECONDS);
}
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 = BALL_RADIUS;
FixtureDef mass = new FixtureDef();
mass.shape = circle;
mass.density = BALL_DENSITY;
mass.friction = BALL_FRICTION;
mass.restitution = BALL_RESTITUTION;
world.createBody(def).createFixture(mass);
}
}