Created
December 2, 2020 08:36
-
-
Save avanderw/2a8ee8f479fff0d31c8eb97b83f898bc to your computer and use it in GitHub Desktop.
Single class immutable 2D vector.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package net.avdw; | |
/** | |
* Single class immutable 2D vector. | |
* | |
* @version 2020-12-02 Copied from Vector2D | |
*/ | |
public class Vector2DConst { | |
protected static final Double EPSILON = 0.0000001; | |
protected static final Double EPSILON_SQR = EPSILON * EPSILON; | |
public final double x; | |
public final double y; | |
/** | |
* @param args empty, Vector2DConst, [ x, y ] | |
*/ | |
public Vector2DConst(Object... args) { | |
switch (args.length) { | |
case 0: | |
x = 0D; | |
y = 0D; | |
break; | |
case 1: | |
if (args[0] instanceof Vector2DConst) { | |
Vector2DConst that = (Vector2DConst) 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 Vector2DConst add(Object... args) { | |
Vector2DConst newVector = null; | |
switch (args.length) { | |
case 1: | |
if (args[0] instanceof Vector2DConst) { | |
Vector2DConst that = (Vector2DConst) args[0]; | |
newVector = new Vector2DConst(this.x + that.x, this.y + that.y); | |
} | |
break; | |
case 2: | |
if (args[0] instanceof Number && args[1] instanceof Number) { | |
newVector = new Vector2DConst(this.x + ((Number) args[0]).doubleValue(), this.y + ((Number) args[1]).doubleValue()); | |
} | |
break; | |
default: | |
throw new RuntimeException(); | |
} | |
return newVector; | |
} | |
public Vector2DConst subtract(Object... args) { | |
Vector2DConst newVector = null; | |
switch (args.length) { | |
case 1: | |
if (args[0] instanceof Vector2DConst) { | |
Vector2DConst that = (Vector2DConst) args[0]; | |
newVector = new Vector2DConst(this.x - that.x, this.y - that.y); | |
} | |
break; | |
case 2: | |
if (args[0] instanceof Number && args[1] instanceof Number) { | |
newVector = new Vector2DConst(this.x - ((Number) args[0]).doubleValue(), this.y - ((Number) args[1]).doubleValue()); | |
} | |
break; | |
default: | |
throw new RuntimeException(); | |
} | |
return newVector; | |
} | |
public Vector2DConst multiply(Object... args) { | |
Vector2DConst newVector = null; | |
switch (args.length) { | |
case 1: | |
if (args[0] instanceof Vector2DConst) { | |
Vector2DConst that = (Vector2DConst) args[0]; | |
newVector = new Vector2DConst(this.x * that.x, this.y * that.y); | |
} | |
break; | |
case 2: | |
if (args[0] instanceof Number && args[1] instanceof Number) { | |
newVector = new Vector2DConst(this.x * ((Number) args[0]).doubleValue(), this.y * ((Number) args[1]).doubleValue()); | |
} | |
break; | |
default: | |
throw new RuntimeException(); | |
} | |
return newVector; | |
} | |
public Vector2DConst divide(Object... args) { | |
Vector2DConst newVector = null; | |
switch (args.length) { | |
case 1: | |
if (args[0] instanceof Vector2DConst) { | |
Vector2DConst that = (Vector2DConst) args[0]; | |
newVector = new Vector2DConst(this.x / that.x, this.y / that.y); | |
} | |
break; | |
case 2: | |
if (args[0] instanceof Number && args[1] instanceof Number) { | |
newVector = new Vector2DConst(this.x / ((Number) args[0]).doubleValue(), this.y / ((Number) args[1]).doubleValue()); | |
} | |
break; | |
default: | |
throw new RuntimeException(); | |
} | |
return newVector; | |
} | |
public Vector2DConst 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 Vector2DConst 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 Vector2DConst normalise() { | |
return length(1D); | |
} | |
public Double distance(Object... args) { | |
double xd = 0D, yd = 0D; | |
switch (args.length) { | |
case 1: | |
if (args[0] instanceof Vector2DConst) { | |
Vector2DConst that = (Vector2DConst) 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 Vector2DConst) { | |
Vector2DConst that = (Vector2DConst) 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 Vector2DConst) { | |
Vector2DConst that = (Vector2DConst) 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 Vector2DConst) { | |
Vector2DConst that = (Vector2DConst) 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 Vector2DConst) { | |
Vector2DConst that = (Vector2DConst) 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 Vector2DConst angle(Double radians) { | |
final Double len = length(); | |
return new Vector2DConst(len * Math.cos(radians), len * Math.sin(radians)); | |
} | |
public Double angleBetween(Object... args) { | |
double angle = Double.NaN; | |
switch (args.length) { | |
case 1: | |
if (args[0] instanceof Vector2DConst) { | |
Vector2DConst that = (Vector2DConst) args[0]; | |
angle = that.angle() - this.angle(); | |
} | |
break; | |
case 2: | |
if (args[0] instanceof Number && args[1] instanceof Number) { | |
angle = new Vector2DConst(((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 Vector2DConst 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; | |
return new Vector2DConst(newX, newY); | |
} | |
public Vector2DConst normalRight() { | |
return new Vector2DConst(-y, x); | |
} | |
public Vector2DConst normalLeft() { | |
return new Vector2DConst(y, -x); | |
} | |
public Vector2DConst negate() { | |
return new Vector2DConst(-x, -y); | |
} | |
public Vector2DConst lerp(Double t, Object... args) { | |
Vector2DConst newVector = null; | |
switch (args.length) { | |
case 1: | |
if (args[0] instanceof Vector2DConst) { | |
Vector2DConst that = (Vector2DConst) args[0]; | |
newVector = new Vector2DConst(x + t * (that.x - x), y + t * (that.y - y)); | |
} | |
break; | |
case 2: | |
if (args[0] instanceof Number && args[1] instanceof Number) { | |
newVector = new Vector2DConst(x + t * (((Number) args[0]).doubleValue() - x), y + t * (((Number) args[1]).doubleValue() - y)); | |
} | |
break; | |
default: | |
throw new RuntimeException(); | |
} | |
return newVector; | |
} | |
public Vector2DConst 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 Vector2DConst to = new Vector2DConst(args); | |
return scale(w1).add(to.scale(w2)); | |
} | |
public Vector2DConst reflect(Object... args) { | |
Vector2DConst newVector = null; | |
double d; | |
switch (args.length) { | |
case 1: | |
if (args[0] instanceof Vector2DConst) { | |
Vector2DConst that = (Vector2DConst) args[0]; | |
d = 2 * (x * that.x + y * that.y); | |
newVector = 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()); | |
newVector = subtract(d * ((Number) args[0]).doubleValue(), d * ((Number) args[1]).doubleValue()); | |
} | |
break; | |
default: | |
throw new RuntimeException(); | |
} | |
return newVector; | |
} | |
public Vector2DConst project(Object... args) { | |
double scalar = Double.NaN; | |
Vector2DConst projVector = null; | |
switch (args.length) { | |
case 1: | |
if (args[0] instanceof Vector2DConst) { | |
projVector = (Vector2DConst) args[0]; | |
scalar = dot(projVector) / projVector.lengthSqr(); | |
} | |
break; | |
case 2: | |
if (args[0] instanceof Number && args[1] instanceof Number) { | |
projVector = new Vector2DConst(args[0], args[1]); | |
scalar = dot(projVector) / projVector.lengthSqr(); | |
} | |
break; | |
default: | |
throw new RuntimeException(); | |
} | |
return projVector.scale(scalar); | |
} | |
public Vector2DConst offsetPolar(Double radius, Double angle) { | |
return new Vector2DConst(x + radius * Math.cos(angle), y + radius * Math.sin(angle)); | |
} | |
public Vector2DConst copy() { | |
return new Vector2DConst(this); | |
} | |
@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