npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@ericrovell/vector

v1.0.0

Published

Euclidean vector library written in JavaScript

Downloads

19

Readme

Vector

Euclidean vector (also known as "Geometric" vector) library written in Typescript. A vector is an entity that has both magnitude and direction. Both 2D and 3D vectors are supported.

Features

Getting started

Package available via npm:

npm i @ericrovell/vector
import { vector } from "@ericrovell/vector";

vector(1, 2).toString();  // -> "(1, 2, 0)"

Parsing

Input types

Types for supported input are included into the package.

Supported input

Parses vector components from arguments.

vector().toString();         // -> "(0, 0, 0)"
vector(1).toString();        // -> "(1, 0, 0)"
vector(1, 2).toString();     // -> "(1, 2, 0)"
vector(1, 2, 3).toString();  // -> "(1, 2, 3)"

Parses the given input from Cartesian object and returns a new Vector instance.

/**
* Vector state defined in Cartesian coordinate system.
*/
interface Cartesian {
	x?: number;
	y?: number;
	z?: number;
}

vector({ x: 1 }).toString();               // -> "(1, 0, 0)"
vector({ x: 1, y: 2 }).toString();         // -> "(1, 2, 0)"
vector({ x: 1, y: 2, z: 3 }).toString();   // -> "(1, 2, 3)"

The Cartesian object is considered valid if it is contains at least one of coordinate components: x, y, or z. All missed components defaults to zero, extra data are simply ignored.

vector({ x: 1, data: "hello!" }).toString();               // -> "(1, 0, 0)"
vector({ x: 1, y: 2, z: 3, data: "hello!" }).toString();   // -> "(1, 2, 3)"

Parses the given input from CartesianTuple and returns a new Vector instance.

/**
* Tuple defining vector state defined in Cartesian coordinate system.
*/
type CartesianTuple = readonly [ x: number, y?: number, z?: number ];

vector([ 1 ]).toString();         // -> "(1, 0, 0)"
vector([ 1, 2 ]).toString();      // -> "(0, 2, 0)"
vector([ 1, 2, 3 ]).toString();   // -> "(0, 0, 3)"

Parses the Polar input representing the vector in polar coordinates and returns a new Vector instance:

/**
* Vector state defined in Polar coordinate system:
*/
interface Polar {
	degrees?: boolean = false;
	magnitude?: number = 1;
	phi: number;
	theta?: number = Math.PI / 2;
}

vector({ phi: 0 }).toString()    // -> "(1, 0, 0)"

vector({ phi: Math.PI / 2 }));   // -> "(0, 1, 0)";

vector({
	phi: Math.PI / 2,
	theta: Math.PI / 2,
	magnitude: 2
})                               // -> "(0, 2, 0)";

By default angles input require radians. To use degrees, pass a degrees boolean argument:

vector({ degrees: true, phi: 0 })                              // -> "(1, 0, 0)");
vector({ degrees: true, phi: 90 })                             // -> "(0, 1, 0)");
vector({ degrees: true, phi: 90, theta: 0, magnitude: 2 })     // -> "(0, 0, 2)");
vector({ degrees: true, phi: 90, theta: 90, magnitude: 2 })    // -> "(0, 2, 0)");

The Polar object is considered valid if it is contains at least one of angle properties: phi or theta. The magnitude defaults to a unit length.

Parses the given input from Cylindrical representing the vector in cylindrical coordinate system and returns a new Vector instance:

/**
* Vector state defined in Cylindrical coordinate system:
*/
interface Cylindrical {
	degrees?: boolean = false;
	p: number = 1;
	phi: number = 0;
	z: number = 0;
}

vector({ p: Math.SQRT2, phi: Math.PI / 4, z: 5 }))    // -> "(1, 1, 5)"
vector({ p: 7.0711, phi: -Math.PI / 4, z: 12 }))      // -> "(5, -5, 12)"

By default angles input require radians. To use degrees, pass a degrees boolean argument:

vector({ degrees: true, p: Math.SQRT2, phi: 45, z: 5 }))  // -> "(1, 1, 5)"
vector({ degrees: true, p: 7.0711, phi: -45, z: 12 }))    // -> "(5, -5, 12)"

The Cylindrical object is considered valid if it is contains all the properties: p, phi, and z. Only degrees property is optional.

Methods input

Most methods input arguments signature is:

(x: VectorInput | number, y?: number, z?: number)

Where the VectorInput is any supported valid vector input representation. This way the valid input besides numeric arguments are:

  • Cartesian;
  • CartesianTuple;
  • Polar;
  • Cylindrical;
  • another Vector instance;
const instance = vector(1, 2, 3);

