From b06bf133820ed37db6352b74dadb1fb71874204b Mon Sep 17 00:00:00 2001 From: Timo Hocker Date: Sat, 27 Jun 2020 15:44:16 +0200 Subject: [PATCH] init --- Boid.java | 40 ++++++++++++++++++++++++ ChunkManager.java | 68 +++++++++++++++++++++++++++++++++++++++++ Display.java | 33 ++++++++++++++++++++ Drawable.java | 5 +++ DrawableCollection.java | 3 ++ Main.java | 53 ++++++++++++++++++++++++++++++++ PhysicsObject.java | 49 +++++++++++++++++++++++++++++ 7 files changed, 251 insertions(+) create mode 100644 Boid.java create mode 100644 ChunkManager.java create mode 100644 Display.java create mode 100644 Drawable.java create mode 100644 DrawableCollection.java create mode 100644 Main.java create mode 100644 PhysicsObject.java diff --git a/Boid.java b/Boid.java new file mode 100644 index 0000000..b0a4925 --- /dev/null +++ b/Boid.java @@ -0,0 +1,40 @@ +import java.awt.Graphics; +import java.util.ArrayList; +import java.util.Random; + +public class Boid extends PhysicsObject implements Drawable { + public Boid(int width, int height) { + super(0, 0, 0, 0); + Random r = new Random(); + this.x = r.nextInt(width); + this.y = r.nextInt(height); + this.apply_force(r.nextFloat() * 2 - 1, r.nextFloat() * 2 - 1, 1); + } + + @Override + public void draw(Graphics g) { + g.fillOval((int) x, (int) y, 2, 2); + } + + @Override + public void simulate() { + super.simulate(); + ArrayList others = ChunkManager.get_objects((int) x, (int) y); + + if (others.size() == 0) + return; + + for (PhysicsObject o : others) { + double dist = this.get_sqr_distance(o); + if (dist < 500 && dist > 20) { + // Rule 1: collect in groups + this.apply_force(o.x - x, o.y - y, (float) Math.min(0.005 / dist, 1)); + this.apply_force(o.v_x, o.v_y, 0.005f); + } else if (dist < 20) { + // Rule 2: keep distance + this.apply_force(x - o.x, y - o.y, (float) Math.min(0.005 / dist, 1)); + } + } + + } +} diff --git a/ChunkManager.java b/ChunkManager.java new file mode 100644 index 0000000..cd68d1e --- /dev/null +++ b/ChunkManager.java @@ -0,0 +1,68 @@ +import java.util.ArrayList; + +public class ChunkManager { + private static ArrayList> chunks; + private static int width; + private static int height; + private static int chunks_x; + private static int chunks_y; + + public static void init(int width, int height, int chunks_x, int chunks_y) { + ChunkManager.width = width; + ChunkManager.height = height; + ChunkManager.chunks_x = chunks_x; + ChunkManager.chunks_y = chunks_y; + ChunkManager.chunks = new ArrayList>(chunks_x * chunks_y); + ChunkManager.clear(); + } + + public static void clear() { + chunks.clear(); + for (int i = 0; i < chunks_x * chunks_y; i++) { + chunks.add(new ArrayList()); + } + } + + private static int[] to_chunk_coordinates(int x, int y) { + return new int[] { (x * chunks_x) / width, (y * chunks_y) / height }; + } + + private static int get_chunk_index(int x, int y) { + return x + (y * chunks_x); + } + + private static int wrap(int index, int max) { + int normalized = index; + while (normalized < 0) { + normalized += max; + } + return normalized % max; + } + + public static void register(PhysicsObject obj) { + int[] chunk = to_chunk_coordinates((int)obj.x, (int)obj.y); + int index = get_chunk_index(chunk[0], chunk[1]); + chunks.get(index).add(obj); + } + + public static ArrayList get_objects(int x, int y) { + int[] chunk = to_chunk_coordinates(x, y); + int[][] chunks = new int[][] { + chunk, + new int[] {wrap(chunk[0] + 1,chunks_x), chunk[1]}, + new int[] {wrap(chunk[0] - 1,chunks_x), chunk[1]}, + new int[] {wrap(chunk[0] + 1,chunks_x), wrap(chunk[1] + 1, chunks_y)}, + new int[] {wrap(chunk[0] - 1,chunks_x), wrap(chunk[1] + 1, chunks_y)}, + new int[] {wrap(chunk[0] + 1,chunks_x), wrap(chunk[1] - 1, chunks_y)}, + new int[] {wrap(chunk[0] - 1,chunks_x), wrap(chunk[1] - 1, chunks_y)}, + new int[] {chunk[0], wrap(chunk[1] + 1, chunks_y)}, + new int[] {chunk[0], wrap(chunk[1] - 1, chunks_y)} + }; + ArrayList objects = new ArrayList(); + for (int[] c : chunks) { + int index = get_chunk_index(c[0], c[1]); + objects.addAll(ChunkManager.chunks.get(index)); + } + return objects; + } +} diff --git a/Display.java b/Display.java new file mode 100644 index 0000000..fd403c4 --- /dev/null +++ b/Display.java @@ -0,0 +1,33 @@ +import java.awt.Color; +import java.awt.Graphics; +import java.awt.image.BufferedImage; +import javax.swing.JFrame; + +public class Display extends JFrame { + private static final long serialVersionUID = 8274011568777903027L; + private int width; + private int height; + private DrawableCollection dc; + + public Display(int width, int height, DrawableCollection dc) { + this.width = width; + this.height = height; + this.dc = dc; + this.setSize(width, height); + this.setVisible(true); + this.setDefaultCloseOperation(EXIT_ON_CLOSE); + } + + @Override + public void paint(Graphics g) { + BufferedImage b = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + Graphics bg = b.getGraphics(); + bg.setColor(Color.BLACK); + bg.fillRect(0, 0, width, height); + bg.setColor(Color.WHITE); + for (Drawable d : dc.get_drawables()) { + d.draw(bg); + } + g.drawImage(b,0, 0, width, height, Color.BLACK, this); + } +} diff --git a/Drawable.java b/Drawable.java new file mode 100644 index 0000000..1238731 --- /dev/null +++ b/Drawable.java @@ -0,0 +1,5 @@ +import java.awt.Graphics; + +public interface Drawable { + public void draw(Graphics g); +} diff --git a/DrawableCollection.java b/DrawableCollection.java new file mode 100644 index 0000000..63d4e5a --- /dev/null +++ b/DrawableCollection.java @@ -0,0 +1,3 @@ +public interface DrawableCollection { + public Drawable[] get_drawables(); +} diff --git a/Main.java b/Main.java new file mode 100644 index 0000000..21794da --- /dev/null +++ b/Main.java @@ -0,0 +1,53 @@ +import java.util.Timer; +import java.util.TimerTask; + +public class Main implements DrawableCollection { + private static final int WIDTH = 800; + private static final int HEIGHT = 600; + private static final int BOID_COUNT = 10000; + private static final int CHUNKS_X = 32; + private static final int CHUNKS_Y = 32; + private static final int FRAME_STEP = 16; + private static final float MAX_SPEED = 1; + + private Boid[] boids; + private Display d; + + public static void main(String[] args) throws InterruptedException { + ChunkManager.init(WIDTH, HEIGHT, CHUNKS_X, CHUNKS_Y); + PhysicsObject.width = WIDTH; + PhysicsObject.height = HEIGHT; + PhysicsObject.max_speed = MAX_SPEED; + new Main().run(); + } + + public Main() { + boids = new Boid[BOID_COUNT]; + for (int i = 0; i < BOID_COUNT; i++) { + boids[i] = new Boid(WIDTH, HEIGHT); + } + + d = new Display(WIDTH, HEIGHT, this); + } + + public void run() { + new Timer().scheduleAtFixedRate(new TimerTask() { + @Override + public void run() { + for (Boid b : boids) { + b.simulate(); + } + ChunkManager.clear(); + for (Boid b : boids) { + ChunkManager.register(b); + } + d.repaint(); + } + }, 100, FRAME_STEP); + } + + @Override + public Drawable[] get_drawables() { + return boids; + } +} diff --git a/PhysicsObject.java b/PhysicsObject.java new file mode 100644 index 0000000..f912df1 --- /dev/null +++ b/PhysicsObject.java @@ -0,0 +1,49 @@ +public class PhysicsObject { + public float x; + public float y; + public float v_x; + public float v_y; + + public static int width = 0; + public static int height = 0; + public static float max_speed = 0; + + public void simulate() { + v_x = clamp(v_x, -max_speed, max_speed) * 0.998f; + v_y = clamp(v_y, -max_speed, max_speed) * 0.998f; + + x += v_x; + y += v_y; + while (x < 0) { + x += width; + } + while (y < 0) { + y += height; + } + x %= width; + y %= height; + } + + private float clamp(float f, float min, float max) { + return Math.min(max, Math.max(f, min)); + } + + public void apply_force(float x, float y, float factor) { + if (x == 0 && y == 0) + return; + double len = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)); + v_x += (x / len) * factor; + v_y += (y / len) * factor; + } + + public PhysicsObject(int x, int y, float v_x, float v_y) { + this.x = x; + this.y = y; + this.v_x = v_x; + this.v_y = v_y; + } + + public double get_sqr_distance(PhysicsObject obj) { + return Math.pow(obj.x - x, 2) + Math.pow(obj.y - y, 2); + } +}