From f24ad9bcaf006a1442b756fd2ffaeeb05219a52b Mon Sep 17 00:00:00 2001 From: Lorenz Stechauner Date: Thu, 31 Mar 2022 19:50:10 +0200 Subject: [PATCH] AB3: BodyLinkedList working --- src/Aufgabe3Test.java | 2 +- src/Body.java | 14 ++-- src/BodyLinkedList.java | 157 ++++++++++++++++++++++++++++++------ src/BodyLinkedListItem.java | 33 ++++++++ src/Simulation.java | 2 +- src/Simulation3.java | 64 ++++++++++++++- src/Vector3.java | 31 +++---- 7 files changed, 252 insertions(+), 51 deletions(-) create mode 100644 src/BodyLinkedListItem.java diff --git a/src/Aufgabe3Test.java b/src/Aufgabe3Test.java index 29495b4..b2002a9 100644 --- a/src/Aufgabe3Test.java +++ b/src/Aufgabe3Test.java @@ -78,7 +78,7 @@ public class Aufgabe3Test { testValue(bfm.get(earth).distanceTo(earth.gravitationalForce(sun)), 0); testValue(bfm.get(sun).distanceTo(sun.gravitationalForce(earth).plus(sun.gravitationalForce(venus))), 0); - testValue(bfm.put(earth, new Vector3(0, 0, 0)).distanceTo(earth.gravitationalForce(sun)), 0); + testValue(bfm.get(mercury).distanceTo(mercury.gravitationalForce(sun)), 0); testValue(bfm.get(earth).distanceTo(new Vector3(0, 0, 0)), 0); testValue(bfm.get(mercury), mercury.gravitationalForce(sun)); diff --git a/src/Body.java b/src/Body.java index 22f5fce..1b6685d 100644 --- a/src/Body.java +++ b/src/Body.java @@ -70,16 +70,20 @@ public class Body { return mass; } + public boolean collidesWith(Body body) { + return this.distanceTo(body) < this.radius() + body.radius(); + } + /** * Returns a new body that is formed by the collision of this body and 'b'. The impulse * of the returned body is the sum of the impulses of 'this' and 'b'. */ - public Body merge(Body b) { - double mass = this.mass + b.mass; + public Body merge(Body body) { + double totalMass = this.mass + body.mass; return new Body( - mass, - massCenter.times(this.mass).plus(b.massCenter.times(b.mass)).times(1.0 / mass), - currentMovement.times(this.mass).plus(b.currentMovement.times(b.mass)).times(1.0 / mass) + totalMass, + this.massCenter.times(this.mass).plus(body.massCenter.times(body.mass)).times(1.0 / totalMass), + this.currentMovement.times(this.mass).plus(body.currentMovement.times(body.mass)).times(1.0 / totalMass) ); } diff --git a/src/BodyLinkedList.java b/src/BodyLinkedList.java index 1e0ceef..f949721 100644 --- a/src/BodyLinkedList.java +++ b/src/BodyLinkedList.java @@ -1,16 +1,20 @@ +import java.util.Iterator; + /** * A list of bodies implemented as a linked list. * The number of elements of the list is not limited. */ -public class BodyLinkedList { - - //TODO: declare variables. +public class BodyLinkedList implements Iterable { + private int size = 0; + private BodyLinkedListItem first; + private BodyLinkedListItem last; /** * Initializes 'this' as an empty list. */ public BodyLinkedList() { - //TODO: define constructor. + first = null; + last = null; } /** @@ -20,21 +24,38 @@ public class BodyLinkedList { * Precondition: list != null. */ public BodyLinkedList(BodyLinkedList list) { - //TODO: define constructor. + this.size = 0; + for (Body b : list) { + this.addLast(b); + } } /** * Inserts the specified element 'body' at the beginning of this list. */ public void addFirst(Body body) { - //TODO: implement method. + if (first == null) { + first = new BodyLinkedListItem(body); + last = first; + } else { + first.setPrev(new BodyLinkedListItem(body)); + first = first.prev(); + } + size++; } /** * Appends the specified element 'body' to the end of this list. */ public void addLast(Body body) { - //TODO: implement method. + if (last == null) { + last = new BodyLinkedListItem(body); + first = last; + } else { + last.setNext(new BodyLinkedListItem(body)); + last = last.next(); + } + size++; } /** @@ -42,8 +63,7 @@ public class BodyLinkedList { * Returns 'null' if the list is empty. */ public Body getLast() { - //TODO: implement method. - return null; + return (last != null) ? last.body() : null; } /** @@ -51,8 +71,7 @@ public class BodyLinkedList { * Returns 'null' if the list is empty. */ public Body getFirst() { - //TODO: implement method. - return null; + return (first != null) ? first.body() : null; } /** @@ -60,8 +79,14 @@ public class BodyLinkedList { * Returns 'null' if the list is empty. */ public Body pollFirst() { - //TODO: implement method. - return null; + if (first == null) { + return null; + } + Body b = first.body(); + first = first.next(); + if (first != null) first.setPrev(null); + size--; + return b; } /** @@ -69,8 +94,14 @@ public class BodyLinkedList { * Returns 'null' if the list is empty. */ public Body pollLast() { - //TODO: implement method. - return null; + if (last == null) { + return null; + } + Body b = last.body(); + last = last.prev(); + if (last != null) last.setNext(null); + size--; + return b; } /** @@ -78,7 +109,36 @@ public class BodyLinkedList { * Precondition: i >= 0 && i <= size(). */ public void add(int i, Body body) { - //TODO: implement method. + if (first == null || i == 0) { + addFirst(body); + return; + } else if (i == size) { + addLast(body); + return; + } + + BodyLinkedListItem item = first; + for (int j = 0; j < i; j++) { + item = item.next(); + } + + item.prev().setNext(new BodyLinkedListItem(body)); + item.setPrev(item.prev().next()); + size++; + } + + private Body removeItem(BodyLinkedListItem item) { + if (item == first) { + first = item.next(); + if (first != null) first.setPrev(null); + } else if (item == last) { + last = item.prev(); + if (last != null) last.setNext(null); + } else { + item.next().setPrev(item.prev()); + } + size--; + return item.body(); } /** @@ -86,8 +146,19 @@ public class BodyLinkedList { * Precondition: i >= 0 && i < size(). */ public Body get(int i) { - //TODO: implement method. - return null; + BodyLinkedListItem item; + if (i < size / 2) { + item = first; + for (int j = 0; j < i; j++) { + item = item.next(); + } + } else { + item = last; + for (int j = size - 1; j > i; j--) { + item = item.prev(); + } + } + return item.body(); } /** @@ -95,8 +166,19 @@ public class BodyLinkedList { * this list does not contain the element. */ public int indexOf(Body body) { - //TODO: implement method. - return -2; + if (first == null) { + return -1; + } + + BodyLinkedListItem item = first; + for (int i = 0; i < size; i++) { + if (item.body() == body) { + return i; + } + item = item.next(); + } + + return -1; } /** @@ -104,15 +186,42 @@ public class BodyLinkedList { * body. Returns a list with all the removed bodies. */ public BodyLinkedList removeCollidingWith(Body body) { - //TODO: implement method. - return null; + BodyLinkedList removed = new BodyLinkedList(); + for (BodyLinkedListItem item = first; item != null; item = item.next()) { + if (body != item.body() && body.collidesWith(item.body())) { + removed.addLast(this.removeItem(item)); + } + } + return removed; } /** * Returns the number of bodies in this list. */ public int size() { - //TODO: implement method. - return -1; + return size; + } + + @Override + public Iterator iterator() { + return new Iterator<>() { + BodyLinkedListItem ptr = first; + boolean yieldedFirst = false; + + @Override + public boolean hasNext() { + return ptr != null && (!yieldedFirst || ptr.next() != null); + } + + @Override + public Body next() { + if (!yieldedFirst) { + yieldedFirst = true; + return ptr.body(); + } + ptr = ptr.next(); + return ptr.body(); + } + }; } } diff --git a/src/BodyLinkedListItem.java b/src/BodyLinkedListItem.java new file mode 100644 index 0000000..36e42a3 --- /dev/null +++ b/src/BodyLinkedListItem.java @@ -0,0 +1,33 @@ +class BodyLinkedListItem { + private final Body body; + private BodyLinkedListItem prev; + private BodyLinkedListItem next; + + public BodyLinkedListItem(Body body) { + this.body = body; + this.prev = null; + this.next = null; + } + + public Body body() { + return body; + } + + public BodyLinkedListItem prev() { + return prev; + } + + public void setPrev(BodyLinkedListItem prev) { + this.prev = prev; + if (prev != null) prev.next = this; + } + + public BodyLinkedListItem next() { + return next; + } + + public void setNext(BodyLinkedListItem next) { + this.next = next; + if (next != null) next.prev = this; + } +} diff --git a/src/Simulation.java b/src/Simulation.java index 4fd770f..9a60df9 100644 --- a/src/Simulation.java +++ b/src/Simulation.java @@ -81,7 +81,7 @@ public class Simulation { // merge bodies that have collided for (int i = 0; i < bodies.length; i++) { for (int j = i + 1; j < bodies.length; j++) { - if (bodies[j].distanceTo(bodies[i]) < bodies[j].radius() + bodies[i].radius()) { + if (bodies[j].collidesWith(bodies[i])) { bodies[i] = bodies[i].merge(bodies[j]); Body[] bodiesOneRemoved = new Body[bodies.length - 1]; for (int k = 0; k < bodiesOneRemoved.length; k++) { diff --git a/src/Simulation3.java b/src/Simulation3.java index b5d1fad..36d6378 100644 --- a/src/Simulation3.java +++ b/src/Simulation3.java @@ -1,3 +1,8 @@ +import codedraw.CodeDraw; + +import java.awt.*; +import java.util.Random; + /** * Simulates the formation of a massive solar system. */ @@ -7,6 +12,63 @@ public class Simulation3 { * The main simulation method using instances of other classes. */ public static void main(String[] args) { - //TODO: change implementation of this method according to 'Aufgabenblatt3.md'. + CodeDraw cd = new CodeDraw(); + BodyLinkedList bodies = new BodyLinkedList(); + BodyForceMap forceOnBody = new BodyForceMap(); + + Random random = new Random(2022); + + for (int i = 0; i < Simulation.NUMBER_OF_BODIES; i++) { + bodies.addLast(new Body( + Math.abs(random.nextGaussian()) * Simulation.OVERALL_SYSTEM_MASS / Simulation.NUMBER_OF_BODIES, + new Vector3( + 0.2 * random.nextGaussian() * Simulation.AU, + 0.2 * random.nextGaussian() * Simulation.AU, + 0.2 * random.nextGaussian() * Simulation.AU + ), + new Vector3( + 0 + random.nextGaussian() * 5e3, + 0 + random.nextGaussian() * 5e3, + 0 + random.nextGaussian() * 5e3 + ) + )); + } + + long seconds = 0; + while (true) { + seconds++; + + BodyLinkedList mergedBodies = new BodyLinkedList(); + for (Body b1 : bodies) { + BodyLinkedList colliding = bodies.removeCollidingWith(b1); + for (Body b2 : colliding) { + b1 = b1.merge(b2); + } + mergedBodies.addLast(b1); + } + bodies = mergedBodies; + + for (Body b1 : bodies) { + Vector3 force = new Vector3(); + for (Body b2 : bodies) { + if (b1 != b2) { + force = force.plus(b1.gravitationalForce(b2)); + } + } + forceOnBody.put(b1, force); + } + + for (Body body : bodies) { + body.move(forceOnBody.get(body)); + } + + if ((seconds % 3600) == 0) { + cd.clear(Color.BLACK); + for (Body body : bodies) { + body.draw(cd); + } + cd.show(); + } + } } } diff --git a/src/Vector3.java b/src/Vector3.java index 6f59323..93a55e8 100644 --- a/src/Vector3.java +++ b/src/Vector3.java @@ -4,7 +4,6 @@ import codedraw.CodeDraw; * This class represents vectors in a 3D vector space. */ public class Vector3 { - private double x; private double y; private double z; @@ -27,33 +26,21 @@ public class Vector3 { * Returns the sum of this vector and vector 'v'. */ public Vector3 plus(Vector3 v) { - Vector3 result = new Vector3(); - result.x = x + v.x; - result.y = y + v.y; - result.z = z + v.z; - return result; + return new Vector3(x + v.x, y + v.y, z + v.z); } /** * Returns the product of this vector and 'd'. */ public Vector3 times(double d) { - Vector3 result = new Vector3(); - result.x = x * d; - result.y = y * d; - result.z = z * d; - return result; + return new Vector3(x * d, y * d, z * d); } /** * Returns the sum of this vector and -1*v. */ public Vector3 minus(Vector3 v) { - Vector3 result = new Vector3(); - result.x = x - v.x; - result.y = y - v.y; - result.z = z - v.z; - return result; + return new Vector3(x - v.x, y - v.y, z - v.z); } /** @@ -85,15 +72,21 @@ public class Vector3 { z /= length; } + public double getScreenX(CodeDraw cd) { + return cd.getWidth() * (this.x + Simulation.SECTION_SIZE / 2) / Simulation.SECTION_SIZE; + } + + public double getScreenY(CodeDraw cd) { + return cd.getWidth() * (this.y + Simulation.SECTION_SIZE / 2) / Simulation.SECTION_SIZE; + } + /** * Draws a filled circle with a specified radius centered at the (x,y) coordinates of this vector * in the canvas associated with 'cd'. The z-coordinate is not used. */ public void drawAsFilledCircle(CodeDraw cd, double radius) { - double x = cd.getWidth() * (this.x + Simulation.SECTION_SIZE / 2) / Simulation.SECTION_SIZE; - double y = cd.getWidth() * (this.y + Simulation.SECTION_SIZE / 2) / Simulation.SECTION_SIZE; radius = cd.getWidth() * radius / Simulation.SECTION_SIZE; - cd.fillCircle(x, y, Math.max(radius, 1.5)); + cd.fillCircle(getScreenX(cd), getScreenY(cd), Math.max(radius, 1.5)); } /**