mirror of
https://github.com/ConnectedHumber/Air-Quality-Web
synced 2024-12-23 10:35:01 +00:00
317 lines
8.2 KiB
JavaScript
317 lines
8.2 KiB
JavaScript
|
"use strict";
|
||
|
|
||
|
/******************************************************
|
||
|
************** Simple ES6 Vector Class **************
|
||
|
******************************************************
|
||
|
* Author: Starbeamrainbowlabs
|
||
|
* Twitter: @SBRLabs
|
||
|
* Email: feedback at starbeamrainbowlabs dot com
|
||
|
*
|
||
|
* From https://gist.github.com/sbrl/69a8fa588865cacef9c0
|
||
|
******************************************************
|
||
|
* Originally written for my 2D Graphics ACW at Hull
|
||
|
* University.
|
||
|
******************************************************
|
||
|
* Changelog
|
||
|
******************************************************
|
||
|
* 19th December 2015:
|
||
|
* Added this changelog.
|
||
|
* 28th December 2015:
|
||
|
* Rewrite tests with klud.js + Node.js
|
||
|
* 30th January 2016:
|
||
|
* Tweak angleFrom function to make it work properly.
|
||
|
* 31st January 2016:
|
||
|
* Add the moveTowards function.
|
||
|
* Add the minComponent getter.
|
||
|
* Add the maxComponent getter.
|
||
|
* Add the equalTo function.
|
||
|
* Tests still need to be written for all of the above.
|
||
|
* 19th September 2016:
|
||
|
* Added Vector support to the multiply method.
|
||
|
* 10th June 2017:
|
||
|
* Fixed a grammatical mistake in a comment.
|
||
|
* Added Vector.fromBearing static method.
|
||
|
* 21st October 2017:
|
||
|
* Converted to ES6 module.
|
||
|
* Added Vector.zero and Vector.one constants. Remember to clone them!
|
||
|
* 4th August 2018: (#LOWREZJAM!)
|
||
|
* Optimised equalTo()
|
||
|
* 6th August 2018: (#LOWREZJAM again!)
|
||
|
* Added round(), floor(), and ceil()
|
||
|
* 7th August 2018: (moar #LOWREZJAM :D)
|
||
|
* Added area() and snapTo(grid_size)
|
||
|
* 10th August 2018: (even more #LOWREZJAM!)
|
||
|
* Added Vector support to divide()
|
||
|
*/
|
||
|
|
||
|
class Vector {
|
||
|
// Constructor
|
||
|
constructor(inX, inY) {
|
||
|
if(typeof inX != "number")
|
||
|
throw new Error("Invalid x value.");
|
||
|
if(typeof inY != "number")
|
||
|
throw new Error("Invalid y value.");
|
||
|
|
||
|
// Store the (x, y) coordinates
|
||
|
this.x = inX;
|
||
|
this.y = inY;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Add another vector to this vector.
|
||
|
* @param {Vector} v The vector to add.
|
||
|
* @return {Vector} The current vector. useful for daisy-chaining calls.
|
||
|
*/
|
||
|
add(v) {
|
||
|
this.x += v.x;
|
||
|
this.y += v.y;
|
||
|
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Take another vector from this vector.
|
||
|
* @param {Vector} v The vector to subtrace from this one.
|
||
|
* @return {Vector} The current vector. useful for daisy-chaining calls.
|
||
|
*/
|
||
|
subtract(v) {
|
||
|
this.x -= v.x;
|
||
|
this.y -= v.y;
|
||
|
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Divide the current vector by a given value.
|
||
|
* @param {Number|Vector} value The number (or Vector) to divide by.
|
||
|
* @return {Vector} The current vector. Useful for daisy-chaining calls.
|
||
|
*/
|
||
|
divide(value) {
|
||
|
if(value instanceof Vector)
|
||
|
{
|
||
|
this.x /= value.x;
|
||
|
this.y /= value.y;
|
||
|
}
|
||
|
else if(typeof value == "number")
|
||
|
{
|
||
|
this.x /= value;
|
||
|
this.y /= value;
|
||
|
}
|
||
|
else
|
||
|
throw new Error("Can't divide by non-number value.");
|
||
|
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Multiply the current vector by a given value.
|
||
|
* @param {Number|Vector} value The number (or Vector) to multiply the current vector by.
|
||
|
* @return {Vector} The current vector. useful for daisy-chaining calls.
|
||
|
*/
|
||
|
multiply(value) {
|
||
|
if(value instanceof Vector)
|
||
|
{
|
||
|
this.x *= value.x;
|
||
|
this.y *= value.y;
|
||
|
}
|
||
|
else if(typeof value == "number")
|
||
|
{
|
||
|
this.x *= value;
|
||
|
this.y *= value;
|
||
|
}
|
||
|
else
|
||
|
throw new Error("Can't multiply by non-number value.");
|
||
|
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Move the vector towards the given vector by the given amount.
|
||
|
* @param {Vector} v The vector to move towards.
|
||
|
* @param {Number} amount The distance to move towards the given vector.
|
||
|
*/
|
||
|
moveTowards(v, amount)
|
||
|
{
|
||
|
// From http://stackoverflow.com/a/2625107/1460422
|
||
|
var dir = new Vector(
|
||
|
v.x - this.x,
|
||
|
v.y - this.y
|
||
|
).limitTo(amount);
|
||
|
this.x += dir.x;
|
||
|
this.y += dir.y;
|
||
|
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Rounds the x and y components of this vector down to the next integer.
|
||
|
* @return {Vector} This vector - useful for diasy-chaining.
|
||
|
*/
|
||
|
floor() {
|
||
|
this.x = Math.floor(this.x);
|
||
|
this.y = Math.floor(this.y);
|
||
|
|
||
|
return this;
|
||
|
}
|
||
|
/**
|
||
|
* Rounds the x and y components of this vector up to the next integer.
|
||
|
* @return {Vector} This vector - useful for diasy-chaining.
|
||
|
*/
|
||
|
ceil() {
|
||
|
this.x = Math.ceil(this.x);
|
||
|
this.y = Math.ceil(this.y);
|
||
|
|
||
|
return this;
|
||
|
}
|
||
|
/**
|
||
|
* Rounds the x and y components of this vector to the nearest integer.
|
||
|
* @return {Vector} This vector - useful for diasy-chaining.
|
||
|
*/
|
||
|
round() {
|
||
|
this.x = Math.round(this.x);
|
||
|
this.y = Math.round(this.y);
|
||
|
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Calculates the 'area' of this vector and returns the result.
|
||
|
* In other words, returns x * y. Useful if you're using a Vector to store
|
||
|
* a size.
|
||
|
* @return {Number} The 'area' of this vector.
|
||
|
*/
|
||
|
area() {
|
||
|
return this.x * this.y;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Snaps this vector to an imaginary square grid with the specified sized
|
||
|
* squares.
|
||
|
* @param {Number} grid_size The size of the squares on the imaginary grid to which to snap.
|
||
|
* @return {Vector} The current vector - useful for daisy-chaining.
|
||
|
*/
|
||
|
snapTo(grid_size) {
|
||
|
this.x = Math.floor(this.x / grid_size) * grid_size;
|
||
|
this.y = Math.floor(this.y / grid_size) * grid_size;
|
||
|
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Limit the length of the current vector to value without changing the
|
||
|
* direction in which the vector is pointing.
|
||
|
* @param {Number} value The number to limit the current vector's length to.
|
||
|
* @return {Vector} The current vector. useful for daisy-chaining calls.
|
||
|
*/
|
||
|
limitTo(value) {
|
||
|
if(typeof value != "number")
|
||
|
throw new Error("Can't limit to non-number value.");
|
||
|
|
||
|
this.divide(this.length);
|
||
|
this.multiply(value);
|
||
|
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Return the dot product of the current vector and another vector.
|
||
|
* @param {Vector} v The other vector we should calculate the dot product with.
|
||
|
* @return {Vector} The current vector. Useful for daisy-chaining calls.
|
||
|
*/
|
||
|
dotProduct(v) {
|
||
|
return (this.x * v.x) + (this.y * v.y);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Calculate the angle, in radians, from north to another vector.
|
||
|
* @param {Vector} v The other vector to which to calculate the angle.
|
||
|
* @return {Vector} The current vector. useful for daisy-chaining calls.
|
||
|
*/
|
||
|
angleFrom(v) {
|
||
|
// From http://stackoverflow.com/a/16340752/1460422
|
||
|
var angle = Math.atan2(v.y - this.y, v.x - this.x) - (Math.PI / 2);
|
||
|
angle += Math.PI/2;
|
||
|
if(angle < 0) angle += Math.PI * 2;
|
||
|
return angle;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Clones the current vector.
|
||
|
* @return {Vector} A clone of the current vector. Very useful for passing around copies of a vector if you don't want the original to be altered.
|
||
|
*/
|
||
|
clone() {
|
||
|
return new Vector(this.x, this.y);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Returns a representation of the current vector as a string.
|
||
|
* @returns {string} A representation of the current vector as a string.
|
||
|
*/
|
||
|
toString() {
|
||
|
return `(${this.x}, ${this.y})`;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Whether the vector is equal to another vector.
|
||
|
* @param {Vector} v The vector to compare to.
|
||
|
* @return {boolean} Whether the current vector is equal to the given vector.
|
||
|
*/
|
||
|
equalTo(v)
|
||
|
{
|
||
|
return this.x == v.x && this.y == v.y;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the unit vector of the current vector - that is a vector poiting in the same direction with a length of 1. Note that this does *not* alter the original vector.
|
||
|
* @return {Vector} The current vector's unit form.
|
||
|
*/
|
||
|
get unitVector() {
|
||
|
var length = this.length;
|
||
|
return new Vector(
|
||
|
this.x / length,
|
||
|
this.y / length);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the length of the current vector.
|
||
|
* @return {Number} The length of the current vector.
|
||
|
*/
|
||
|
get length() {
|
||
|
return Math.sqrt((this.x * this.x) + (this.y * this.y));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the value of the minimum component of the vector.
|
||
|
* @return {Number} The minimum component of the vector.
|
||
|
*/
|
||
|
get minComponent() {
|
||
|
return Math.min(this.x, this.y);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the value of the maximum component of the vector.
|
||
|
* @return {Number} The maximum component of the vector.
|
||
|
*/
|
||
|
get maxComponent() {
|
||
|
return Math.min(this.x, this.y);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns a new vector based on an angle and a length.
|
||
|
* @param {Number} angle The angle, in radians.
|
||
|
* @param {Number} length The length.
|
||
|
* @return {Vector} A new vector that represents the (x, y) of the specified angle and length.
|
||
|
*/
|
||
|
Vector.fromBearing = function(angle, length) {
|
||
|
return new Vector(
|
||
|
length * Math.cos(angle),
|
||
|
length * Math.sin(angle)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
Vector.zero = new Vector(0, 0);
|
||
|
Vector.one = new Vector(1, 1);
|
||
|
|
||
|
export default Vector;
|