ootk-core
Version:
Orbital Object Toolkit. A modern typed replacement for satellite.js including SGP4 propagation, TLE parsing, Sun and Moon calculations, and more.
415 lines • 14.3 kB
JavaScript
/**
* @author Theodore Kruczek.
* @license MIT
* @copyright (c) 2022-2025 Theodore Kruczek Permission is
* hereby granted, free of charge, to any person obtaining a copy of this
* software and associated documentation files (the "Software"), to deal in the
* Software without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import { Vector3D, Matrix } from '../main.js';
/**
* A Vector is a mathematical object that has both magnitude and direction.
*/
export class Vector {
elements;
/**
* The length of the vector.
*/
length;
/**
* Represents a 3-dimensional vector.
*/
static origin3 = new Vector([0, 0, 0]);
/**
* Represents a vector with all elements set to zero.
*/
static origin6 = new Vector([0, 0, 0, 0, 0, 0]);
/**
* Represents the x-axis vector.
*/
static xAxis = new Vector([1, 0, 0]);
/**
* Represents the y-axis vector.
*/
static yAxis = new Vector([0, 1, 0]);
/**
* Represents the z-axis vector.
*/
static zAxis = new Vector([0, 0, 1]);
/**
* Represents a vector pointing along the negative x-axis.
*/
static xAxisNeg = new Vector([-1, 0, 0]);
/**
* Represents a vector pointing along the negative y-axis.
*/
static yAxisNeg = new Vector([0, -1, 0]);
/**
* Represents a vector pointing along the negative z-axis.
*/
static zAxisNeg = new Vector([0, 0, -1]);
constructor(elements) {
this.elements = elements;
this.length = elements.length;
}
/**
* Creates a zero vector of the specified length.
* @param length The length of the vector.
* @returns A new Vector object representing the zero vector.
*/
static zero(length) {
return new Vector(new Array(length).fill(0));
}
/**
* Creates a new Vector with the specified length, filled with the specified
* value.
* @param length The length of the new Vector.
* @param value The value to fill the Vector with.
* @returns A new Vector filled with the specified value.
*/
static filled(length, value) {
return new Vector(new Array(length).fill(value));
}
/**
* Creates a new Vector instance from an array of elements.
* @param elements - The array of elements to create the Vector from.
* @returns A new Vector instance.
*/
static fromList(elements) {
return new Vector(elements);
}
/**
* Returns a string representation of the vector.
* @param fixed - The number of digits to appear after the decimal point.
* Defaults to -1.
* @returns A string representation of the vector.
*/
toString(fixed = -1) {
if (fixed < 0) {
return `[${this.elements.join(', ')}]`;
}
const output = this.elements.map((e) => e.toFixed(fixed));
return `[${output.join(', ')}]`;
}
/**
* Returns a string representation of the x value of the vector.
* @returns A string representation of the x value of the vector.
*/
get x() {
return this.elements[0];
}
/**
* Returns a string representation of the y value of the vector.
* @returns A string representation of the y value of the vector.
*/
get y() {
return this.elements[1];
}
/**
* Returns a string representation of the z value of the vector.
* @returns A string representation of the z value of the vector.
*/
get z() {
return this.elements[2];
}
/**
* Converts the vector elements to an array.
* @returns An array containing the vector elements.
*/
toList() {
return Array.from(this.elements);
}
/**
* Converts the vector to a Float64Array.
* @returns The vector as a Float64Array.
*/
toArray() {
return new Float64Array(this.elements);
}
/**
* Calculates the magnitude of the vector.
* @returns The magnitude of the vector.
*/
magnitude() {
let total = 0;
for (const x of this.elements) {
total += x * x;
}
return Math.sqrt(total);
}
/**
* Adds the elements of another vector to this vector and returns a new
* vector.
* @param v - The vector to add.
* @returns A new vector containing the sum of the elements.
*/
add(v) {
const output = new Array(this.length);
for (let i = 0; i < this.length; i++) {
output[i] = this.elements[i] + v.elements[i];
}
return new Vector(output);
}
/**
* Subtracts a vector from the current vector.
* @param v The vector to subtract.
* @returns A new vector representing the result of the subtraction.
*/
subtract(v) {
const output = new Array(this.length);
for (let i = 0; i < this.length; i++) {
output[i] = this.elements[i] - v.elements[i];
}
return new Vector(output);
}
/**
* Scales the vector by a given factor.
* @param n The scaling factor.
* @returns A new Vector object representing the scaled vector.
*/
scale(n) {
const output = new Array(this.length);
for (let i = 0; i < this.length; i++) {
output[i] = this.elements[i] * n;
}
return new Vector(output);
}
/**
* Negates the vector by scaling it by -1.
* @returns A new Vector object representing the negated vector.
*/
negate() {
return this.scale(-1);
}
/**
* Return the Euclidean distance between this and another Vector.
* @param v The vector to calculate the distance to.
* @returns The distance between the two vectors.
*/
distance(v) {
return this.subtract(v).magnitude();
}
/**
* Normalizes the vector, making it a unit vector with the same direction but
* a magnitude of 1. If the vector has a magnitude of 0, it returns a zero
* vector of the same length.
* @returns The normalized vector.
*/
normalize() {
const m = this.magnitude();
if (m === 0) {
return Vector.zero(this.length);
}
return this.scale(1.0 / m);
}
/**
* Calculates the dot product of this vector and another vector.
* @param v - The vector to calculate the dot product with.
* @returns The dot product of the two vectors.
*/
dot(v) {
let total = 0;
for (let i = 0; i < this.length; i++) {
total += this.elements[i] * v.elements[i];
}
return total;
}
/**
* Calculates the outer product of this vector with another vector.
* @param v The vector to calculate the outer product with.
* @returns A matrix representing the outer product of the two vectors.
*/
outer(v) {
const result = [];
for (let i = 0; i < this.length; i++) {
result[i] = [];
for (let j = 0; j < v.length; j++) {
result[i][j] = this.elements[i] * v.elements[j];
}
}
return new Matrix(result);
}
/**
* Calculates the cross product of this vector and the given vector.
* @param v - The vector to calculate the cross product with.
* @returns The resulting vector.
*/
cross(v) {
const output = new Array(this.length);
for (let i = 0; i < this.length; i++) {
output[i] =
this.elements[(i + 1) % this.length] * v.elements[(i + 2) % this.length] -
this.elements[(i + 2) % this.length] * v.elements[(i + 1) % this.length];
}
return new Vector(output);
}
/**
* Calculate the skew-symmetric matrix for this [Vector].
* @returns The skew-symmetric matrix.
* @throws [Error] if the vector is not of length 3.
*/
skewSymmetric() {
if (this.length !== 3) {
throw new Error('Skew-symmetric matrix requires a vector of length 3.');
}
return new Matrix([
[0, -this.elements[2], this.elements[1]],
[this.elements[2], 0, -this.elements[0]],
[-this.elements[1], this.elements[0], 0],
]);
}
/**
* Rotates the vector around the X-axis by the specified angle.
* @param theta The angle in radians.
* @returns The rotated vector.
*/
rotX(theta) {
const cosT = Math.cos(theta);
const sinT = Math.sin(theta);
const output = new Array(3);
output[0] = this.elements[0];
output[1] = cosT * this.elements[1] + sinT * this.elements[2];
output[2] = -sinT * this.elements[1] + cosT * this.elements[2];
return new Vector(output);
}
/**
* Rotates the vector around the Y-axis by the specified angle.
* @param theta The angle of rotation in radians.
* @returns A new Vector representing the rotated vector.
*/
rotY(theta) {
const cosT = Math.cos(theta);
const sinT = Math.sin(theta);
const output = new Array(3);
output[0] = cosT * this.elements[0] + -sinT * this.elements[2];
output[1] = this.elements[1];
output[2] = sinT * this.elements[0] + cosT * this.elements[2];
return new Vector(output);
}
/**
* Rotates the vector around the Z-axis by the specified angle.
* @param theta The angle of rotation in radians.
* @returns A new Vector representing the rotated vector.
*/
rotZ(theta) {
const cosT = Math.cos(theta);
const sinT = Math.sin(theta);
const output = new Array(3);
output[0] = cosT * this.elements[0] + sinT * this.elements[1];
output[1] = -sinT * this.elements[0] + cosT * this.elements[1];
output[2] = this.elements[2];
return new Vector(output);
}
/**
* Calculates the angle between this vector and another vector.
* @param v The other vector.
* @returns The angle between the two vectors in radians.
*/
angle(v) {
// better than acos for small angles
const theta = Math.atan2(this.cross(v).magnitude(), this.dot(v));
if (isNaN(theta)) {
return 0.0;
}
return theta;
}
/**
* Calculates the angle between this vector and another vector in degrees.
* @param v The other vector.
* @returns The angle between the two vectors in degrees.
*/
angleDegrees(v) {
return (this.angle(v) * (180 / Math.PI));
}
/**
* Determines if there is line of sight between this vector and another vector
* within a given radius.
* @param v - The vector to check line of sight with.
* @param radius - The radius within which line of sight is considered.
* @returns True if there is line of sight, false otherwise.
*/
sight(v, radius) {
const r1Mag2 = this.magnitude() ** 2;
const r2Mag2 = v.magnitude() ** 2;
const rDot = this.dot(v);
const tMin = (r1Mag2 - rDot) / (r1Mag2 + r2Mag2 - 2.0 * rDot);
let los = false;
if (tMin < 0 || tMin > 1) {
los = true;
}
else {
const c = (1.0 - tMin) * r1Mag2 + rDot * tMin;
if (c >= radius * radius) {
los = true;
}
}
return los;
}
/**
* Returns the bisect vector between this vector and the given vector. The
* bisect vector is calculated by scaling this vector's magnitude by the
* magnitude of the given vector, adding the result to the product of scaling
* the given vector's magnitude by this vector's magnitude, and then
* normalizing the resulting vector.
* @param v - The vector to calculate the bisect with.
* @returns The bisect vector.
*/
bisect(v) {
return this.scale(v.magnitude()).add(v.scale(this.magnitude())).normalize();
}
/**
* Joins the current vector with another vector.
* @param v The vector to join with.
* @returns A new vector that contains the elements of both vectors.
*/
join(v) {
return new Vector(this.toList().concat(v.toList()));
}
/**
* Returns a new Vector containing a portion of the elements from the
* specified start index to the specified end index
* @param start The start index of the slice (inclusive).
* @param end The end index of the slice (exclusive).
* @returns A new Vector containing the sliced elements.
*/
slice(start, end) {
return new Vector(this.elements.slice(start, end));
}
/**
* Returns a new Matrix object representing the row vector.
* @returns The row vector as a Matrix object.
*/
row() {
return new Matrix([this.toList()]);
}
/**
* Returns a new Matrix object representing the column vector of this Vector.
* @returns The column vector as a Matrix object.
*/
column() {
return new Matrix(this.toList().map((e) => [e]));
}
/**
* Converts the elements at the specified index to a Vector3D object.
* @param index - The index of the elements to convert.
* @returns A new Vector3D object containing the converted elements.
*/
toVector3D(index) {
return new Vector3D(this.elements[index], this.elements[index + 1], this.elements[index + 2]);
}
}
//# sourceMappingURL=Vector.js.map