My 2D vector class:
public class Vec2D {
public double x;
public double y;
public static Vec2D ZERO = new Vec2D(0, 0);
public static Vec2D X = new Vec2D(1, 0);
public static Vec2D Y = new Vec2D(0, 1);
@Override
public String toString() {
return "[" + x + ", " + y + "]";
}
public Vec2D() {
}
public Vec2D(Vec2D v) {
set(v);
}
public Vec2D(double x, double y) {
this.x = x;
this.y = y;
}
public Vec2D(double... coords) {
this.x = coords[0];
this.y = coords[1];
}
public void rotate(Vec2D center, double angle) {
double cx = center.x;
double cy = center.y;
double diffx = x - cx;
double diffy = y - cy;
x = cx + diffx * Math.cos(angle) - diffy * Math.sin(angle);
y = cy + diffx * Math.sin(angle) + diffy * Math.cos(angle);
}
public Vec2D copy() {
return new Vec2D(x, y);
}
// Returns a new Vec2D in the base b1 and b2 (which must be normalized
// first)
public static Vec2D getNewCoords(Vec2D v, Vec2D b1, Vec2D b2) {
// Project v on both b1 and b2 to get the coordinates
double c1 = v.dot(b1);
double c2 = v.dot(b2);
return new Vec2D(c1, c2);
}
public Vec2D crossWithZ() {
return new Vec2D(-y, x);
}
// Projects this Vector to v
public Vec2D projectCopy(Vec2D v) {
double dot = dot(v);
double vlength = v.length();
return v.multCopy(dot / (vlength * vlength));
}
public double dot(Vec2D v) {
return x * v.x + y * v.y;
}
public static Vec2D center(Vec2D v1, Vec2D v2) {
return new Vec2D(0.5 * (v1.x + v2.x), 0.5 * (v1.y + v2.y));
}
public static Vec2D center(ArrayList<Vec2D> list) {
Vec2D result = new Vec2D();
if (list.size() > 0) {
for (Vec2D v : list) {
result.plus(v);
}
result.mult(1.0 / list.size());
}
return result;
}
public static Vec2D center(Vec2D v1, Vec2D v2, Vec2D v3) {
return new Vec2D((v1.x + v2.x + v3.x) / 3.0, (v1.y + v2.y + v3.y) / 3.0);
}
public Vec2D rotateCopy(double angle) {
Vec2D result = copy();
result.rotate(angle);
return result;
}
public Vec2D rotate(double angle) {
double newX = x * Math.cos(angle) - y * Math.sin(angle);
double newY = x * Math.sin(angle) + y * Math.cos(angle);
x = newX;
y = newY;
return this;
}
public Vec2D multCopy(double val) {
return new Vec2D(x * val, y * val);
}
public Vec2D mult(double val) {
x *= val;
y *= val;
return this;
}
public Vec2D minusCopy(Vec2D v) {
return new Vec2D(x - v.x, y - v.y);
}
public Vec2D plusCopy(Vec2D v) {
return new Vec2D(x + v.x, y + v.y);
}
public Vec2D plus(double x, double y) {
this.x += x;
this.y += y;
return this;
}
public Vec2D plusCopy(double x, double y) {
return copy().plus(x, y);
}
public Vec2D perpendicularCopy() {
return new Vec2D(y, -x);
}
public double angle(Vec2D v) {
return Math.atan2(v.y, v.x) - Math.atan2(y, x);
// return Math.atan2(diffy, diffx);
}
public static double angle(double x1, double y1, double x2, double y2) {
return Math.atan2(y2, x2) - Math.atan2(y1, x1);
// return Math.atan2(diffy, diffx);
}
public static double angle(double x, double y) {
return Math.atan2(y, x);
}
public double angle() {
return Math.atan2(y, x);
}
public double length() {
return Math.sqrt(x * x + y * y);
}
public double sqLength() {
return x * x + y * y;
}
public double getLength() {
return Math.sqrt(x * x + y * y);
}
public double crossValue(Vec2D v) {
return x * v.y - y * v.x;
}
public Vec2D normalizeCopy() {
double l = Math.sqrt(x * x + y * y);
return new Vec2D(x / l, y / l);
}
public Vec2D normalize() {
double l = Math.sqrt(x * x + y * y);
x /= l;
y /= l;
return this;
}
public static double distanceBetween(double x1, double y1, double x2,
double y2) {
double diffx = x1 - x2;
double diffy = y1 - y2;
return Math.sqrt(diffx * diffx + diffy * diffy);
}
public double distanceTo(Vec2D v) {
double diffx = x - v.x;
double diffy = y - v.y;
return Math.sqrt(diffx * diffx + diffy * diffy);
}
public double distanceToSq(double tx, double ty) {
double diffx = x - tx;
double diffy = y - ty;
return diffx * diffx + diffy * diffy;
}
public boolean within(int mx, int my, int width, double zoomx,
double zoomy, double offsetx, double offsety) {
int sx = (int) (zoomx * x - offsetx);
int sy = (int) (zoomy * y - offsety);
return (Math.abs(sx - mx) <= width && Math.abs(sy - my) <= width);
}
public Vec2D translate(double dx, double dy) {
x += dx;
y += dy;
return this;
}
public Vec2D scale(double scalex, double scaley, double centerx,
double centery) {
double diffx = x - centerx;
double diffy = y - centery;
x = centerx + diffx * scalex;
y = centery + diffy * scaley;
return this;
}
public Vec2D scale(double scaleX, double scaleY) {
return scale(scaleX, scaleY, 0.0, 0.0);
}
public Vec2D plus(Vec2D v) {
x += v.x;
y += v.y;
return this;
}
public double distanceTo(double x, double y) {
return distanceTo(new Vec2D(x, y));
}
public Vec2D negateCopy() {
Vec2D result = copy();
return result.negate();
}
public Vec2D negate() {
x = -x;
y = -y;
return this;
}
public void set(Vec2D v) {
this.x = v.x;
this.y = v.y;
}
public void set(double x, double y) {
this.x = x;
this.y = y;
}
public Vec2D minus(Vec2D v) {
x -= v.x;
y -= v.y;
return this;
}
public static double diffAngle(Vec2D v1, Vec2D v2) {
Vec2D v1n = v1.normalizeCopy();
Vec2D v2n = v2.normalizeCopy();
double product = v1n.dot(v2n);
double crossProductZ = v1n.crossValue(v2n);
double result = Math.acos(product);
if (crossProductZ > 0) {
result *= -1;
}
return result;
}
public Vec2D rotateTowards(Vec2D v, double maxAngle) {
double currentAngle = angle(v);
// System.out.println(this + " " + currentAngle);
if (currentAngle > maxAngle) {
currentAngle = maxAngle;
} else if (currentAngle < -maxAngle) {
currentAngle = -maxAngle;
}
rotate(currentAngle);
return this;
}
}
If you want to create a vector between two points, such as the gun position and the mouse position, for animation, you can do like this:
Vec2D gunPos = new Vec2D(gx, gy);
Vec2D mousePos = new Vec2D(mx, my);
Vec2D gunToMouseN = mousePos.minusCopy(gunPos).normalize();
// gunToMouseN is a vector of length 1
double bulletSpeed = ...;
Vec2D bulletVelocity = gunToMouseN.multCopy(bulletSpeed);
// Then, in animation where bulletPos is updated for each step
bulletPos.plus(bulletVelocity);