vector(1, 2, 3).add({ x: 1, y: 2, z: 3 }).toString();     // "(2, 4, 6)";
vector(1, 2, 3).add(instance).toString()                  // "(2, 4, 6)";
vector({ x: 1, y: 2, z: 3 }).add([ 1, 2, 3]).toString();  // "(2, 4, 6)";

API

Performs the addition and returns the sum as new Vector instance.

vector(1, 2).add(3, 4).toString();  // -> "(4, 6, 0)"

Adds the another Vector instance or a valid vector input to this vector.

const v1 = vector(1, 2, 3).addSelf(1, 2, 3);
const v2 = vector(1, 2, 3);

v1.addSelf(v2);
v1.toString(); // ->  "(2, 4, 6)"

Calculates the angle between the vector instance and another valid vector input. The angle can be signed if signed boolean argument is passed.

vector(1, 2, 3).angle(4, 5, 6) // -> 0.22573
vector(1, 2, 3).angle(4, 5, 6, true) // -> -0.22573
vector(1, 2, 3).angle(4, 5, 6, true, true) // -> -12.93315

Note: this method do not accept simple arguments input.

Rounds this vector's components values to the next upper bound with defined precision.

vector(1.12345, 2.45678, 3.78921).ceilSelf().toString()          // -> "(2, 3, 4)");
vector(Math.SQRT2, Math.PI, 2 * Math.PI).ceilSelf(3).toString()  // -> "(1.415, 3.142, 6.284)");

Clamps this vector's component values between an upper and lower bound.

vector(1.2, -1).clamp().toString()        // -> "(1, 0, 0)");
vector(5, 10, -2).clamp(2, 8).toString()  // -> "(5, 8, 2)");

Returns a copy of the vector instance.

const a = vector(1, 2, 3);
const b = a.copy();

b.toString(); // -> "(1, 2, 3)"

Calculates the cross product between the instance and another valid vector input and returns a new Vector instance.

vector(1, 2, 3).cross(4, 5, 6)         // -> (-3, 6, -3)

Sets this vector to the cross product between the original vector and another valid input.

vector(1, 2, 3).crossSelf(4, 5, 6)         // -> (-3, 6, -3)

Calculates the Euclidean distance between the vector and another valid vector input, considering a point as a vector.

vector(1, 2, 3).distance(4, 5, 6) // -> 5.19615

Calculates the squared Euclidean distance between the vector and another valid vector input, considering a point as a vector. Slightly more efficient to calculate, useful to comparing.

vector(1, 2, 3).distanceSq(4, 5, 6) // -> 27

Calculates the dot product of the vector and another valid vector input.

vector(1, 2, 3).dot(4, 5, 6)   // -> 32

Performs an equality check against another valid vector input.

vector(1, 2, 3).equals(1, 2, 3);                  // -> true
vector({ x: 1, y: 2 }).equals([ 1, 2 ]);          // -> true
vector({ x: -1, y: -2 }).equals({ x: -1, y: 2});  // -> false

Rounds this vector's components values to the next lower bound with defined precision.

vector(1.12345, 2.45678, 3.78921).floorSelf(4).toString()         // -> "(1.1234, 2.4567, 3.7892)");
vector(Math.SQRT2, Math.PI, 2 * Math.PI).floorSelf(3).toString()  // -> "(1.414, 3.141, 6.283)");

Calculates vector's azimuthal angle.

vector(3, 4).getPhi();         // -> 0.927295
vector(1, -2, 3).getPhi(true); // -> 53.130102

Calculates vector's elevation angle.

vector(3, 4, 5).getTheta();     // -> 0.785398
vector(3, 4, 5).getTheta(true); // -> 45

Returns an inverted Vector instance.

vector(-1, 2).inverted;  // -> "(1, -2, 0)"

Linearly interpolate the vector to another vector.

const a = vector([ 4, 8, 16 ]);
const b = vector([ 8, 24, 48 ]);

a.lerp(b)         // ->  "(4, 8, 16)"
a.lerp(b, -0.5)   // ->  "(4, 8, 16)"
a.lerp(b, 0.25)   // ->  "(5, 12, 24)"
a.lerp(b, 0.5)    // ->  "(6, 16, 32)"
a.lerp(b, 0.75)   // ->  "(7, 20, 40)"
a.lerp(b, 1)      // ->  "(8, 24, 48)"
a.lerp(b, 1.5)    // ->  "(8, 24, 48)"

Note: this method do not accept simple arguments input.

Limits the magnitude of the vector and returns the result as new Vector instance.

const v = vector(3, 4, 12); // magnitude is 13

v.limit(15).magnitude  // -> 13
v.limit(10).magnitude  // -> 10
v.limit(13).magnitude  // -> 13

Limits the magnitude of this vector and returns itself.

const v = vector(3, 4, 12); // magnitude is 13

v.limitSelf(15).magnitude  // -> 13
v.limitSelf(10).magnitude  // -> 10
v.limitSelf(13).magnitude  // -> 13

Calculates the magnitude of the vector:

vector(0).magnitude;         // -> 0
vector(3, 4).magnitude;      // -> 5
vector(3, 4, 12).magnitude;  // -> 13

Calls a defined callback on every vector component and returns a new Vector instance:

vector(1, 2, 3)
.map(value => value * 2)
.toString() // -> "(2, 4, 6)"

Calls a defined callback on each of this vector component.

const v = vector(1, 2, 3);
v.mapSelf(value => value * 2);
v.toString() // -> "(2, 4, 6)"

Calculates the squared magnitude of the vector. It may be useful and faster where the real value is not that important. For example, to compare two vectors' length.

vector(0).magnitudeSq;         // -> 0
vector(3, 4).magnitudeSq;      // -> 25
vector(3, 4, 12).magnitudeSq;  // -> 169

Normalizes the vector and returns a new Vector instance as unit vector:

vector().normalize().magnitude;       // -> 1
vector(3, 4, 5).normalize().magnitude; // -> 1

Makes the current vector a unit vector.

vector().normalizeSelf().magnitude;          // -> 0
vector(3, 4, 12).normalizeSelf().magnitude;   // -> 13

Creates a random planar unit vector (OXY plane).

vector().random2d().toString() // ->  "(0.23565, 0.75624, 0)"

Creates a random 3D unit vector.

Correct distribution thanks to wolfram.

vector().random3d().toString() // ->  "(0.23565, 0.75624, -0.56571)"

Reflects the vector about a normal line for 2D vector, or about a normal to a plane in 3D.

Here, in an example the vector a can be viewed as the incident ray, the vector n as the normal, and the resulting vector should be the reflected ray.

const a = vector([ 4, 6 ]);
const n = vector([ 0, -1 ]);

a.reflect(n).toString() // ->  "(4, -6, 0)"

Rotates the vector by an azimuthal angle (XOY plane) and returns a new Vector instance.

vector(1, 2).rotate(Math.PI / 3);
vector(1, 2).rotate(60, true);

Rotates the current vector by an azimuthal angle (XOY plane).

vector(1, 2).rotateSelf(Math.PI / 3);
vector(1, 2).rotateSelf(60, true);

Rotates the vector by an azimuthal and elevation angles and returns a new Vector instance.

vector(1, 2, 3).rotate3d(Math.PI / 3, Math.PI / 6);
vector(1, 2, 3).rotate3d(60, 30, true);

Rotates the current vector by an azimuthal and elevation angles.

vector(1, 2, 3).rotateSelf3d(Math.PI / 3, Math.PI / 6);
vector(1, 2, 3).rotateSelf3d(60, 30, true);

Rounds this vector's component values to the closest bound with defined precision.

vector(1.12345, 2.45678, 3.78921).roundSelf(4).toString()         // -> "(1.1235, 2.4568, 3.7892)");
vector(Math.SQRT2, Math.PI, 2 * Math.PI).roundSelf(3).toString()  // -> "(1.414, 3.142, 6.283)");

Performs the scalar vector multiplication and returns a new Vector instance:

vector(1, 2).scale(2).toString();      // -> "(2, 4, 0)"
vector(1, 2, 3).scale(-2).toString();  // -> "(-2, -4, -6)"

The second argument turns the passed value into reciprocal, in other words the division will be performed:

vector(2, 4, 6).scale(2, true).toString(); // -> "(1, 2, 3)"

Although the same effect can be obtained just with .scale(0.5), it is useful when the variable may have zero value. In case of zero division the zero vector will be returned and marked as invalid.

const v = vector(1, 2, 3).scale(0, true);

v.valid      // -> false
v.toString() // -> "(0, 0, 0)"

Scales this vector by a scalar value.

const a = vector(-1, 2, 3).scaleSelf(5);

a.toString() // -> "(-5, 10, 15)"

The second parameter turns the passed value into reciprocal, in other words the division will be performed:

const v = vector(-12, -18, -24).scale(2, true);
v.toString(); // -> "(-6, -9, -12)"

It is useful when the variable may have zero value. In this case the vector components won't change.

Set's the current vector state from another Vector instance or valid vector input.

const v1 = vector(1, 2, 3);
v1.setSelf(-1, -2, -3);

v1.toString() // -> "(-1, -2, -3)"

Creates and returns a new Vector instance with modified component value.

