Created
November 25, 2019 09:27
-
-
Save bartwttewaall/4d0bcf58c0b67b85e7fa6ccc101def30 to your computer and use it in GitHub Desktop.
Vector2 class with many handy methods. Needs to be written as TypeScript some day
This file contains hidden or 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
export default class Vector2 { | |
constructor(x, y) { | |
this.x = x || 0; | |
this.y = y || 0; | |
} | |
// ---- public instance methods ---- | |
get name() { | |
const v = this.clone().normalize(); | |
if (v.equals(Vector2.UP)) return "UP"; | |
if (v.equals(Vector2.DOWN)) return "DOWN"; | |
if (v.equals(Vector2.LEFT)) return "LEFT"; | |
if (v.equals(Vector2.RIGHT)) return "RIGHT"; | |
return v.toString(); | |
} | |
set(vector) { | |
this.x = vector.x; | |
this.y = vector.y; | |
return this; | |
} | |
setValues(x, y) { | |
this.x = x; | |
this.y = y; | |
Vector2.fix(this); | |
return this; | |
} | |
add(vector) { | |
this.x += vector.x; | |
this.y += vector.y; | |
return this; | |
} | |
addScalar(val) { | |
this.x += val; | |
this.y += val; | |
return this; | |
} | |
subtract(vector) { | |
this.x -= vector.x; | |
this.y -= vector.y; | |
return this; | |
} | |
subtractScalar(val) { | |
this.x -= val; | |
this.y -= val; | |
return this; | |
} | |
multiply(vector) { | |
this.x *= vector.x; | |
this.y *= vector.y; | |
return this; | |
} | |
multiplyScalar(val) { | |
this.x *= val; | |
this.y *= val; | |
return this; | |
} | |
divide(vector) { | |
this.x /= vector.x; | |
this.y /= vector.y; | |
return this; | |
} | |
divideScalar(val) { | |
this.x /= val; | |
this.y /= val; | |
return this; | |
} | |
copy(vector) { | |
this.x = vector.x; | |
this.y = vector.y; | |
return this; | |
} | |
clone() { | |
return new Vector2(this.x, this.y); | |
} | |
// ---- rotation ---- | |
// horizontal angle | |
angle() { | |
return Math.atan2(this.y, this.x); | |
} | |
// vertical angle | |
angle2() { | |
return Math.atan2(this.x, this.y); | |
} | |
sameAngleRange(vector, range) { | |
range = !isNaN(range) ? range : Math.PI / 2; | |
return Math.abs(this.angle() + Math.PI - (vector.angle() + Math.PI)) <= range; | |
} | |
rotate(angle) { | |
const cos = Math.cos(angle); | |
const sin = Math.sin(angle); | |
return this.setValues(this.x * cos - this.y * sin, this.x * sin + this.y * cos); | |
} | |
rotateBy(angle) { | |
return this.rotate(this.angle() + angle); | |
} | |
angleTo(vector) { | |
return Math.atan2(vector.y - this.y, vector.x - this.x); | |
} | |
rotateAround(point, angle) { | |
return this.subtract(point) | |
.rotate(angle) | |
.add(point); | |
} | |
// ---- | |
magnitude() { | |
return Math.sqrt(this.x * this.x + this.y * this.y); | |
} | |
distanceTo(vector) { | |
return Math.sqrt((vector.x - this.x) * (vector.x - this.x) + (vector.y - this.y) * (vector.y - this.y)); | |
} | |
invert() { | |
return this.setValues(-this.x, -this.y); | |
} | |
dot(vector) { | |
return this.x * vector.x + this.y * vector.y; | |
} | |
cross(vector) { | |
return this.x * vector.y - this.y * vector.x; | |
} | |
normalize() { | |
if (!this.isZero()) { | |
const m = this.magnitude(); | |
this.x /= m; | |
this.y /= m; | |
} | |
return this; | |
} | |
projectOnto(vector) { | |
const coeff = (this.x * vector.x + this.y * vector.y) / (vector.x * vector.x + vector.y * vector.y); | |
return this.setValues(coeff * vector.x, coeff * vector.y); | |
} | |
round() { | |
this.x = Math.round(this.x); | |
this.y = Math.round(this.y); | |
return this; | |
} | |
toPerp(normalized) { | |
const tx = this.x; | |
this.x = -this.y; | |
this.y = tx; | |
return normalized === true ? this.normalize() : this; | |
} | |
// the perpendicular vector is a 90 deg CCW rotated vector | |
getPerp(normalized) { | |
const p = new Vector2(-this.y, this.x); | |
return normalized === true ? p.normalize() : p; | |
} | |
lerp(vector, f) { | |
this.x += (vector.x - this.x) * f; | |
this.y += (vector.y - this.y) * f; | |
return this; | |
} | |
// ---- validation ---- | |
equals(vector) { | |
return vector.x === this.x && vector.y === this.y; | |
} | |
equalsNormalized(vector) { | |
const v1 = this.clone().normalize(); | |
const v2 = vector.clone().normalize(); | |
return v2.x === v1.x && v2.y === v1.y; | |
} | |
isZero() { | |
return this.x === 0 && this.y === 0; | |
} | |
// ---- output ---- | |
toString() { | |
return "x:" + this.x + ", y:" + this.y; | |
} | |
toObject() { | |
return { x: this.x, y: this.y }; | |
} | |
toArray() { | |
return [this.x, this.y]; | |
} | |
} | |
Vector2.UP = new Vector2(0, -1); | |
Vector2.DOWN = new Vector2(0, 1); | |
Vector2.LEFT = new Vector2(-1, 0); | |
Vector2.RIGHT = new Vector2(1, 0); | |
Vector2.DEG2RAD = Math.PI / 180; | |
Vector2.RAD2DEG = 180 / Math.PI; | |
Vector2.RANGE = 1e-12; | |
Vector2.zero = function() { | |
return new Vector2(0, 0); | |
}; | |
Vector2.from = function(object) { | |
if (object instanceof Vector2) return new Vector2(object.x, object.y); | |
if (object.hasOwnProperty("x") && object.hasOwnProperty("y")) return new Vector2(object.x, object.y); | |
if (Array.isArray(object)) return Vector2.fromArray(object); | |
throw new Error("Cannot intantiate a new Vector2 from object:", object); | |
}; | |
Vector2.fromArray = function(array) { | |
if (Array.isArray(array)) return new Vector2(array[0], array[1]); | |
throw new Error("Cannot intantiate a new Vector2 from array:", array); | |
}; | |
Vector2.angleBetween = function(v1, v2) { | |
return v2.angle() - v1.angle(); | |
}; | |
Vector2.lerp = function(v1, v2, f) { | |
return new Vector2((v2.x - v1.x) * f + v1.x, (v2.y - v1.y) * f + v1.y); | |
}; | |
Vector2.fixFloatValues = function(vector) { | |
// get rid of negative zero (-0) and fix floating point errors for near-zero values | |
if (vector.x < Vector2.RANGE && vector.x > -Vector2.RANGE) vector.x = 0; | |
if (vector.y < Vector2.RANGE && vector.y > -Vector2.RANGE) vector.y = 0; | |
}; | |
Vector2.extrudePoint = function(point, direction, magnitude) { | |
const offset = direction.clone().multiplyScalar(magnitude); | |
return Vector2.from(point) | |
.add(offset) | |
.toObject(); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment