Not working
This commit is contained in:
126
src/Body.java
Normal file
126
src/Body.java
Normal file
@ -0,0 +1,126 @@
|
||||
import codedraw.CodeDraw;
|
||||
|
||||
/**
|
||||
* This class represents celestial bodies like stars, planets, asteroids, etc...
|
||||
*/
|
||||
public class Body {
|
||||
private final double mass;
|
||||
private Vector massCenter; // position of the mass center.
|
||||
private Vector currentMovement;
|
||||
|
||||
public Body(double mass, Vector massCenter, Vector currentMovement) {
|
||||
this.mass = mass;
|
||||
this.massCenter = massCenter;
|
||||
this.currentMovement = currentMovement;
|
||||
}
|
||||
|
||||
public Body(double mass, Vector massCenter) {
|
||||
this(mass, massCenter, new Vector());
|
||||
}
|
||||
|
||||
public Body(Body other) {
|
||||
this(other.mass, new Vector(other.massCenter), new Vector(other.currentMovement));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the distance between the mass centers of this body and the specified body 'b'.
|
||||
*/
|
||||
public double distanceTo(Body b) {
|
||||
return massCenter.distanceTo(b.massCenter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a vector representing the gravitational force exerted by 'b' on this body.
|
||||
* The gravitational Force F is calculated by F = G*(m1*m2)/(r*r), with m1 and m2 being the
|
||||
* masses of the objects interacting, r being the distance between the centers of the masses
|
||||
* and G being the gravitational constant.
|
||||
* Hint: see simulation loop in Simulation.java to find out how this is done.
|
||||
*/
|
||||
public Vector gravitationalForce(Body b) {
|
||||
Vector direction = b.massCenter.minus(massCenter);
|
||||
double distance = direction.length();
|
||||
direction.normalize();
|
||||
double force = Simulation.G * mass * b.mass / (distance * distance);
|
||||
return direction.times(force);
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves this body to a new position, according to the specified force vector 'force' exerted
|
||||
* on it, and updates the current movement accordingly.
|
||||
* (Movement depends on the mass of this body, its current movement and the exerted force.)
|
||||
* Hint: see simulation loop in Simulation.java to find out how this is done.
|
||||
*/
|
||||
public void move(Vector force) {
|
||||
// F = m*a -> a = F/m
|
||||
Vector newPosition = massCenter.plus(force.times(1.0 / mass)).plus(currentMovement);
|
||||
|
||||
// new minus old position.
|
||||
Vector newMovement = newPosition.minus(massCenter);
|
||||
|
||||
// update body state
|
||||
massCenter = newPosition;
|
||||
currentMovement = newMovement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the approximate radius of this body.
|
||||
* (It is assumed that the radius r is related to the mass m of the body by r = m ^ 0.5,
|
||||
* where m and r measured in solar units.)
|
||||
*/
|
||||
public double radius() {
|
||||
return SpaceDraw.massToRadius(mass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mass of the Body.
|
||||
*/
|
||||
public double mass() {
|
||||
return mass;
|
||||
}
|
||||
|
||||
public Vector massCenter() {
|
||||
return massCenter;
|
||||
}
|
||||
|
||||
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 body) {
|
||||
double totalMass = this.mass + body.mass;
|
||||
return new Body(
|
||||
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)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the body to the specified canvas as a filled circle.
|
||||
* The radius of the circle corresponds to the radius of the body
|
||||
* (use a conversion of the real scale to the scale of the canvas as
|
||||
* in 'Simulation.java').
|
||||
* Hint: call the method 'drawAsFilledCircle' implemented in 'Vector'.
|
||||
*/
|
||||
public void draw(CodeDraw cd) {
|
||||
cd.setColor(SpaceDraw.massToColor(mass));
|
||||
massCenter.drawAsFilledCircle(cd, SpaceDraw.massToRadius(mass));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string with the information about this body including
|
||||
* mass, position (mass center) and current movement. Example:
|
||||
* "5.972E24 kg, position: [1.48E11,0.0,0.0] m, movement: [0.0,29290.0,0.0] m/s."
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format(
|
||||
"%g kg, position: %s m, movement: %s m/s.",
|
||||
mass, massCenter.toString(), currentMovement.toString()
|
||||
);
|
||||
}
|
||||
}
|
167
src/Octree.java
Normal file
167
src/Octree.java
Normal file
@ -0,0 +1,167 @@
|
||||
|
||||
/**
|
||||
* + -> >= 0
|
||||
* - -> < 0
|
||||
* 0 -> x+ y+ z+
|
||||
* 1 -> x- y+ z+
|
||||
* 2 -> x+ y- z+
|
||||
* 3 -> x- y- z+
|
||||
* 4 -> x+ y+ z-
|
||||
* 5 -> x- y+ z-
|
||||
* 6 -> x+ y- z-
|
||||
* 7 -> x- y- z-
|
||||
*/
|
||||
public class Octree {
|
||||
private OctreeItem root;
|
||||
private final Vector center;
|
||||
private final double size;
|
||||
|
||||
public Octree(double size) {
|
||||
this(new Vector(), size);
|
||||
}
|
||||
|
||||
public Octree(Vector center, double size) {
|
||||
this.center = center;
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public void add(Body body) {
|
||||
if (root == null) {
|
||||
root = new OctreeLeaf(center, size, body);
|
||||
} else {
|
||||
root = root.add(body);
|
||||
}
|
||||
}
|
||||
|
||||
public void applyForces(Body[] bodies, double t) {
|
||||
if (root == null) return;
|
||||
root.preCalc();
|
||||
Vector[] forces = new Vector[bodies.length];
|
||||
for (int i = 0; i < bodies.length; i++) {
|
||||
forces[i] = root.getForcesOnBody(bodies[i], t);
|
||||
}
|
||||
for (int i = 0; i < bodies.length; i++) {
|
||||
bodies[i].move(forces[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class OctreeItem {
|
||||
protected Vector center;
|
||||
protected double size;
|
||||
|
||||
protected Body pseudoBody;
|
||||
|
||||
protected OctreeItem(Vector center, double size) {
|
||||
this.center = center;
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
abstract protected OctreeNode add(Body body);
|
||||
|
||||
abstract protected void preCalc();
|
||||
|
||||
abstract protected Vector getForcesOnBody(Body body, double t);
|
||||
}
|
||||
|
||||
class OctreeNode extends OctreeItem {
|
||||
private final OctreeItem[] children = new OctreeItem[8];;
|
||||
|
||||
protected OctreeNode(Vector center, double size) {
|
||||
super(center, size);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected OctreeNode add(Body body) {
|
||||
int num = getOctantNum(body.massCenter());
|
||||
if (num < 0) return this;
|
||||
OctreeItem oct = children[num];
|
||||
if (oct == null) {
|
||||
children[num] = new OctreeLeaf(getOctantCenter(num), size / 2.0, body);
|
||||
} else {
|
||||
children[num] = oct.add(body);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void preCalc() {
|
||||
double mass = 0;
|
||||
for (OctreeItem oct : children) {
|
||||
if (oct == null) continue;
|
||||
oct.preCalc();
|
||||
mass += oct.pseudoBody.mass();
|
||||
}
|
||||
Vector massCenter = new Vector();
|
||||
for (OctreeItem oct : children) {
|
||||
if (oct == null) continue;
|
||||
massCenter = massCenter.plus(oct.pseudoBody.massCenter().times(oct.pseudoBody.mass() / mass));
|
||||
}
|
||||
this.pseudoBody = new Body(mass, massCenter);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Vector getForcesOnBody(Body body, double t) {
|
||||
double r = pseudoBody.massCenter().distanceTo(body.massCenter());
|
||||
if (r == 0) {
|
||||
return new Vector();
|
||||
} else if (size / r < t) {
|
||||
return body.gravitationalForce(pseudoBody);
|
||||
}
|
||||
Vector force = new Vector();
|
||||
for (OctreeItem child : children) {
|
||||
if (child == null) continue;
|
||||
force = force.plus(child.getForcesOnBody(body, t));
|
||||
}
|
||||
return force;
|
||||
}
|
||||
|
||||
private int getOctantNum(Vector bodyPos) {
|
||||
Vector pos = bodyPos.minus(this.center);
|
||||
if (!isInOctant(bodyPos)) return -1;
|
||||
return ((pos.getX() < 0) ? 1 : 0) | ((pos.getY() < 0) ? 2 : 0) | ((pos.getZ() < 0) ? 4 : 0);
|
||||
}
|
||||
|
||||
private boolean isInOctant(Vector pos) {
|
||||
Vector p1 = center.plus(new Vector(size / 2));
|
||||
Vector p2 = center.minus(new Vector(size / 2));
|
||||
return pos.getX() <= p1.getX() && pos.getY() <= p1.getY() && pos.getZ() <= p1.getZ() &&
|
||||
pos.getX() >= p2.getX() && pos.getY() >= p2.getY() && pos.getZ() >= p2.getZ();
|
||||
}
|
||||
|
||||
private Vector getOctantCenter(int octNum) {
|
||||
return this.center.plus(new Vector(
|
||||
((octNum & 1) != 0) ? -0.5 : 0.5,
|
||||
((octNum & 2) != 0) ? -0.5 : 0.5,
|
||||
((octNum & 4) != 0) ? -0.5 : 0.5)
|
||||
.times(this.size));
|
||||
}
|
||||
}
|
||||
|
||||
class OctreeLeaf extends OctreeItem {
|
||||
private final Body body;
|
||||
|
||||
public OctreeLeaf(Vector center, double size, Body body) {
|
||||
super(center, size);
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected OctreeNode add(Body body) {
|
||||
OctreeNode replace = new OctreeNode(this.center, this.size);
|
||||
replace.add(this.body);
|
||||
replace.add(body);
|
||||
return replace;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void preCalc() {
|
||||
System.out.println(this.body);
|
||||
this.pseudoBody = new Body(this.body);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Vector getForcesOnBody(Body body, double t) {
|
||||
return body.gravitationalForce(pseudoBody);
|
||||
}
|
||||
}
|
@ -1,8 +1,80 @@
|
||||
import codedraw.CodeDraw;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.Random;
|
||||
|
||||
public class Simulation {
|
||||
|
||||
// gravitational constant
|
||||
public static final double G = 6.6743e-11;
|
||||
|
||||
// one astronomical unit (AU) is the average distance of earth to the sun.
|
||||
public static final double AU = 150e9; // meters
|
||||
|
||||
// one light year
|
||||
public static final double LY = 9.461e15; // meters
|
||||
|
||||
// some further constants needed in the simulation
|
||||
public static final double SUN_MASS = 1.989e30; // kilograms
|
||||
public static final double SUN_RADIUS = 696340e3; // meters
|
||||
public static final double EARTH_MASS = 5.972e24; // kilograms
|
||||
public static final double EARTH_RADIUS = 6371e3; // meters
|
||||
|
||||
// set some system parameters
|
||||
public static final double SECTION_SIZE = 2 * AU; // the size of the square region in space
|
||||
public static final int NUMBER_OF_BODIES = 22;
|
||||
public static final double OVERALL_SYSTEM_MASS = 20 * SUN_MASS; // kilograms
|
||||
|
||||
public static void main(String[] args) {
|
||||
//TODO: please use this class to run your simulation
|
||||
CodeDraw cd = new CodeDraw();
|
||||
Body[] bodies = new Body[NUMBER_OF_BODIES];
|
||||
|
||||
Random random = new Random(2022);
|
||||
|
||||
for (int i = 0; i < Simulation.NUMBER_OF_BODIES; i++) {
|
||||
bodies[i] = new Body(
|
||||
Math.abs(random.nextGaussian()) * Simulation.OVERALL_SYSTEM_MASS / Simulation.NUMBER_OF_BODIES,
|
||||
new Vector(
|
||||
0.2 * random.nextGaussian() * Simulation.AU,
|
||||
0.2 * random.nextGaussian() * Simulation.AU,
|
||||
0.2 * random.nextGaussian() * Simulation.AU
|
||||
),
|
||||
new Vector(
|
||||
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;
|
||||
*/
|
||||
Octree octree = new Octree(new Vector(AU), SECTION_SIZE);
|
||||
for (Body body : bodies) {
|
||||
octree.add(body);
|
||||
}
|
||||
octree.applyForces(bodies, 1);
|
||||
|
||||
if ((seconds % 3600) == 0) {
|
||||
cd.clear(Color.BLACK);
|
||||
for (Body body : bodies) {
|
||||
body.draw(cd);
|
||||
}
|
||||
cd.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
63
src/SpaceDraw.java
Normal file
63
src/SpaceDraw.java
Normal file
@ -0,0 +1,63 @@
|
||||
import java.awt.*;
|
||||
|
||||
public class SpaceDraw {
|
||||
|
||||
/**
|
||||
* Returns the approximate radius of a celestial body with the specified mass.
|
||||
* (It is assumed that the radius r is related to the mass m of the body by r = m ^ 0.5,
|
||||
* where m and r measured in solar units.)
|
||||
*/
|
||||
public static double massToRadius(double mass) {
|
||||
return Simulation.SUN_RADIUS * (Math.pow(mass / Simulation.SUN_MASS, 0.5));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the approximate color of a celestial body with the specified mass. The color of
|
||||
* the body corresponds to the temperature of the body, assuming the relation of mass and
|
||||
* temperature of a main sequence star.
|
||||
*/
|
||||
public static Color massToColor(double mass) {
|
||||
Color color;
|
||||
if (mass < Simulation.SUN_MASS / 10) {
|
||||
// not a star-like body below this mass
|
||||
color = Color.LIGHT_GRAY;
|
||||
} else {
|
||||
// assume a main sequence star
|
||||
color = SpaceDraw.kelvinToColor((int) (5500 * mass / Simulation.SUN_MASS));
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the approximate color of temperature 'kelvin'.
|
||||
*/
|
||||
private static Color kelvinToColor(int kelvin) {
|
||||
double k = kelvin / 100D;
|
||||
double red = k <= 66 ? 255 : 329.698727446 * Math.pow(k - 60, -0.1332047592);
|
||||
double green = k <= 66 ? 99.4708025861 * Math.log(k) - 161.1195681661 : 288.1221695283 * Math.pow(k - 60, -0.0755148492);
|
||||
double blue = k >= 66 ? 255 : (k <= 19 ? 0 : 138.5177312231 * Math.log(k - 10) - 305.0447927307);
|
||||
|
||||
return new Color(
|
||||
limitAndDarken(red, kelvin),
|
||||
limitAndDarken(green, kelvin),
|
||||
limitAndDarken(blue, kelvin)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* A transformation used in the method 'kelvinToColor'.
|
||||
*/
|
||||
private static int limitAndDarken(double color, int kelvin) {
|
||||
int kelvinNorm = kelvin - 373;
|
||||
if (color < 0 || kelvinNorm < 0) {
|
||||
return 0;
|
||||
} else if (color > 255) {
|
||||
return 255;
|
||||
} else if (kelvinNorm < 500) {
|
||||
return (int) ((color / 256D) * (kelvinNorm / 500D) * 256);
|
||||
} else {
|
||||
return (int) color;
|
||||
}
|
||||
}
|
||||
}
|
125
src/Vector.java
Normal file
125
src/Vector.java
Normal file
@ -0,0 +1,125 @@
|
||||
import codedraw.CodeDraw;
|
||||
|
||||
/**
|
||||
* This class represents vectors in a 3D vector space.
|
||||
*/
|
||||
public class Vector {
|
||||
private double x;
|
||||
private double y;
|
||||
private double z;
|
||||
|
||||
public Vector() {
|
||||
this(0);
|
||||
}
|
||||
|
||||
public Vector(double v) {
|
||||
this(v, v, v);
|
||||
}
|
||||
|
||||
public Vector(double x, double y, double z) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
public Vector(Vector other) {
|
||||
this(other.x, other.y, other.z);
|
||||
}
|
||||
|
||||
public double getX() {
|
||||
return this.x;
|
||||
}
|
||||
|
||||
public double getY() {
|
||||
return this.y;
|
||||
}
|
||||
|
||||
public double getZ() {
|
||||
return this.z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sum of this vector and vector 'v'.
|
||||
*/
|
||||
public Vector plus(Vector v) {
|
||||
return new Vector(x + v.x, y + v.y, z + v.z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the product of this vector and 'd'.
|
||||
*/
|
||||
public Vector times(double d) {
|
||||
return new Vector(x * d, y * d, z * d);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sum of this vector and -1*v.
|
||||
*/
|
||||
public Vector minus(Vector v) {
|
||||
return new Vector(x - v.x, y - v.y, z - v.z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Euclidean distance of this vector
|
||||
* to the specified vector 'v'.
|
||||
*/
|
||||
public double distanceTo(Vector v) {
|
||||
double dX = x - v.x;
|
||||
double dY = y - v.y;
|
||||
double dZ = z - v.z;
|
||||
return Math.sqrt(dX * dX + dY * dY + dZ * dZ);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the length (norm) of this vector.
|
||||
*/
|
||||
public double length() {
|
||||
return distanceTo(new Vector());
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes this vector: changes the length of this vector such that it becomes 1.
|
||||
* The direction and orientation of the vector is not affected.
|
||||
*/
|
||||
public void normalize() {
|
||||
double length = length();
|
||||
x /= length;
|
||||
y /= length;
|
||||
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) {
|
||||
radius = cd.getWidth() * radius / Simulation.SECTION_SIZE;
|
||||
cd.fillCircle(getScreenX(cd), getScreenY(cd), Math.max(radius, 1.5));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the coordinates of this vector in brackets as a string
|
||||
* in the form "[x,y,z]", e.g., "[1.48E11,0.0,0.0]".
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("[%g,%g,%g]", x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (other.getClass() != Vector.class) {
|
||||
return false;
|
||||
}
|
||||
Vector v = (Vector) other;
|
||||
return this.x == v.x && this.y == v.y && this.z == v.z;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user