diff --git a/.classpath b/.classpath
new file mode 100644
index 0000000..2a6f40e
--- /dev/null
+++ b/.classpath
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ae3c172
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+/bin/
diff --git a/.project b/.project
new file mode 100644
index 0000000..0080706
--- /dev/null
+++ b/.project
@@ -0,0 +1,17 @@
+
+
+ Octree
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..021167a
--- /dev/null
+++ b/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,12 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=9
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=9
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.release=enabled
+org.eclipse.jdt.core.compiler.source=9
diff --git a/src/Main.java b/src/Main.java
new file mode 100644
index 0000000..2e73a17
--- /dev/null
+++ b/src/Main.java
@@ -0,0 +1,126 @@
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import Octree.Octree;
+import Octree.OctreeNode;
+import Octree.OctreeObject;
+import boid.Boid;
+import core.Boundary;
+import processing.core.PApplet;
+import processing.event.KeyEvent;
+import processing.event.MouseEvent;
+
+public class Main extends PApplet {
+
+ public static void main(String[] args) {
+ PApplet.main("Main");
+ }
+
+ Octree world = new Octree(1800, 900, 3, 5);
+ ArrayList walls = new ArrayList<>();
+
+ int boids = 60;
+
+ public void settings() {
+ size(world.getWidth(), world.getHeight());
+ }
+
+ public void setup() {
+
+ for (int i = 0; i < boids; i++) {
+ world.add(new Boid(200, 200, world, this));
+ }
+
+ walls.add(new Boundary(0, 0, world.getWidth(), 0));
+ walls.add(new Boundary(0, 0, 0, world.getHeight()));
+ walls.add(new Boundary(world.getWidth(), 0, world.getWidth(), world.getHeight()));
+ walls.add(new Boundary(0, world.getHeight(), world.getWidth(), world.getHeight()));
+
+ }
+
+ public void draw() {
+
+ if (paused)
+ return;
+
+ clear();
+ background(255);
+
+ ArrayList nodes = new ArrayList<>();
+ nodes.add(world);
+ nodes.addAll(world.getAllChildren());
+
+ for (OctreeNode node : nodes) {
+ int x = node.getX();
+ int y = node.getY();
+ int width = node.getWidth();
+ int height = node.getHeight();
+
+ line(x, y, x + width, y);
+ line(x + width, y, x + width, y + height);
+ line(x + width, y + height, x, y + height);
+ line(x, y + height, x, y);
+
+ }
+
+ for (Boundary wall : walls) {
+ line((int) wall.a.x, (int) wall.a.y, (int) wall.b.x, (int) wall.b.y);
+ }
+
+ ArrayList objects = world.getAllObjects();
+
+ for (OctreeObject object : objects) {
+ Boid boid = ((Boid) object);
+
+ boid.update();
+
+ line((int) boid.getX(), (int) boid.getY(), (int) (boid.getX() + boid.velocity.x * 10),
+ (int) (boid.getY() + boid.velocity.y * 10));
+
+
+
+ fill(((Boid)object).color);
+ circle((int) object.getX(), (int) object.getY(), boidSize);
+
+ }
+
+ }
+
+ boolean paused = false;
+
+ @Override
+ public void keyReleased(KeyEvent event) {
+ switch (event.getKeyCode()) {
+ case 32:
+ paused = !paused;
+ break;
+ }
+ ;
+ }
+
+ Boid selectedBoid;
+
+ int boidSize = 10;
+
+ @Override
+ public void mouseReleased(MouseEvent event) {
+ int mouseX = event.getX();
+ int mouseY = event.getY();
+
+ System.out.println(mouseX + " " + mouseY);
+ for (OctreeObject obj : world.getAllObjects()) {
+ Boid boid = (Boid) obj;
+
+ double x = boid.getX(), y = boid.getY();
+
+ line((int) x, (int) y, (int) (x + boidSize), (int) (y + boidSize));
+ if (mouseX >= x && mouseX < x + boidSize && mouseY >= y && mouseY < y + boidSize)
+ boid.drawDebug = true;
+ else
+ boid.drawDebug = false;
+
+ }
+
+ }
+}
diff --git a/src/Octree/Octree.java b/src/Octree/Octree.java
new file mode 100644
index 0000000..f102255
--- /dev/null
+++ b/src/Octree/Octree.java
@@ -0,0 +1,25 @@
+package Octree;
+
+public class Octree extends OctreeNode {
+
+ public int maxNodeSize, maxNodes, minWidth, minHeight;
+
+ public Octree(int width, int height, int maxNodeSize, int maxNodes, int minWidth, int minHeight) {
+ super(0, 0, width, height, null, null);
+ this.octree = this;
+ this.maxNodeSize = maxNodeSize;
+ this.maxNodes = maxNodes;
+ this.minWidth = minWidth;
+ this.minHeight = minHeight;
+ }
+
+ public Octree(int width, int height, int maxNodeSize, int maxNodes) {
+ super(0, 0, width, height, null, null);
+ this.octree = this;
+ this.maxNodeSize = maxNodeSize;
+ this.maxNodes = maxNodes;
+ this.minWidth = 10;
+ this.minHeight = 10;
+ }
+
+}
diff --git a/src/Octree/OctreeNode.java b/src/Octree/OctreeNode.java
new file mode 100644
index 0000000..ce059d7
--- /dev/null
+++ b/src/Octree/OctreeNode.java
@@ -0,0 +1,188 @@
+package Octree;
+
+import java.util.ArrayList;
+
+public class OctreeNode {
+
+ private int x, y, width, height, distanceToTop = 0;
+
+ Octree octree;
+
+ private OctreeNode parent;
+
+ public OctreeNode(int x, int y, int width, int height, Octree octree, OctreeNode parent) {
+ this.x = x;
+ this.y = y;
+ this.width = width;
+ this.height = height;
+ this.octree = octree;
+ this.parent = parent;
+
+ if (parent != null) // means its the octree
+ distanceToTop++;
+
+ }
+
+ private ArrayList objects = new ArrayList<>();
+
+ private ArrayList children = new ArrayList<>();
+
+ public void add(OctreeObject obj) {
+ octree.addFull(obj);
+ }
+
+ void addFull(OctreeObject obj) {
+ obj.setOctree(octree);
+
+ OctreeNode node = getQuadrant(obj);
+
+ //means no children (this node)
+ if (node == null) {
+ objects.add(obj);
+ obj.setOctreeNode(this);
+ obj.setOctreeParentNode(parent);
+ } else {
+ node.addFull(obj);
+ }
+
+ if (!hasChildren() && objects.size() >= octree.maxNodeSize && distanceToTop < octree.maxNodes
+ && this.width > octree.minWidth && this.height > octree.minHeight) {
+ children = newNodes(x, y, width, height, octree, this);
+
+ for (OctreeObject object : objects) {
+ OctreeNode temp = getQuadrant(object);
+ temp.addFull(object);
+ }
+
+ objects.clear();
+
+ }
+
+ }
+
+ public void remove(OctreeObject object) {
+ if (hasChildren())
+ for (OctreeNode octreeNode : children) {
+ octreeNode.remove(object);
+ }
+ else
+ objects.remove(object);
+
+ if (hasChildren() && objects.size() < octree.maxNodeSize) {
+
+ objects.addAll(getAllObjects());
+
+ children.clear();
+ }
+ }
+
+ private OctreeNode getQuadrant(OctreeObject object) {
+
+ if (!hasChildren())
+ return null;
+
+ for (OctreeNode octreeNode : children) {
+
+ if (object.getX() >= octreeNode.getX() && object.getY() >= octreeNode.getY()
+ && object.getX() <= octreeNode.getX() + octreeNode.getWidth()
+ && object.getY() <= octreeNode.getY() + octreeNode.getHeight())
+ return octreeNode;
+ }
+
+ // maybe problematisch
+ // if (parent != null)
+ // return parent.getQuadrant(object);
+
+ return children.get(0);
+ }
+
+
+
+ public boolean hasChildren() {
+ return !children.isEmpty();
+ }
+
+ public int getX() {
+ return x;
+ }
+
+ public int getY() {
+ return y;
+ }
+
+ public int getWidth() {
+ return width;
+ }
+
+ public int getHeight() {
+ return height;
+ }
+
+ public Octree getOctree() {
+ return octree;
+ }
+
+ public OctreeNode getParent() {
+ return parent;
+ }
+
+ public ArrayList getObjects() {
+ return (ArrayList) objects.clone();
+ }
+
+ public ArrayList getAllObjects() {
+
+ ArrayList c = getObjects();
+
+ for (OctreeNode octreeNode : getChildren()) {
+ c.addAll(octreeNode.getAllObjects());
+ }
+
+ return c;
+ }
+
+ public ArrayList getChildren() {
+ return (ArrayList) children.clone();
+ }
+
+ public ArrayList getAllChildren() {
+
+ ArrayList c = getChildren();
+
+ for (OctreeNode octreeNode : getChildren()) {
+ c.addAll(octreeNode.getAllChildren());
+ }
+
+ return c;
+ }
+
+ public int size() {
+ return objects.size();
+ }
+
+ private static ArrayList newNodes(int parenX, int parentY, int parentWidth, int parentHeight,
+ Octree octree, OctreeNode parent) {
+ ArrayList nodes = new ArrayList<>();
+
+ int width = parentWidth / 2;
+ int height = parentHeight / 2;
+
+ nodes.add(new OctreeNode(parenX, parentY, width, height, octree, parent));
+ nodes.add(new OctreeNode(parenX + width, parentY, width, height, octree, parent));
+ nodes.add(new OctreeNode(parenX, parentY + height, width, height, octree, parent));
+ nodes.add(new OctreeNode(parenX + width, parentY + height, width, height, octree, parent));
+
+ return nodes;
+
+ }
+
+ @Override
+ public String toString() {
+ String s = this.x + " " + this.y + " " + this.width + " " + this.height + " [ allChildren:"
+ + getAllChildren().size() + ", objects:" + getObjects().size() + ", allObjects:"
+ + getAllObjects().size() + " ]";
+
+ return s;
+ }
+
+}
diff --git a/src/Octree/OctreeObject.java b/src/Octree/OctreeObject.java
new file mode 100644
index 0000000..dc68d8f
--- /dev/null
+++ b/src/Octree/OctreeObject.java
@@ -0,0 +1,85 @@
+package Octree;
+
+public class OctreeObject {
+
+ private double x = 0, y = 0;
+ private OctreeNode octreeNode, OctreeParentNode;
+ private Octree octree;
+
+
+
+ public double getX() {
+ return x;
+ };
+
+ public double getY() {
+ return y;
+ };
+
+ public void setPosition(int x, int y) {
+ setX(x);
+ setY(y);
+ };
+
+ public void setX(double x) {
+ if (octree != null)
+ this.x = (x <= 0) ? 0 : ((x > octree.getWidth()) ? octree.getWidth() : x);
+ else
+ this.x = x;
+ updateTree();
+ }
+
+ public void setY(double y) {
+ if (octree != null)
+ this.y = (y <= 0) ? 0 : ((y > octree.getHeight()) ? octree.getHeight() : y);
+ else
+ this.y = y;
+ updateTree();
+ }
+
+ public void setPosition(double x, double y) {
+ setX(x);
+ setY(y);
+ };
+
+ public void setX(int x) {
+ setX((double) x);
+ }
+
+ public void setY(int y) {
+ setY((double) y);
+ }
+
+ public OctreeNode getOctreeNode() {
+ return octreeNode;
+ }
+
+ public OctreeNode getOctreeParentNode() {
+ return OctreeParentNode;
+ }
+
+ void setOctreeNode(OctreeNode octreeNode) {
+ this.octreeNode = octreeNode;
+ }
+
+ void setOctreeParentNode(OctreeNode octreeParentNode) {
+ OctreeParentNode = octreeParentNode;
+ }
+
+ void setOctree(Octree octree) {
+ this.octree = octree;
+ }
+
+ private void updateTree() {
+ if (octree == null)
+ return;
+ octree.remove(this);
+ octree.add(this);
+ }
+
+ @Override
+ public String toString() {
+ return getX()+ " "+ getY();
+ }
+
+}
diff --git a/src/boid/Boid.java b/src/boid/Boid.java
new file mode 100644
index 0000000..9a0ed43
--- /dev/null
+++ b/src/boid/Boid.java
@@ -0,0 +1,215 @@
+package boid;
+
+import java.util.ArrayList;
+
+import Octree.Octree;
+import Octree.OctreeObject;
+import core.Boundary;
+import core.Position;
+import core.Vector;
+import processing.core.PApplet;
+
+public class Boid extends OctreeObject {
+
+ public Vector velocity = new Vector(1, 1);
+ public Vector direction = new Vector(3, 0);
+
+ public static int COLOR_RED = 16711680;
+ public static int COLOR_GREEN = 65280;
+ public static int COLOR_BLUE = 255;
+
+
+
+ private PApplet p5;
+
+ public Boid(int x, int y, Octree world, PApplet p5) {
+ setPosition(x, y);
+ this.world = world;
+ this.p5 = p5;
+ }
+
+ public Octree world;
+
+ public int color = 16777215;
+
+ public boolean drawDebug = false;
+
+ public static double edgeValue = 100;
+
+ public static double turnFactor = 0.3;
+ public static double avoidFactor = 0.05;
+ public static double matchingFactor = 0.05;
+ public static double centeringFactor = 0.00005;
+ public static double minSpeed = 3, maxSpeed = 8;
+ public static double protectedRange = 8;
+ public static double visibleRange = 40;
+
+ public static double randomMovementAmount = 0.01;
+
+ public void update() {
+
+ double boidX = getX(), boidY = getY();
+
+ updateBoidBehavoir(boidX, boidY);
+
+ randomMovement();
+ min_maxSpeed();
+ walls(boidX, boidY);
+
+ setX(getX() + velocity.x);
+ setY(getY() + velocity.y);
+
+ if(drawDebug) {
+ p5.noFill();
+ p5.circle((int) getX(), (int) getY(), (int) protectedRange);
+ p5.circle((int) getX(), (int) getY(), (int) visibleRange);
+ p5.fill(255);
+ }
+
+ }
+
+ private void min_maxSpeed() {
+ double speed = Math.sqrt(velocity.x * velocity.x + velocity.y * velocity.y);
+
+ if (speed > maxSpeed) {
+ velocity.x = (velocity.x / speed) * maxSpeed;
+ velocity.y = (velocity.y / speed) * maxSpeed;
+ }
+ if (speed < minSpeed) {
+ velocity.x = (velocity.x / speed) * minSpeed;
+ velocity.y = (velocity.y / speed) * minSpeed;
+ }
+ }
+
+ private void randomMovement() {
+ double rand = Math.random();
+ if (rand > 0.5)
+ velocity.x = velocity.x + randomMovementAmount * rand;
+ else
+ velocity.y = velocity.y + randomMovementAmount * rand;
+
+ }
+
+ private void updateBoidBehavoir(double boidX, double boidY) {
+ xvel_avg = 0;
+ yvel_avg = 0;
+ xpos_avg = 0;
+ ypos_avg = 0;
+
+ neighboring_boids = 0;
+ otherBoid_dx = 0;
+ otherBoid_dy = 0;
+
+ for (OctreeObject ob : world.getAllObjects()) {
+ Boid otherBoid = (Boid) ob;
+
+ double dist = new Vector(new Position(boidX, boidY), new Position(otherBoid.getX(), otherBoid.getY()))
+ .getLength();
+
+ if (dist < protectedRange) {
+
+ otherBoid.color=COLOR_RED;
+
+ // seperation
+ otherBoid_dx += boidX - otherBoid.getX();
+ otherBoid_dy += boidY - otherBoid.getY();
+ }
+
+ if (dist < visibleRange && dist > protectedRange) {
+
+ otherBoid.color=COLOR_GREEN;
+
+ // cohesion
+ xpos_avg += boidX - otherBoid.getX();
+ ypos_avg += boidY - otherBoid.getY();
+
+ // allignment
+ xvel_avg = xvel_avg + otherBoid.velocity.x;
+ yvel_avg = yvel_avg + otherBoid.velocity.y;
+
+ neighboring_boids++;
+
+ }
+ }
+
+
+ endBehavoirUpdate();
+
+ }
+
+ private void endBehavoirUpdate() {
+
+ if (neighboring_boids > 0) {
+
+ // cohesion
+ xpos_avg = xpos_avg / neighboring_boids;
+ ypos_avg = ypos_avg / neighboring_boids;
+
+ // alignment
+ xvel_avg = xvel_avg / neighboring_boids;
+ yvel_avg = yvel_avg / neighboring_boids;
+ }
+
+ // cohesion
+ velocity.x += (xpos_avg - velocity.x) * centeringFactor;
+ velocity.y += (ypos_avg - velocity.y) * centeringFactor;
+
+ // alignment
+ velocity.x += (xvel_avg - velocity.x) * matchingFactor;
+ velocity.y += (yvel_avg - velocity.y) * matchingFactor;
+
+ // seperation
+ velocity.x = velocity.x + otherBoid_dx * avoidFactor;
+ velocity.y = velocity.y + otherBoid_dy * avoidFactor;
+ }
+
+ double xvel_avg = 0, yvel_avg = 0, neighboring_boids = 0;
+ double otherBoid_dx = 0, otherBoid_dy = 0, xpos_avg = 0, ypos_avg = 0;
+
+ private void walls(double boidX, double boidY) {
+ if (boidX < edgeValue)
+ velocity.x = velocity.x + turnFactor;
+ if (boidX > world.getWidth() - edgeValue)
+ velocity.x = velocity.x - turnFactor;
+ if (boidY < edgeValue)
+ velocity.y = velocity.y + turnFactor;
+ if (boidY > world.getHeight() - edgeValue)
+ velocity.y = velocity.y - turnFactor;
+
+ }
+
+ private double wallForceStart = 150, maxBoundaryForce = 1, wfs = maxBoundaryForce / wallForceStart;
+
+ public Boundary hitBoundary(ArrayList walls) {
+
+ Position position = new Position(getX(), getY());
+
+ Boundary hit = null;
+
+ for (Boundary boundary : walls) {
+
+ // double l = (y3 * x4+ x1* y4 - x3* y4 - y1*x4)/ ( y2 *x4 - x2* y4)
+ double l = (boundary.a.y * boundary.direction.x + position.x * boundary.direction.y
+ - boundary.a.x * boundary.direction.y - position.y * boundary.direction.x)
+ / (direction.y * boundary.direction.x - direction.x * boundary.direction.y);
+
+ double dirLength = direction.getLength();
+
+ if (l < dirLength)
+ if (dirLength >= 0 && l >= 0 || dirLength <= 0 && l <= 0) {
+
+ double sx = (position.x + l * direction.x - boundary.a.x) / boundary.direction.x;
+ double sy = (position.y + l * direction.y - boundary.a.y) / boundary.direction.y;
+
+ if (sx >= 0 || sy >= 0)
+ if (sx <= boundary.direction.getLength() || sy <= boundary.direction.getLength()) {
+ dirLength = l;
+ hit = boundary;
+ }
+ }
+ }
+
+ return hit;
+ }
+
+}
diff --git a/src/core/Boundary.java b/src/core/Boundary.java
new file mode 100644
index 0000000..c2911ba
--- /dev/null
+++ b/src/core/Boundary.java
@@ -0,0 +1,26 @@
+package core;
+
+public class Boundary {
+
+ public Position a, b;
+ public Vector direction;
+
+ public Boundary(Position a, Position b) {
+ this.a = a;
+ this.b = b;
+ this.direction = new Vector(a, b);
+ }
+
+ public Boundary(double x, double y, double x2, double y2) {
+ this.a = new Position(x, y);
+ this.b = new Position(x2, y2);
+ this.direction = new Vector(a, b);
+ }
+
+ public Boundary(Position a, Vector direction) {
+ this.a = a;
+ this.b = direction.pointFrom(a);
+ this.direction = direction;
+ }
+
+}
diff --git a/src/core/NormalizedVector.java b/src/core/NormalizedVector.java
new file mode 100644
index 0000000..1fd3a30
--- /dev/null
+++ b/src/core/NormalizedVector.java
@@ -0,0 +1,125 @@
+package core;
+
+public class NormalizedVector {
+
+ public double x, y;
+ public double lenght;
+
+ public NormalizedVector(double x, double y) {
+ this.x = x;
+ this.y = y;
+ normalize();
+ }
+
+ public NormalizedVector(Position position) {
+ this.x = position.getX();
+ this.y = position.getY();
+ normalize();
+ }
+
+ public NormalizedVector(Position a, Position b) {
+ this.x = b.getX() - a.getX();
+ this.y = b.getY() - a.getY();
+ normalize();
+
+ }
+
+ public NormalizedVector fromLength(double lenght) {
+ return new NormalizedVector(x * lenght, y * lenght);
+ }
+
+ public Position pointFrom(Position a) {
+ return new Position(a.x + x * lenght, a.y + y * lenght);
+ }
+
+ public NormalizedVector subtract(NormalizedVector b) {
+ return new NormalizedVector(getX() - b.getX(), getY() - b.getY());
+ }
+
+ public NormalizedVector add(NormalizedVector b) {
+ return new NormalizedVector(getX() + b.getX(), getY() + b.getY());
+ }
+
+ public NormalizedVector multiply(double b) {
+ return new NormalizedVector(getX() * b, getY() * b);
+ }
+
+ public NormalizedVector divide(double b) {
+ return new NormalizedVector(getX() / b, getY() / b);
+ }
+
+ public void subtractME(NormalizedVector b) {
+ x = getX() - b.getX();
+ y = getY() - b.getY();
+ normalize();
+ }
+
+ public void addME(NormalizedVector b) {
+ x = getX() + b.getX();
+ y = getY() + b.getY();
+ normalize();
+ }
+
+ public void multiplyME(double b) {
+ x = getX() * b;
+ y = getY() * b;
+ normalize();
+ }
+
+ public void divideME(double b) {
+ x = getX() / b;
+ y = getY() / b;
+ normalize();
+ }
+
+ public void normalize() {
+
+ lenght = Math.abs(Math.sqrt(x * x + y * y));
+
+ if (lenght == 0)
+ return;
+
+ x = x / lenght;
+ y = y / lenght;
+
+ }
+
+ public double getX() {
+ return (x * lenght);
+ }
+
+ public void setX(double x) {
+ this.x = x;
+ y = y * lenght;
+
+ normalize();
+ }
+
+ public double getY() {
+ return (y * lenght);
+ }
+
+ public void setY(double y) {
+ this.y = y;
+
+ x = x * lenght;
+
+ normalize();
+ }
+
+ public void clear() {
+ this.x = 0;
+ this.y = 0;
+ this.lenght = 0;
+ }
+
+ public NormalizedVector clone() {
+ return new NormalizedVector(x * lenght, y * lenght);
+ }
+
+ @Override
+ public String toString() {
+ return "[" + this.x + "," + this.y + "->" + this.lenght + "]";
+ }
+
+}
diff --git a/src/core/Position.java b/src/core/Position.java
new file mode 100644
index 0000000..79a0c63
--- /dev/null
+++ b/src/core/Position.java
@@ -0,0 +1,30 @@
+package core;
+
+public class Position {
+
+ public Position(double x, double y) {
+ this.x = x;
+ this.y = y;
+ }
+
+ public double x,y;
+
+ public double getX() {
+ return x;
+ }
+
+ public void setX(double x) {
+ this.x = x;
+ }
+
+ public double getY() {
+ return y;
+ }
+
+ public void setY(double y) {
+ this.y = y;
+ }
+
+
+
+}
diff --git a/src/core/Vector.java b/src/core/Vector.java
new file mode 100644
index 0000000..5018abe
--- /dev/null
+++ b/src/core/Vector.java
@@ -0,0 +1,90 @@
+package core;
+
+public class Vector {
+
+ public double x, y;
+
+ public Vector(double x, double y) {
+ this.x = x;
+ this.y = y;
+ }
+
+ public Vector(Position position) {
+ this.x = position.x;
+ this.y = position.y;
+ }
+
+ public Vector(Position a, Position b) {
+ this.x = b.x - a.x;
+ this.y = b.y - a.y;
+ }
+
+ public Vector fromLength(double lenght) {
+ return new Vector(x, y);
+ }
+
+ public Position pointFrom(Position a) {
+ return new Position(a.x + x, a.y + y);
+ }
+
+ public Vector subtract(Vector b) {
+ return new Vector(x - b.x, y - b.y);
+ }
+
+ public Vector add(Vector b) {
+ return new Vector(x + b.x, y + b.y);
+ }
+
+ public Vector multiply(double b) {
+ return new Vector(x * b, y * b);
+ }
+
+ public Vector divide(double b) {
+ return new Vector(x / b, y / b);
+ }
+
+ public void subtractME(Vector b) {
+ x = x - b.x;
+ y = y - b.y;
+ }
+
+ public void addME(Vector b) {
+ x = x + b.x;
+ y = y + b.y;
+ }
+
+ public void multiplyME(double b) {
+ x = x * b;
+ y = y * b;
+ }
+
+ public void divideME(double b) {
+ x = x / b;
+ y = y / b;
+ }
+
+ public double getLength() {
+ return Math.abs(Math.sqrt(x * x + y * y));
+ }
+
+
+
+ public NormalizedVector normalize() {
+ return new NormalizedVector(x, y);
+ }
+
+ public void clear() {
+ this.x = 0;
+ this.y = 0;
+ }
+
+ public Vector clone() {
+ return new Vector(x, y);
+ }
+
+ @Override
+ public String toString() {
+ return "[" + this.x + "," + this.y + "->" + getLength() + "]";
+ }
+
+}