vector(1, 2, 3).setComponent("x", 2).toString(); // -> "(2, 2, 3)"
vector(1, 2, 3).setComponent("y", 3).toString(); // -> "(1, 3, 3)"
vector(1, 2, 3).setComponent("z", 4).toString(); // -> "(1, 2, 4)"

Sets the vector instance component value.

const v = vector(1, 2, 3)
	.setComponentSelf("x", 0)
	.setComponentSelf("y", 0)
	.setComponentSelf("z", 0)

v.toString() // -> "(0, 0, 0)"

Sets the magnitude of the vector and returns a new Vector instance.

vector(1).setMagnitude(5).magnitude        // -> 5;
vector(1, 2, 3).setMagnitude(5).magnitude  // -> 5;

Sets the magnitude of this vector.

vector(1).setMagnitudeSelf(5).magnitude         // -> 5;
vector(1, 2, 3).setMagnitudeSelf(-5).magnitude  // -> 5;

Rotates the vector instance to a specific azimuthal angle (OXY plane) and returns a new Vector instance.

vector(1, 2).setPhi(Math.PI / 3);
vector(1, 2, 3).setPhi(60, true);

Rotates the vector instance to a specific azimuthal angle (OXY plane).

vector(1, 2).setPhiSelf(Math.PI / 3);
vector(1, 2, 3).setPhiSelf(60, true);

Rotates the vector instance to a specific elevation angle and returns a new Vector instance.

vector(1, 2).setTheta(Math.PI / 3);
vector(1, 2, 3).setTheta(60, true);

Rotates the vector instance to a specific elevation angle.

vector(1, 2).setThetaSelf(Math.PI / 3);
vector(1, 2, 3).setThetaSelf(60, true);

Performs the subtraction and returns the result as new Vector instance.

vector(1, 2, 3).sub(2, 3, 4).toString()  // -> "(-1, -1, -1)"

Subtracts another Vector instance or valid vector input from this vector.

const v1 = vector(1, 2, 3);
const v2 = vector(2, 1, 5);

v1.subSelf(v2);
v1.toString(); // -> "(-1, 1, -2)"

Returns vector's components packed into array.

vector(1).toArray();        // -> [ 1, 0, 0 ]
vector(1, 2).toArray();     // -> [ 1, 2, 0 ]
vector(1, 2, 3).toArray();  // -> [ 1, 2, 3 ]

Returns a Vector string representation.

vector(1).toString();        // -> "(1, 0, 0)"
vector(1, 2).toString();     // -> "(1, 2, 0)"
vector(1, 2, 3).toString();  // -> "(1, 2, 3)"

Passing an invalid input does not throw error. Getter returns a boolean indicating whether user input was valid or not.

Invalid input defaults to zero vector.

vector([ 1, 2 ]).valid;        // -> true
vector([ NaN ]).valid;         // -> false
vector({ x: 1, y: 2 }).valid;  // -> true
vector({ a: 1, b: 2 }).valid;  // -> false

Converts the vector instance to primitive value - it's magnitude. May be useful when using type coercion.

const a = vector(3, 4);
const b = vector(6, 8);

a + b // -> 15

Other Features

Immutability

All operations have both mutable and immutable methods. They are easy to distinguish by self postfix:

  • .add() is immutable;
  • addSelf() is mutable;

Extendibility

To extend the functionality for your needs, extend the parent Vector class:

import { Vector, type VectorInput } from "@ericrovell/vector";

class VectorExtended extends Vector {
	constructor(input: VectorInput) {
		super(input);
	}

	get sum() {
		return this.x + this.y + this.z;
	}
}

const instance = new VectorExtended([ 1, 2, 3 ]);
instance.sum; // -> 6

Method Chaining

Most of the methods are chainable, no matter is it mutable or immutable method:

const v = vector(1, 2, 3)
	.add(1, 2, 3)
	.sub(1, 2, 3)
	.scale(2)
	.toString(); // "(2, 4, 6)";

const v = vector(1, 2, 3)
	.addSelf(1, 2, 3)
	.subSelf(1, 2, 3)
	.scaleSelf(2)
	.toString(); // "(2, 4, 6)";

Iterability

The Vector instance can be iterated via for ... of loop to loop through the vector's components:

const v = vector(1, 2, 3);

for (const component of v) {
	console.log(component);
	// -> yielding 1, 2, 3
}

The same way the spread operator can be used, Array.from(), and all other methods and functions that operates on iterables.

Types

Tha package includes all necessary types useful for all possible valid input options are available for import:

export type {
	Cartesian,
	CartesianTuple,
	Polar,
	Cylindrical,
	VectorInput,
	Vector
} from "@ericrovell/vector";

Tests

To run the tests use the npm run test command.

Attribution

Vector's logo is done thanks to FreakAddL