Skip to content

Instantly share code, notes, and snippets.

@avanderw
Last active December 2, 2020 08:35
Show Gist options
  • Save avanderw/c0b7dc39a539039f0c6b2f0958072a86 to your computer and use it in GitHub Desktop.
Save avanderw/c0b7dc39a539039f0c6b2f0958072a86 to your computer and use it in GitHub Desktop.
Mutable 2D Vector
package net.avdw;
/**
* Single class mutable 2D vector.
*
* @version 2020-12-02 Started versioning
*/
public class Vector2D {
protected static final Double EPSILON = 0.0000001;
protected static final Double EPSILON_SQR = EPSILON * EPSILON;
public double x;
public double y;
/**
* @param args empty, Vector2D, [ x, y ]
*/
public Vector2D(Object... args) {
switch (args.length) {
case 0:
x = 0D;
y = 0D;
break;
case 1:
if (args[0] instanceof Vector2D) {
Vector2D that = (Vector2D) args[0];
this.x = that.x;
this.y = that.y;
break;
}
case 2:
if (args[0] instanceof Number && args[1] instanceof Number) {
x = ((Number) args[0]).doubleValue();
y = ((Number) args[1]).doubleValue();
break;
}
default:
throw new RuntimeException();
}
}
public Vector2D set(Object... args) {
switch (args.length) {
case 1:
if (args[0] instanceof Vector2D) {
Vector2D that = (Vector2D) args[0];
this.x = that.x;
this.y = that.y;
}
break;
case 2:
if (args[0] instanceof Number && args[1] instanceof Number) {
x = ((Number) args[0]).doubleValue();
y = ((Number) args[1]).doubleValue();
}
break;
default:
throw new RuntimeException();
}
return this;
}
public Vector2D add(Object... args) {
switch (args.length) {
case 1:
if (args[0] instanceof Vector2D) {
Vector2D that = (Vector2D) args[0];
this.x += that.x;
this.y += that.y;
}
break;
case 2:
if (args[0] instanceof Number && args[1] instanceof Number) {
x += ((Number) args[0]).doubleValue();
y += ((Number) args[1]).doubleValue();
}
break;
default:
throw new RuntimeException();
}
return this;
}
public Vector2D subtract(Object... args) {
switch (args.length) {
case 1:
if (args[0] instanceof Vector2D) {
Vector2D that = (Vector2D) args[0];
this.x -= that.x;
this.y -= that.y;
}
break;
case 2:
if (args[0] instanceof Number && args[1] instanceof Number) {
x -= ((Number) args[0]).doubleValue();
y -= ((Number) args[1]).doubleValue();
}
break;
default:
throw new RuntimeException();
}
return this;
}
public Vector2D multiply(Object... args) {
switch (args.length) {
case 1:
if (args[0] instanceof Vector2D) {
Vector2D that = (Vector2D) args[0];
this.x *= that.x;
this.y *= that.y;
}
break;
case 2:
if (args[0] instanceof Number && args[1] instanceof Number) {
x *= ((Number) args[0]).doubleValue();
y *= ((Number) args[1]).doubleValue();
}
break;
default:
throw new RuntimeException();
}
return this;
}
public Vector2D divide(Object... args) {
switch (args.length) {
case 1:
if (args[0] instanceof Vector2D) {
Vector2D that = (Vector2D) args[0];
this.x /= that.x;
this.y /= that.y;
}
break;
case 2:
if (args[0] instanceof Number && args[1] instanceof Number) {
x /= ((Number) args[0]).doubleValue();
y /= ((Number) args[1]).doubleValue();
}
break;
default:
throw new RuntimeException();
}
return this;
}
public Vector2D zero() {
x = 0D;
y = 0D;
return this;
}
public Vector2D scale(Double scale) {
return multiply(scale, scale);
}
public Double length() {
return Math.sqrt(x * x + y * y);
}
public Double lengthSqr() {
return x * x + y * y;
}
public Vector2D length(Double length) {
if (isZero()) {
if (Math.abs(length) < EPSILON) {
return this;
} else {
throw new RuntimeException("don't have a direction to scale in");
}
} else {
return scale(length / length());
}
}
public Vector2D normalise() {
return length(1D);
}
public Double distance(Object... args) {
double xd = 0D, yd = 0D;
switch (args.length) {
case 1:
if (args[0] instanceof Vector2D) {
Vector2D that = (Vector2D) args[0];
xd = this.x - that.x;
yd = this.y - that.y;
}
break;
case 2:
if (args[0] instanceof Number && args[1] instanceof Number) {
xd = x - ((Number) args[0]).doubleValue();
yd = y - ((Number) args[1]).doubleValue();
}
break;
default:
throw new RuntimeException();
}
return Math.sqrt(xd * xd + yd * yd);
}
public Double distanceSqr(Object... args) {
double xd = 0D, yd = 0D;
switch (args.length) {
case 1:
if (args[0] instanceof Vector2D) {
Vector2D that = (Vector2D) args[0];
xd = this.x - that.x;
yd = this.y - that.y;
}
break;
case 2:
if (args[0] instanceof Number && args[1] instanceof Number) {
xd = x - ((Number) args[0]).doubleValue();
yd = y - ((Number) args[1]).doubleValue();
}
break;
default:
throw new RuntimeException();
}
return xd * xd + yd * yd;
}
public Boolean equals(Object... args) {
boolean equal = true;
switch (args.length) {
case 1:
if (args[0] instanceof Vector2D) {
Vector2D that = (Vector2D) args[0];
equal = equal && Math.abs(this.x - that.x) > EPSILON;
equal = equal && Math.abs(this.y - that.y) > EPSILON;
}
break;
case 2:
if (args[0] instanceof Number && args[1] instanceof Number) {
equal = equal && Math.abs(x - ((Number) args[0]).doubleValue()) > EPSILON;
equal = equal && Math.abs(y - ((Number) args[1]).doubleValue()) > EPSILON;
}
break;
default:
throw new RuntimeException();
}
return equal;
}
public Boolean isNormalized() {
return Math.abs(length() - 1) < EPSILON;
}
public Boolean isZero() {
return Math.abs(x) < EPSILON && Math.abs(y) < EPSILON;
}
public Boolean isNear(Object... args) {
return distanceSqr(args) < EPSILON_SQR;
}
public Boolean isWithin(Double epsilon, Object... args) {
return distanceSqr(args) < epsilon * epsilon;
}
public Boolean isValid() {
return !Double.isNaN(x) && !Double.isNaN(y) && Double.isFinite(x) && Double.isFinite(y);
}
public Double dot(Object... args) {
double dot = Double.NaN;
switch (args.length) {
case 1:
if (args[0] instanceof Vector2D) {
Vector2D that = (Vector2D) args[0];
dot = this.x * that.x + this.y * that.y;
}
break;
case 2:
if (args[0] instanceof Number && args[1] instanceof Number) {
dot = x * ((Number) args[0]).doubleValue() + y * ((Number) args[1]).doubleValue();
}
break;
default:
throw new RuntimeException();
}
return dot;
}
public Double cross(Object... args) {
double cross = Double.NaN;
switch (args.length) {
case 1:
if (args[0] instanceof Vector2D) {
Vector2D that = (Vector2D) args[0];
cross = this.x * that.y - this.y * that.x;
}
break;
case 2:
if (args[0] instanceof Number && args[1] instanceof Number) {
cross = x * ((Number) args[1]).doubleValue() - y * ((Number) args[0]).doubleValue();
}
break;
default:
throw new RuntimeException();
}
return cross;
}
public Boolean isNormalTo(Object... args) {
return dot(args) < EPSILON;
}
public Double angle() {
double ang = Math.atan2(y, x);
if (ang < 0) {
ang += Math.PI + Math.PI;
}
return ang;
}
public Vector2D angle(Double radians) {
final Double origLength = length();
x = origLength * Math.cos(radians);
y = origLength * Math.sin(radians);
return this;
}
public Double angleBetween(Object... args) {
double angle = Double.NaN;
switch (args.length) {
case 1:
if (args[0] instanceof Vector2D) {
Vector2D that = (Vector2D) args[0];
angle = that.angle() - this.angle();
}
break;
case 2:
if (args[0] instanceof Number && args[1] instanceof Number) {
angle = new Vector2D(((Number) args[0]).doubleValue(), ((Number) args[1]).doubleValue()).angle() - this.angle();
}
break;
default:
throw new RuntimeException();
}
if (angle > Math.PI) {
angle -= 2 * Math.PI;
} else if (angle < -Math.PI) {
angle += 2 * Math.PI;
}
return angle;
}
public Vector2D rotate(Double radians) {
final Double s = Math.sin(radians);
final Double c = Math.cos(radians);
double newX = x * c - y * s;
double newY = x * s + y * c;
x = newX;
y = newY;
return this;
}
public Vector2D normalRight() {
return new Vector2D(-y, x);
}
public Vector2D normalLeft() {
return new Vector2D(y, -x);
}
public Vector2D negate() {
x = -x;
y = -y;
return this;
}
public Vector2D lerp(Double t, Object... args) {
switch (args.length) {
case 1:
if (args[0] instanceof Vector2D) {
Vector2D that = (Vector2D) args[0];
x = x + t * (that.x - x);
y = y + t * (that.y - y);
}
break;
case 2:
if (args[0] instanceof Number && args[1] instanceof Number) {
x = x + t * (((Number) args[0]).doubleValue() - x);
y = y + t * (((Number) args[1]).doubleValue() - y);
}
break;
default:
throw new RuntimeException();
}
return this;
}
public Vector2D slerp(Double t, Object... args) {
final Double cosTheta = dot(args);
final double theta = Math.acos(cosTheta);
final double sinTheta = Math.sin(theta);
final Double w1 = Math.sin((1 - t) * theta) / sinTheta;
final Double w2 = Math.sin(t * theta) / sinTheta;
final Vector2D to = new Vector2D();
to.set(args);
scale(w1).add(to.scale(w2));
return this;
}
public Vector2D reflect(Object... args) {
double d;
switch (args.length) {
case 1:
if (args[0] instanceof Vector2D) {
Vector2D that = (Vector2D) args[0];
d = 2 * (x * that.x + y * that.y);
subtract(d * that.x, d * that.y);
}
break;
case 2:
if (args[0] instanceof Number && args[1] instanceof Number) {
d = 2 * (x * ((Number) args[0]).doubleValue() + y * ((Number) args[1]).doubleValue());
subtract(d * ((Number) args[0]).doubleValue(), d * ((Number) args[1]).doubleValue());
}
break;
default:
throw new RuntimeException();
}
return this;
}
public Vector2D project(Object... args) {
double scalar = Double.NaN;
Vector2D projVector = null;
switch (args.length) {
case 1:
if (args[0] instanceof Vector2D) {
Vector2D that = (Vector2D) args[0];
projVector = that;
scalar = dot(projVector) / projVector.lengthSqr();
}
break;
case 2:
if (args[0] instanceof Number && args[1] instanceof Number) {
projVector = new Vector2D(args[0], args[1]);
scalar = dot(projVector) / projVector.lengthSqr();
}
break;
default:
throw new RuntimeException();
}
set(projVector);
scale(scalar);
return this;
}
public Vector2D offsetPolar(Double radius, Double angle) {
x += radius * Math.cos(angle);
y += radius * Math.sin(angle);
return this;
}
public Vector2D copy() {
return new Vector2D(this);
}
public static void swap(Vector2D a, Vector2D b) {
final Vector2D tmp = a.copy();
a.set(b);
b.set(tmp);
}
@Override
public String toString() {
return "[" + x + "," + y + "]";
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment