mirror of
https://github.com/zrwusa/data-structure-typed.git
synced 2024-11-23 04:44:04 +00:00
refactor: Remove the _addTo method from BinaryTree and TreeMultiMap.
feat: Reimplement Matrix. docs: Use typedoc.json configuration to only output class documentation.
This commit is contained in:
parent
dc6ef95f78
commit
2247316d16
|
@ -28,7 +28,7 @@ anymore! JavaScript and TypeScript now have [data-structure-typed]().**`Benchmar
|
|||
|
||||
### We provide data structures that are not available in JS/TS
|
||||
|
||||
Heap, Binary Tree, RedBlack Tree, Linked List, Deque, Trie, Directed Graph, Undirected Graph, BST, AVL Tree, Priority Queue, Queue, Tree Multiset, Linked List.
|
||||
Heap, Binary Tree, RedBlack Tree, Linked List, Deque, Trie, Directed Graph, Undirected Graph, BST, AVL Tree, Priority Queue, Queue, Tree Multiset.
|
||||
|
||||
|
||||
### Performance surpasses that of native JS/TS
|
||||
|
@ -1033,5 +1033,3 @@ avl2.print();
|
|||
</div>
|
||||
|
||||
[//]: # (No deletion!!! End of Replace Section)
|
||||
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ JavaScript和TypeScript的数据结构。
|
|||
|
||||
### 提供了JS/TS中没有的数据结构
|
||||
|
||||
Heap, Binary Tree, RedBlack Tree, Linked List, Deque, Trie, Directed Graph, Undirected Graph, BST, AVL Tree, Priority Queue, Queue, Tree Multiset, Linked List.
|
||||
Heap, Binary Tree, RedBlack Tree, Linked List, Deque, Trie, Directed Graph, Undirected Graph, BST, AVL Tree, Priority Queue, Queue, Tree Multiset.
|
||||
|
||||
|
||||
### 性能超越原生JS/TS
|
||||
|
|
701
package-lock.json
generated
701
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -14,11 +14,12 @@
|
|||
}
|
||||
},
|
||||
"scripts": {
|
||||
"build": "npm run build:mjs && npm run build:cjs && npm run build:umd && npm run build:docs",
|
||||
"build": "npm run build:mjs && npm run build:cjs && npm run build:umd && npm run build:docs-class",
|
||||
"build:mjs": "rm -rf dist/mjs && tsc -p tsconfig-mjs.json",
|
||||
"build:cjs": "rm -rf dist/cjs && tsc -p tsconfig-cjs.json",
|
||||
"build:umd": "tsup",
|
||||
"build:docs": "typedoc --out docs ./src",
|
||||
"build:docs-class": "typedoc --out docs ./src/data-structures",
|
||||
"test:unit": "jest --runInBand",
|
||||
"test": "npm run test:unit",
|
||||
"test:integration": "npm run update:subs && jest --config jest.integration.config.js",
|
||||
|
@ -87,7 +88,7 @@
|
|||
"ts-loader": "^9.4.4",
|
||||
"ts-node": "^10.9.1",
|
||||
"tsup": "^7.2.0",
|
||||
"typedoc": "^0.25.1",
|
||||
"typedoc": "^0.25.4",
|
||||
"typescript": "^5.3.2"
|
||||
},
|
||||
"keywords": [
|
||||
|
|
|
@ -2032,43 +2032,6 @@ export class BinaryTree<
|
|||
return newNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* The function `_addTo` adds a new node to a binary tree if there is an available position.
|
||||
* @param {N | null | undefined} newNode - The `newNode` parameter represents the node that you want to add to
|
||||
* the binary tree. It can be either a node object or `null`.
|
||||
* @param {N} parent - The `parent` parameter represents the parent node to which the new node will
|
||||
* be added as a child.
|
||||
* @returns either the left or right child node of the parent node, depending on which child is
|
||||
* available for adding the new node. If a new node is added, the function also updates the size of
|
||||
* the binary tree. If neither the left nor right child is available, the function returns undefined.
|
||||
* If the parent node is null, the function also returns undefined.
|
||||
*/
|
||||
protected _addTo(newNode: N | null | undefined, parent: BTNKeyOrNode<K, N>): N | null | undefined {
|
||||
if (this.isNotNodeInstance(parent)) parent = this.getNode(parent);
|
||||
|
||||
if (parent) {
|
||||
// When all leaf nodes are null, it will no longer be possible to add new entity nodes to this binary tree.
|
||||
// In this scenario, null nodes serve as "sentinel nodes," "virtual nodes," or "placeholder nodes."
|
||||
if (parent.left === undefined) {
|
||||
parent.left = newNode;
|
||||
if (newNode) {
|
||||
this._size = this.size + 1;
|
||||
}
|
||||
return parent.left;
|
||||
} else if (parent.right === undefined) {
|
||||
parent.right = newNode;
|
||||
if (newNode) {
|
||||
this._size = this.size + 1;
|
||||
}
|
||||
return parent.right;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The function sets the root property of an object to a given value, and if the value is not null,
|
||||
* it also sets the parent property of the value to undefined.
|
||||
|
|
|
@ -361,47 +361,6 @@ export class TreeMultimap<
|
|||
return cloned;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(1) - constant time, as it performs basic pointer assignments.
|
||||
* Space Complexity: O(1) - constant space, as it only uses a constant amount of memory.
|
||||
*
|
||||
* The function adds a new node to a binary tree, either as the left child or the right child of a
|
||||
* given parent node.
|
||||
* @param {N | undefined} newNode - The `newNode` parameter represents the node that needs to be
|
||||
* added to the binary tree. It can be of type `N` (which represents a node in the binary tree) or
|
||||
* `undefined` if there is no node to add.
|
||||
* @param {K | N | undefined} parent - The `parent` parameter represents the parent node to
|
||||
* which the new node will be added as a child. It can be either a node object (`N`) or a key value
|
||||
* (`K`).
|
||||
* @returns The method `_addTo` returns either the `parent.left` or `parent.right` node that was
|
||||
* added, or `undefined` if no node was added.
|
||||
*/
|
||||
protected override _addTo(newNode: N | undefined, parent: BSTNKeyOrNode<K, N>): N | undefined {
|
||||
parent = this.ensureNode(parent);
|
||||
if (parent) {
|
||||
if (parent.left === undefined) {
|
||||
parent.left = newNode;
|
||||
if (newNode !== undefined) {
|
||||
this._size = this.size + 1;
|
||||
this._count += newNode.count;
|
||||
}
|
||||
|
||||
return parent.left;
|
||||
} else if (parent.right === undefined) {
|
||||
parent.right = newNode;
|
||||
if (newNode !== undefined) {
|
||||
this._size = this.size + 1;
|
||||
this._count += newNode.count;
|
||||
}
|
||||
return parent.right;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `_swapProperties` function swaps the key, value, count, and height properties between two nodes.
|
||||
* @param {K | N | undefined} srcNode - The `srcNode` parameter represents the source node from
|
||||
|
|
|
@ -1,4 +1,2 @@
|
|||
export * from './matrix';
|
||||
export * from './vector2d';
|
||||
export * from './matrix2d';
|
||||
export * from './navigator';
|
||||
|
|
|
@ -5,23 +5,452 @@
|
|||
* @copyright Copyright (c) 2022 Tyler Zeng <zrwusa@gmail.com>
|
||||
* @license MIT License
|
||||
*/
|
||||
// todo need to be improved
|
||||
export class MatrixNTI2D<V = any> {
|
||||
protected readonly _matrix: Array<Array<V>>;
|
||||
|
||||
export class Matrix {
|
||||
/**
|
||||
* The constructor function initializes a matrix object with the provided data and options, or with
|
||||
* default values if no options are provided.
|
||||
* @param {number[][]} data - A 2D array of numbers representing the data for the matrix.
|
||||
* @param [options] - The `options` parameter is an optional object that can contain the following
|
||||
* properties:
|
||||
*/
|
||||
constructor(
|
||||
data: number[][],
|
||||
options?: {
|
||||
rows?: number;
|
||||
cols?: number;
|
||||
addFn?: (a: number, b: number) => any;
|
||||
subtractFn?: (a: number, b: number) => any;
|
||||
multiplyFn?: (a: number, b: number) => any;
|
||||
}
|
||||
) {
|
||||
if (options) {
|
||||
const { rows, cols, addFn, subtractFn, multiplyFn } = options;
|
||||
if (typeof rows === 'number' && rows > 0) this._rows = rows;
|
||||
else this._rows = data.length;
|
||||
if (typeof cols === 'number' && cols > 0) this._cols = cols;
|
||||
else this._cols = data[0]?.length || 0;
|
||||
if (addFn) this._addFn = addFn;
|
||||
if (subtractFn) this._subtractFn = subtractFn;
|
||||
if (multiplyFn) this._multiplyFn = multiplyFn;
|
||||
} else {
|
||||
this._rows = data.length;
|
||||
this._cols = data[0]?.length ?? 0;
|
||||
}
|
||||
|
||||
if (data.length > 0) {
|
||||
this._data = data;
|
||||
} else {
|
||||
this._data = [];
|
||||
for (let i = 0; i < this.rows; i++) {
|
||||
this._data[i] = new Array(this.cols).fill(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected _rows: number = 0;
|
||||
|
||||
get rows(): number {
|
||||
return this._rows;
|
||||
}
|
||||
|
||||
protected _cols: number = 0;
|
||||
|
||||
get cols(): number {
|
||||
return this._cols;
|
||||
}
|
||||
|
||||
protected _data: number[][];
|
||||
|
||||
get data(): number[][] {
|
||||
return this._data;
|
||||
}
|
||||
|
||||
get addFn() {
|
||||
return this._addFn;
|
||||
}
|
||||
|
||||
get subtractFn() {
|
||||
return this._subtractFn;
|
||||
}
|
||||
|
||||
get multiplyFn() {
|
||||
return this._multiplyFn;
|
||||
}
|
||||
|
||||
/**
|
||||
* The constructor creates a matrix with the specified number of rows and columns, and initializes all elements to a
|
||||
* given initial value or 0 if not provided.
|
||||
* @param options - An object containing the following properties:
|
||||
* The `get` function returns the value at the specified row and column index if it is a valid index.
|
||||
* @param {number} row - The `row` parameter represents the row index of the element you want to
|
||||
* retrieve from the data array.
|
||||
* @param {number} col - The parameter "col" represents the column number of the element you want to
|
||||
* retrieve from the data array.
|
||||
* @returns The `get` function returns a number if the provided row and column indices are valid.
|
||||
* Otherwise, it returns `undefined`.
|
||||
*/
|
||||
constructor(options: { row: number; col: number; initialVal?: V }) {
|
||||
const { row, col, initialVal } = options;
|
||||
this._matrix = new Array(row).fill(undefined).map(() => new Array(col).fill(initialVal || 0));
|
||||
get(row: number, col: number): number | undefined {
|
||||
if (this.isValidIndex(row, col)) {
|
||||
return this.data[row][col];
|
||||
}
|
||||
}
|
||||
|
||||
/* The `toArray` method returns the matrix as a two-dimensional array. It converts the internal representation of the
|
||||
matrix, which is an array of arrays, into a format that is more commonly used in JavaScript. */
|
||||
toArray(): Array<Array<V>> {
|
||||
return this._matrix;
|
||||
/**
|
||||
* The set function updates the value at a specified row and column in a two-dimensional array.
|
||||
* @param {number} row - The "row" parameter represents the row index of the element in a
|
||||
* two-dimensional array or matrix. It specifies the row where the value will be set.
|
||||
* @param {number} col - The "col" parameter represents the column index of the element in a
|
||||
* two-dimensional array.
|
||||
* @param {number} value - The value parameter represents the number that you want to set at the
|
||||
* specified row and column in the data array.
|
||||
* @returns a boolean value. It returns true if the index (row, col) is valid and the value is
|
||||
* successfully set in the data array. It returns false if the index is invalid and the value is not
|
||||
* set.
|
||||
*/
|
||||
set(row: number, col: number, value: number): boolean {
|
||||
if (this.isValidIndex(row, col)) {
|
||||
this.data[row][col] = value;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* The function checks if the dimensions of the given matrix match the dimensions of the current
|
||||
* matrix.
|
||||
* @param {Matrix} matrix - The parameter `matrix` is of type `Matrix`.
|
||||
* @returns a boolean value.
|
||||
*/
|
||||
isMatchForCalculate(matrix: Matrix): boolean {
|
||||
return this.rows === matrix.rows && this.cols === matrix.cols;
|
||||
}
|
||||
|
||||
/**
|
||||
* The `add` function adds two matrices together, returning a new matrix with the result.
|
||||
* @param {Matrix} matrix - The `matrix` parameter is an instance of the `Matrix` class.
|
||||
* @returns The `add` method returns a new `Matrix` object that represents the result of adding the
|
||||
* current matrix with the provided `matrix` parameter.
|
||||
*/
|
||||
add(matrix: Matrix): Matrix | undefined {
|
||||
if (!this.isMatchForCalculate(matrix)) {
|
||||
throw new Error('Matrix dimensions must match for addition.');
|
||||
}
|
||||
|
||||
const resultData: number[][] = [];
|
||||
for (let i = 0; i < this.rows; i++) {
|
||||
resultData[i] = [];
|
||||
for (let j = 0; j < this.cols; j++) {
|
||||
const a = this.get(i, j),
|
||||
b = matrix.get(i, j);
|
||||
if (a !== undefined && b !== undefined) {
|
||||
const added = this._addFn(a, b);
|
||||
if (added) {
|
||||
resultData[i][j] = added;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new Matrix(resultData, {
|
||||
rows: this.rows,
|
||||
cols: this.cols,
|
||||
addFn: this.addFn,
|
||||
subtractFn: this.subtractFn,
|
||||
multiplyFn: this.multiplyFn
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* The `subtract` function performs element-wise subtraction between two matrices and returns a new
|
||||
* matrix with the result.
|
||||
* @param {Matrix} matrix - The `matrix` parameter is an instance of the `Matrix` class. It
|
||||
* represents the matrix that you want to subtract from the current matrix.
|
||||
* @returns a new Matrix object with the result of the subtraction operation.
|
||||
*/
|
||||
subtract(matrix: Matrix): Matrix | undefined {
|
||||
if (!this.isMatchForCalculate(matrix)) {
|
||||
throw new Error('Matrix dimensions must match for subtraction.');
|
||||
}
|
||||
|
||||
const resultData: number[][] = [];
|
||||
for (let i = 0; i < this.rows; i++) {
|
||||
resultData[i] = [];
|
||||
for (let j = 0; j < this.cols; j++) {
|
||||
const a = this.get(i, j),
|
||||
b = matrix.get(i, j);
|
||||
if (a !== undefined && b !== undefined) {
|
||||
const subtracted = this._subtractFn(a, b);
|
||||
if (subtracted) {
|
||||
resultData[i][j] = subtracted;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new Matrix(resultData, {
|
||||
rows: this.rows,
|
||||
cols: this.cols,
|
||||
addFn: this.addFn,
|
||||
subtractFn: this.subtractFn,
|
||||
multiplyFn: this.multiplyFn
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* The `multiply` function performs matrix multiplication between two matrices and returns the result
|
||||
* as a new matrix.
|
||||
* @param {Matrix} matrix - The `matrix` parameter is an instance of the `Matrix` class.
|
||||
* @returns a new Matrix object.
|
||||
*/
|
||||
multiply(matrix: Matrix): Matrix | undefined {
|
||||
if (this.cols !== matrix.rows) {
|
||||
throw new Error('Matrix dimensions must be compatible for multiplication (A.cols = B.rows).');
|
||||
}
|
||||
|
||||
const resultData: number[][] = [];
|
||||
for (let i = 0; i < this.rows; i++) {
|
||||
resultData[i] = [];
|
||||
for (let j = 0; j < matrix.cols; j++) {
|
||||
let sum: number | undefined;
|
||||
for (let k = 0; k < this.cols; k++) {
|
||||
const a = this.get(i, k),
|
||||
b = matrix.get(k, j);
|
||||
if (a !== undefined && b !== undefined) {
|
||||
const multiplied = this.multiplyFn(a, b);
|
||||
if (multiplied !== undefined) {
|
||||
sum = this.addFn(sum, multiplied);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (sum !== undefined) resultData[i][j] = sum;
|
||||
}
|
||||
}
|
||||
|
||||
return new Matrix(resultData, {
|
||||
rows: this.rows,
|
||||
cols: matrix.cols,
|
||||
addFn: this.addFn,
|
||||
subtractFn: this.subtractFn,
|
||||
multiplyFn: this.multiplyFn
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* The transpose function takes a matrix and returns a new matrix that is the transpose of the
|
||||
* original matrix.
|
||||
* @returns The transpose() function returns a new Matrix object with the transposed data.
|
||||
*/
|
||||
transpose(): Matrix {
|
||||
if (this.data.some(row => row.length !== this.rows)) {
|
||||
throw new Error('Matrix must be rectangular for transposition.');
|
||||
}
|
||||
|
||||
const resultData: number[][] = [];
|
||||
|
||||
for (let j = 0; j < this.cols; j++) {
|
||||
resultData[j] = [];
|
||||
for (let i = 0; i < this.rows; i++) {
|
||||
const trans = this.get(i, j);
|
||||
if (trans !== undefined) resultData[j][i] = trans;
|
||||
}
|
||||
}
|
||||
|
||||
return new Matrix(resultData, {
|
||||
rows: this.cols,
|
||||
cols: this.rows,
|
||||
addFn: this.addFn,
|
||||
subtractFn: this.subtractFn,
|
||||
multiplyFn: this.multiplyFn
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* The `inverse` function calculates the inverse of a square matrix using Gaussian elimination.
|
||||
* @returns a Matrix object, which represents the inverse of the original matrix.
|
||||
*/
|
||||
inverse(): Matrix | undefined {
|
||||
// Check if the matrix is square
|
||||
if (this.rows !== this.cols) {
|
||||
throw new Error('Matrix must be square for inversion.');
|
||||
}
|
||||
|
||||
// Create an augmented matrix [this | I]
|
||||
const augmentedMatrixData: number[][] = [];
|
||||
for (let i = 0; i < this.rows; i++) {
|
||||
augmentedMatrixData[i] = this.data[i].slice(); // Copy the original matrix
|
||||
for (let j = 0; j < this.cols; j++) {
|
||||
augmentedMatrixData[i][this.cols + j] = i === j ? 1 : 0; // Append the identity matrix
|
||||
}
|
||||
}
|
||||
|
||||
const augmentedMatrix = new Matrix(augmentedMatrixData, {
|
||||
rows: this.rows,
|
||||
cols: this.cols * 2,
|
||||
addFn: this.addFn,
|
||||
subtractFn: this.subtractFn,
|
||||
multiplyFn: this.multiplyFn
|
||||
});
|
||||
|
||||
// Apply Gaussian elimination to transform the left half into the identity matrix
|
||||
for (let i = 0; i < this.rows; i++) {
|
||||
// Find pivot
|
||||
let pivotRow = i;
|
||||
while (pivotRow < this.rows && augmentedMatrix.get(pivotRow, i) === 0) {
|
||||
pivotRow++;
|
||||
}
|
||||
|
||||
if (pivotRow === this.rows) {
|
||||
// Matrix is singular, and its inverse does not exist
|
||||
throw new Error('Matrix is singular, and its inverse does not exist.');
|
||||
}
|
||||
|
||||
// Swap rows to make the pivot the current row
|
||||
augmentedMatrix._swapRows(i, pivotRow);
|
||||
|
||||
// Scale the pivot row to make the pivot element 1
|
||||
const pivotElement = augmentedMatrix.get(i, i) ?? 1;
|
||||
|
||||
if (pivotElement === 0) {
|
||||
// Handle division by zero
|
||||
throw new Error('Matrix is singular, and its inverse does not exist (division by zero).');
|
||||
}
|
||||
|
||||
augmentedMatrix._scaleRow(i, 1 / pivotElement);
|
||||
|
||||
// Eliminate other rows to make elements in the current column zero
|
||||
for (let j = 0; j < this.rows; j++) {
|
||||
if (j !== i) {
|
||||
let factor = augmentedMatrix.get(j, i);
|
||||
if (factor === undefined) factor = 0;
|
||||
|
||||
augmentedMatrix._addScaledRow(j, i, -factor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Extract the right half of the augmented matrix as the inverse
|
||||
const inverseData: number[][] = [];
|
||||
for (let i = 0; i < this.rows; i++) {
|
||||
inverseData[i] = augmentedMatrix.data[i].slice(this.cols);
|
||||
}
|
||||
|
||||
return new Matrix(inverseData, {
|
||||
rows: this.rows,
|
||||
cols: this.cols,
|
||||
addFn: this.addFn,
|
||||
subtractFn: this.subtractFn,
|
||||
multiplyFn: this.multiplyFn
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* The dot function calculates the dot product of two matrices and returns a new matrix.
|
||||
* @param {Matrix} matrix - The `matrix` parameter is an instance of the `Matrix` class.
|
||||
* @returns a new Matrix object.
|
||||
*/
|
||||
dot(matrix: Matrix): Matrix | undefined {
|
||||
if (this.cols !== matrix.rows) {
|
||||
throw new Error(
|
||||
'Number of columns in the first matrix must be equal to the number of rows in the second matrix for dot product.'
|
||||
);
|
||||
}
|
||||
|
||||
const resultData: number[][] = [];
|
||||
for (let i = 0; i < this.rows; i++) {
|
||||
resultData[i] = [];
|
||||
for (let j = 0; j < matrix.cols; j++) {
|
||||
let sum: number | undefined;
|
||||
for (let k = 0; k < this.cols; k++) {
|
||||
const a = this.get(i, k),
|
||||
b = matrix.get(k, j);
|
||||
if (a !== undefined && b !== undefined) {
|
||||
const multiplied = this.multiplyFn(a, b);
|
||||
if (multiplied !== undefined) {
|
||||
sum = this.addFn(sum, multiplied);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (sum !== undefined) resultData[i][j] = sum;
|
||||
}
|
||||
}
|
||||
|
||||
return new Matrix(resultData, {
|
||||
rows: this.rows,
|
||||
cols: matrix.cols,
|
||||
addFn: this.addFn,
|
||||
subtractFn: this.subtractFn,
|
||||
multiplyFn: this.multiplyFn
|
||||
});
|
||||
}
|
||||
|
||||
protected _addFn(a: number | undefined, b: number): number | undefined {
|
||||
if (a === undefined) return b;
|
||||
return a + b;
|
||||
}
|
||||
|
||||
protected _subtractFn(a: number, b: number) {
|
||||
return a - b;
|
||||
}
|
||||
|
||||
protected _multiplyFn(a: number, b: number) {
|
||||
return a * b;
|
||||
}
|
||||
|
||||
/**
|
||||
* The function checks if a given row and column index is valid within a specified range.
|
||||
* @param {number} row - The `row` parameter represents the row index of a two-dimensional array or
|
||||
* matrix. It is a number that indicates the specific row in the matrix.
|
||||
* @param {number} col - The "col" parameter represents the column index in a two-dimensional array
|
||||
* or grid. It is used to check if the given column index is valid within the bounds of the grid.
|
||||
* @returns A boolean value is being returned.
|
||||
*/
|
||||
protected isValidIndex(row: number, col: number): boolean {
|
||||
return row >= 0 && row < this.rows && col >= 0 && col < this.cols;
|
||||
}
|
||||
|
||||
/**
|
||||
* The function `_swapRows` swaps the positions of two rows in an array.
|
||||
* @param {number} row1 - The `row1` parameter is the index of the first row that you want to swap.
|
||||
* @param {number} row2 - The `row2` parameter is the index of the second row that you want to swap
|
||||
* with the first row.
|
||||
*/
|
||||
protected _swapRows(row1: number, row2: number): void {
|
||||
const temp = this.data[row1];
|
||||
this.data[row1] = this.data[row2];
|
||||
this.data[row2] = temp;
|
||||
}
|
||||
|
||||
/**
|
||||
* The function scales a specific row in a matrix by a given scalar value.
|
||||
* @param {number} row - The `row` parameter represents the index of the row in the matrix that you
|
||||
* want to scale. It is a number that indicates the position of the row within the matrix.
|
||||
* @param {number} scalar - The scalar parameter is a number that is used to multiply each element in
|
||||
* a specific row of a matrix.
|
||||
*/
|
||||
protected _scaleRow(row: number, scalar: number): void {
|
||||
for (let j = 0; j < this.cols; j++) {
|
||||
let multiplied = this.multiplyFn(this.data[row][j], scalar);
|
||||
if (multiplied === undefined) multiplied = 0;
|
||||
this.data[row][j] = multiplied;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The function `_addScaledRow` multiplies a row in a matrix by a scalar value and adds it to another
|
||||
* row.
|
||||
* @param {number} targetRow - The targetRow parameter represents the index of the row in which the
|
||||
* scaled values will be added.
|
||||
* @param {number} sourceRow - The sourceRow parameter represents the index of the row from which the
|
||||
* values will be scaled and added to the targetRow.
|
||||
* @param {number} scalar - The scalar parameter is a number that is used to scale the values in the
|
||||
* source row before adding them to the target row.
|
||||
*/
|
||||
protected _addScaledRow(targetRow: number, sourceRow: number, scalar: number): void {
|
||||
for (let j = 0; j < this.cols; j++) {
|
||||
let multiplied = this.multiplyFn(this.data[sourceRow][j], scalar);
|
||||
if (multiplied === undefined) multiplied = 0;
|
||||
const scaledValue = multiplied;
|
||||
let added = this.addFn(this.data[targetRow][j], scaledValue);
|
||||
if (added === undefined) added = 0;
|
||||
this.data[targetRow][j] = added;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,211 +0,0 @@
|
|||
/**
|
||||
* data-structure-typed
|
||||
*
|
||||
* @author Tyler Zeng
|
||||
* @copyright Copyright (c) 2022 Tyler Zeng <zrwusa@gmail.com>
|
||||
* @license MIT License
|
||||
*/
|
||||
import { Vector2D } from './vector2d';
|
||||
|
||||
export class Matrix2D {
|
||||
protected readonly _matrix: number[][];
|
||||
|
||||
/**
|
||||
* The constructor function initializes a Matrix2D object with either a default identity matrix, or a provided matrix
|
||||
* or Vector2D object.
|
||||
* @param {number[][] | Vector2D} [value] - The `value` parameter can be either a 2D array of numbers (`number[][]`) or
|
||||
* an instance of the `Vector2D` class.
|
||||
*/
|
||||
constructor(value?: number[][] | Vector2D) {
|
||||
if (typeof value === 'undefined') {
|
||||
this._matrix = Matrix2D.identity;
|
||||
} else if (value instanceof Vector2D) {
|
||||
this._matrix = Matrix2D.identity;
|
||||
this._matrix[0][0] = value.x;
|
||||
this._matrix[1][0] = value.y;
|
||||
this._matrix[2][0] = value.w;
|
||||
} else {
|
||||
this._matrix = value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The function returns a 2D array with three empty arrays.
|
||||
* @returns An empty 2-dimensional array with 3 empty arrays inside.
|
||||
*/
|
||||
static get empty(): number[][] {
|
||||
return [[], [], []];
|
||||
}
|
||||
|
||||
/**
|
||||
* The above function returns a 3x3 identity matrix.
|
||||
* @returns The method is returning a 2-dimensional array of numbers representing the identity matrix.
|
||||
*/
|
||||
static get identity(): number[][] {
|
||||
return [
|
||||
[1, 0, 0],
|
||||
[0, 1, 0],
|
||||
[0, 0, 1]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* The function returns a two-dimensional array of numbers.
|
||||
* @returns The getter method is returning the value of the private variable `_matrix`, which is a two-dimensional
|
||||
* array of numbers.
|
||||
*/
|
||||
get m(): number[][] {
|
||||
return this._matrix;
|
||||
}
|
||||
|
||||
/**
|
||||
* The function takes two 2D matrices as input and returns their sum as a new 2D matrix.
|
||||
* @param {Matrix2D} matrix1 - Matrix2D - The first matrix to be added.
|
||||
* @param {Matrix2D} matrix2 - The parameter `matrix2` is a Matrix2D object.
|
||||
* @returns a new instance of the Matrix2D class, which is created using the result array.
|
||||
*/
|
||||
static add(matrix1: Matrix2D, matrix2: Matrix2D): Matrix2D {
|
||||
const result = Matrix2D.empty;
|
||||
for (let i = 0; i < 3; i++) {
|
||||
for (let j = 0; j < 3; j++) {
|
||||
result[i][j] = matrix1.m[i][j] + matrix2.m[i][j];
|
||||
}
|
||||
}
|
||||
return new Matrix2D(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* The function subtracts two 2D matrices and returns the result as a new Matrix2D object.
|
||||
* @param {Matrix2D} matrix1 - Matrix2D - The first matrix to subtract from.
|
||||
* @param {Matrix2D} matrix2 - Matrix2D is a class representing a 2D matrix. It has a property `m` which is a 2D array
|
||||
* representing the matrix elements.
|
||||
* @returns a new instance of the Matrix2D class, which is created using the result array.
|
||||
*/
|
||||
static subtract(matrix1: Matrix2D, matrix2: Matrix2D): Matrix2D {
|
||||
const result = Matrix2D.empty;
|
||||
for (let i = 0; i < 3; i++) {
|
||||
for (let j = 0; j < 3; j++) {
|
||||
result[i][j] = matrix1.m[i][j] - matrix2.m[i][j];
|
||||
}
|
||||
}
|
||||
return new Matrix2D(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* The function multiplies two 2D matrices and returns the result as a new Matrix2D object.
|
||||
* @param {Matrix2D} matrix1 - A 2D matrix represented by the Matrix2D class.
|
||||
* @param {Matrix2D} matrix2 - The parameter `matrix2` is a 2D matrix of size 3x3.
|
||||
* @returns a new instance of the Matrix2D class, created using the result array.
|
||||
*/
|
||||
static multiply(matrix1: Matrix2D, matrix2: Matrix2D): Matrix2D {
|
||||
const result = Matrix2D.empty;
|
||||
for (let i = 0; i < 3; i++) {
|
||||
for (let j = 0; j < 3; j++) {
|
||||
result[i][j] = 0;
|
||||
for (let k = 0; k < 3; k++) {
|
||||
result[i][j] += matrix1.m[i][k] * matrix2.m[k][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
return new Matrix2D(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* The function multiplies each element of a 2D matrix by a given value and returns the resulting matrix.
|
||||
* @param {Matrix2D} matrix - The `matrix` parameter is an instance of the `Matrix2D` class, which represents a 2D
|
||||
* matrix. It contains a property `m` that is a 2D array representing the matrix elements.
|
||||
* @param {number} value - The `value` parameter is a number that you want to multiply each element of the `matrix` by.
|
||||
* @returns a new instance of the Matrix2D class, which is created using the result array.
|
||||
*/
|
||||
static multiplyByValue(matrix: Matrix2D, value: number): Matrix2D {
|
||||
const result = Matrix2D.empty;
|
||||
for (let i = 0; i < 3; i++) {
|
||||
for (let j = 0; j < 3; j++) {
|
||||
result[i][j] = matrix.m[i][j] * value;
|
||||
}
|
||||
}
|
||||
return new Matrix2D(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* The function multiplies a 2D matrix by a 2D vector and returns the result as a 2D vector.
|
||||
* @param {Matrix2D} matrix - The parameter "matrix" is of type Matrix2D. It represents a 2-dimensional matrix.
|
||||
* @param {Vector2D} vector - The "vector" parameter is a 2D vector, represented by an object of type Vector2D.
|
||||
* @returns a Vector2D.
|
||||
*/
|
||||
static multiplyByVector(matrix: Matrix2D, vector: Vector2D): Vector2D {
|
||||
const resultMatrix = Matrix2D.multiply(matrix, new Matrix2D(vector));
|
||||
return resultMatrix.toVector();
|
||||
}
|
||||
|
||||
/**
|
||||
* The function returns a 2D matrix that scales and flips a vector around the center of a given width and height.
|
||||
* @param {number} width - The width parameter represents the width of the view or the canvas. It is a number that
|
||||
* specifies the width in pixels or any other unit of measurement.
|
||||
* @param {number} height - The height parameter represents the height of the view or the canvas. It is used to
|
||||
* calculate the centerY value, which is the vertical center of the view.
|
||||
* @returns a Matrix2D object.
|
||||
*/
|
||||
static view(width: number, height: number): Matrix2D {
|
||||
const scaleStep = 1; // Scale every vector * scaleStep
|
||||
const centerX = width / 2;
|
||||
const centerY = height / 2;
|
||||
const flipX = Math.cos(Math.PI); // rotate 180deg / 3.14radian around X-axis
|
||||
|
||||
return new Matrix2D([
|
||||
[scaleStep, 0, centerX],
|
||||
[0, flipX * scaleStep, centerY],
|
||||
[0, 0, 1]
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* The function scales a matrix by a given factor.
|
||||
* @param {number} factor - The factor parameter is a number that represents the scaling factor by which the matrix
|
||||
* should be scaled.
|
||||
* @returns the result of multiplying a new instance of Matrix2D by the given factor.
|
||||
*/
|
||||
static scale(factor: number) {
|
||||
return Matrix2D.multiplyByValue(new Matrix2D(), factor);
|
||||
}
|
||||
|
||||
/**
|
||||
* The function "rotate" takes an angle in radians and returns a 2D transformation matrix for rotating objects.
|
||||
* @param {number} radians - The "radians" parameter is the angle in radians by which you want to rotate an object.
|
||||
* @returns The code is returning a new instance of a Matrix2D object.
|
||||
*/
|
||||
static rotate(radians: number) {
|
||||
const cos = Math.cos(radians);
|
||||
const sin = Math.sin(radians);
|
||||
|
||||
return new Matrix2D([
|
||||
[cos, -sin, 0],
|
||||
[sin, cos, 0],
|
||||
[0, 0, 1]
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* The translate function takes a 2D vector and returns a 2D matrix that represents a translation transformation.
|
||||
* @param {Vector2D} vector - The parameter "vector" is of type Vector2D. It represents a 2D vector with components x
|
||||
* and y, and an optional w component.
|
||||
* @returns The method is returning a new instance of the Matrix2D class.
|
||||
*/
|
||||
static translate(vector: Vector2D): Matrix2D {
|
||||
return new Matrix2D([
|
||||
[1, 0, vector.x],
|
||||
[0, 1, vector.y],
|
||||
[0, 0, vector.w]
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* The function "toVector" returns a new Vector2D object with the values from the first and second elements of the
|
||||
* _matrix array.
|
||||
* @returns A new instance of the Vector2D class is being returned. The values of the returned vector are taken from
|
||||
* the first column of the matrix.
|
||||
*/
|
||||
toVector(): Vector2D {
|
||||
return new Vector2D(this._matrix[0][0], this._matrix[1][0]);
|
||||
}
|
||||
}
|
|
@ -1,315 +0,0 @@
|
|||
/**
|
||||
* data-structure-typed
|
||||
*
|
||||
* @author Tyler Zeng
|
||||
* @copyright Copyright (c) 2022 Tyler Zeng <zrwusa@gmail.com>
|
||||
* @license MIT License
|
||||
*/
|
||||
export class Vector2D {
|
||||
constructor(
|
||||
public x: number = 0,
|
||||
public y: number = 0,
|
||||
public w: number = 1 // needed for matrix multiplication
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* The function checks if the x and y values of a point are both zero.
|
||||
* @returns A boolean value indicating whether both the x and y properties of the object are equal to 0.
|
||||
*/
|
||||
get isZero(): boolean {
|
||||
return this.x === 0 && this.y === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* The above function calculates the length of a vector using the Pythagorean theorem.
|
||||
* @returns The length of a vector, calculated using the Pythagorean theorem.
|
||||
*/
|
||||
get length(): number {
|
||||
return Math.sqrt(this.x * this.x + this.y * this.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* The function calculates the square of the length of a vector.
|
||||
* @returns The method is returning the sum of the squares of the x and y values.
|
||||
*/
|
||||
get lengthSq(): number {
|
||||
return this.x * this.x + this.y * this.y;
|
||||
}
|
||||
|
||||
/**
|
||||
* The "rounded" function returns a new Vector2D object with the x and y values rounded to the nearest whole number.
|
||||
* @returns The method is returning a new instance of the Vector2D class with the x and y values rounded to the nearest
|
||||
* whole number.
|
||||
*/
|
||||
get rounded(): Vector2D {
|
||||
return new Vector2D(Math.round(this.x), Math.round(this.y));
|
||||
}
|
||||
|
||||
/**
|
||||
* The function "add" takes two Vector2D objects as parameters and returns a new Vector2D object with the sum of their
|
||||
* x and y components.
|
||||
* @param {Vector2D} vector1 - The parameter `vector1` is an instance of the `Vector2D` class. It represents a
|
||||
* 2-dimensional vector with an `x` and `y` component.
|
||||
* @param {Vector2D} vector2 - The parameter "vector2" is of type Vector2D. It represents a 2-dimensional vector with
|
||||
* an x and y component.
|
||||
* @returns The method is returning a new instance of the Vector2D class with the x and y components of the two input
|
||||
* vectors added together.
|
||||
*/
|
||||
static add(vector1: Vector2D, vector2: Vector2D): Vector2D {
|
||||
return new Vector2D(vector1.x + vector2.x, vector1.y + vector2.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* The subtract function takes two Vector2D objects as parameters and returns a new Vector2D object with the x and y
|
||||
* components subtracted.
|
||||
* @param {Vector2D} vector1 - The parameter `vector1` is an instance of the `Vector2D` class, representing a
|
||||
* 2-dimensional vector. It has properties `x` and `y` which represent the x and y components of the vector
|
||||
* respectively.
|
||||
* @param {Vector2D} vector2 - The parameter "vector2" is a Vector2D object. It represents the second vector that you
|
||||
* want to subtract from the first vector.
|
||||
* @returns The method is returning a new Vector2D object with the x and y components subtracted from vector1 and
|
||||
* vector2.
|
||||
*/
|
||||
static subtract(vector1: Vector2D, vector2: Vector2D): Vector2D {
|
||||
return new Vector2D(vector1.x - vector2.x, vector1.y - vector2.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* The function subtracts a given value from the x and y components of a Vector2D object and returns a new Vector2D
|
||||
* object.
|
||||
* @param {Vector2D} vector - The parameter "vector" is of type Vector2D, which represents a 2-dimensional vector with
|
||||
* x and y components.
|
||||
* @param {number} value - The "value" parameter is a number that will be subtracted from both the x and y components
|
||||
* of the "vector" parameter.
|
||||
* @returns A new Vector2D object with the x and y values subtracted by the given value.
|
||||
*/
|
||||
static subtractValue(vector: Vector2D, value: number): Vector2D {
|
||||
return new Vector2D(vector.x - value, vector.y - value);
|
||||
}
|
||||
|
||||
/**
|
||||
* The function multiplies a Vector2D object by a given value.
|
||||
* @param {Vector2D} vector - The parameter "vector" is of type Vector2D, which represents a 2-dimensional vector with
|
||||
* x and y components.
|
||||
* @param {number} value - The "value" parameter is a number that represents the value by which the x and y components
|
||||
* of the vector will be multiplied.
|
||||
* @returns A new Vector2D object with the x and y values multiplied by the given value.
|
||||
*/
|
||||
static multiply(vector: Vector2D, value: number): Vector2D {
|
||||
return new Vector2D(vector.x * value, vector.y * value);
|
||||
}
|
||||
|
||||
/**
|
||||
* The function divides the x and y components of a Vector2D by a given value and returns a new Vector2D.
|
||||
* @param {Vector2D} vector - The parameter "vector" is of type Vector2D, which represents a 2-dimensional vector with
|
||||
* x and y components.
|
||||
* @param {number} value - The value parameter is a number that will be used to divide the x and y components of the
|
||||
* vector.
|
||||
* @returns A new instance of the Vector2D class with the x and y values divided by the given value.
|
||||
*/
|
||||
static divide(vector: Vector2D, value: number): Vector2D {
|
||||
return new Vector2D(vector.x / value, vector.y / value);
|
||||
}
|
||||
|
||||
/**
|
||||
* The function checks if two Vector2D objects are equal by comparing their x and y values.
|
||||
* @param {Vector2D} vector1 - The parameter `vector1` is of type `Vector2D`, which represents a 2-dimensional vector.
|
||||
* It has two properties: `x` and `y`, which represent the x and y components of the vector, respectively.
|
||||
* @param {Vector2D} vector2 - The parameter "vector2" is of type Vector2D.
|
||||
* @returns a boolean value, which indicates whether the two input vectors are equal or not.
|
||||
*/
|
||||
static equals(vector1: Vector2D, vector2: Vector2D): boolean {
|
||||
return vector1.x === vector2.x && vector1.y === vector2.y;
|
||||
}
|
||||
|
||||
/**
|
||||
* The function checks if two Vector2D objects are equal within a specified rounding factor.
|
||||
* @param {Vector2D} vector1 - The first vector to compare.
|
||||
* @param {Vector2D} vector2 - The parameter "vector2" is a Vector2D object, which represents a 2-dimensional vector.
|
||||
* It is used as one of the inputs for the "equalsRounded" function.
|
||||
* @param [roundingFactor=12] - The roundingFactor parameter is used to determine the threshold for considering two
|
||||
* vectors as equal. If the absolute difference in the x and y components of the vectors is less than the
|
||||
* roundingFactor, the vectors are considered equal.
|
||||
* @returns a boolean value.
|
||||
*/
|
||||
static equalsRounded(vector1: Vector2D, vector2: Vector2D, roundingFactor = 12): boolean {
|
||||
const vector = Vector2D.abs(Vector2D.subtract(vector1, vector2));
|
||||
if (vector.x < roundingFactor && vector.y < roundingFactor) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* The normalize function takes a vector as input and returns a normalized version of the vector.Normalizes the vector if it matches a certain condition
|
||||
* @param {Vector2D} vector - The parameter "vector" is of type Vector2D.
|
||||
* @returns the normalized vector if its length is greater than a very small value (epsilon), otherwise it returns the
|
||||
* original vector.
|
||||
*/
|
||||
static normalize(vector: Vector2D): Vector2D {
|
||||
const length = vector.length;
|
||||
if (length > 2.220446049250313e-16) {
|
||||
// Epsilon
|
||||
return Vector2D.divide(vector, length);
|
||||
}
|
||||
|
||||
return vector;
|
||||
}
|
||||
|
||||
/**
|
||||
* The function truncates a vector to a maximum length if it exceeds that length.Adjusts x and y so that the length of the vector does not exceed max
|
||||
* @param {Vector2D} vector - A 2D vector represented by the Vector2D class.
|
||||
* @param {number} max - The `max` parameter is a number that represents the maximum length that the `vector` should
|
||||
* have.
|
||||
* @returns either the original vector or a truncated version of the vector, depending on whether the length of the
|
||||
* vector is greater than the maximum value specified.
|
||||
*/
|
||||
static truncate(vector: Vector2D, max: number): Vector2D {
|
||||
if (vector.length > max) {
|
||||
return Vector2D.multiply(Vector2D.normalize(vector), max);
|
||||
}
|
||||
|
||||
return vector;
|
||||
}
|
||||
|
||||
/**
|
||||
* The function returns a new Vector2D object that is perpendicular to the input vector.The vector that is perpendicular to this one
|
||||
* @param {Vector2D} vector - The parameter "vector" is of type Vector2D.
|
||||
* @returns A new Vector2D object is being returned.
|
||||
*/
|
||||
static perp(vector: Vector2D): Vector2D {
|
||||
return new Vector2D(-vector.y, vector.x);
|
||||
}
|
||||
|
||||
/**
|
||||
* The reverse function takes a Vector2D object and returns a new Vector2D object with the negated x and y values.
|
||||
* @param {Vector2D} vector - The parameter "vector" is of type Vector2D, which represents a 2-dimensional vector. It
|
||||
* has two properties: "x" and "y", which represent the x and y components of the vector, respectively.
|
||||
* @returns A new Vector2D object with the negated x and y values of the input vector. Returns the vector that is the reverse of this vector
|
||||
*/
|
||||
static reverse(vector: Vector2D): Vector2D {
|
||||
return new Vector2D(-vector.x, -vector.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* The function takes a Vector2D object as input and returns a new Vector2D object with the absolute values of its x
|
||||
* and y components.
|
||||
* @param {Vector2D} vector - The parameter "vector" is of type Vector2D, which represents a 2-dimensional vector. It
|
||||
* has two properties: "x" and "y", which represent the x and y components of the vector, respectively.
|
||||
* @returns The method is returning a new Vector2D object with the absolute values of the x and y components of the
|
||||
* input vector.
|
||||
*/
|
||||
static abs(vector: Vector2D): Vector2D {
|
||||
return new Vector2D(Math.abs(vector.x), Math.abs(vector.y));
|
||||
}
|
||||
|
||||
/**
|
||||
* The dot function calculates the dot product of two 2D vectors.The dot product of v1 and v2
|
||||
* @param {Vector2D} vector1 - The parameter `vector1` represents a 2D vector with its x and y components.
|
||||
* @param {Vector2D} vector2 - The "vector2" parameter is a Vector2D object. It represents a two-dimensional vector
|
||||
* with an x and y component.
|
||||
* @returns The dot product of the two input vectors.
|
||||
*/
|
||||
static dot(vector1: Vector2D, vector2: Vector2D): number {
|
||||
return vector1.x * vector2.x + vector1.y * vector2.y;
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Transform vectors based on the current tranformation matrices: translation, rotation and scale
|
||||
// * @param vectors The vectors to transform
|
||||
// */
|
||||
// static transform(vector: Vector2D, transformation: Matrix2D): Vector2D {
|
||||
// return Matrix2D.multiplyByVector(transformation, vector)
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Transform vectors based on the current tranformation matrices: translation, rotation and scale
|
||||
// * @param vectors The vectors to transform
|
||||
// */
|
||||
// static transformList(vectors: Vector2D[], transformation: Matrix2D): Vector2D[] {
|
||||
// return vectors.map(vector => Matrix2D.multiplyByVector(transformation, vector))
|
||||
// }
|
||||
|
||||
/**
|
||||
* The function calculates the distance between two points in a two-dimensional space.
|
||||
* @param {Vector2D} vector1 - The parameter `vector1` represents the first vector in 2D space, while `vector2`
|
||||
* represents the second vector. Each vector has an `x` and `y` component, which represent their respective coordinates
|
||||
* in the 2D space.
|
||||
* @param {Vector2D} vector2 - The `vector2` parameter represents the second vector in the calculation of distance. It
|
||||
* is an instance of the `Vector2D` class, which typically has properties `x` and `y` representing the coordinates of
|
||||
* the vector in a 2D space.
|
||||
* @returns The distance between vector1 and vector2.
|
||||
*/
|
||||
static distance(vector1: Vector2D, vector2: Vector2D): number {
|
||||
const ySeparation = vector2.y - vector1.y;
|
||||
const xSeparation = vector2.x - vector1.x;
|
||||
return Math.sqrt(ySeparation * ySeparation + xSeparation * xSeparation);
|
||||
}
|
||||
|
||||
/**
|
||||
* The function calculates the squared distance between two 2D vectors.
|
||||
* @param {Vector2D} vector1 - The parameter `vector1` represents the first vector, which is an instance of the
|
||||
* `Vector2D` class. It contains the x and y coordinates of the vector.
|
||||
* @param {Vector2D} vector2 - The `vector2` parameter represents the second vector in a two-dimensional space. It has
|
||||
* properties `x` and `y` which represent the coordinates of the vector.
|
||||
* @returns the square of the distance between the two input vectors.
|
||||
*/
|
||||
static distanceSq(vector1: Vector2D, vector2: Vector2D): number {
|
||||
const ySeparation = vector2.y - vector1.y;
|
||||
const xSeparation = vector2.x - vector1.x;
|
||||
return ySeparation * ySeparation + xSeparation * xSeparation;
|
||||
}
|
||||
|
||||
/**
|
||||
* The sign function determines the sign of the cross product between two 2D vectors.
|
||||
* (assuming the Y axis is pointing down, X axis to right like a Window app)
|
||||
* @param {Vector2D} vector1 - The parameter `vector1` is of type `Vector2D`, which represents a 2-dimensional vector.
|
||||
* It likely has properties `x` and `y` representing the x and y components of the vector, respectively.
|
||||
* @param {Vector2D} vector2 - The above code defines a function called "sign" that takes two parameters: vector1 and
|
||||
* vector2. Both vector1 and vector2 are of type Vector2D.
|
||||
* @returns either -1 or 1. Returns positive if v2 is clockwise of this vector, negative if counterclockwise
|
||||
*/
|
||||
static sign(vector1: Vector2D, vector2: Vector2D): number {
|
||||
if (vector1.y * vector2.x > vector1.x * vector2.y) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* The function calculates the angle between a given vector and the negative y-axis.
|
||||
* @param {Vector2D} vector - The "vector" parameter is an instance of the Vector2D class, which represents a
|
||||
* 2-dimensional vector. It has two properties: "x" and "y", which represent the x and y components of the vector,
|
||||
* respectively.
|
||||
* @returns the angle between the given vector and the vector (0, -1) in radians.Returns the angle between origin and the given vector in radians
|
||||
*/
|
||||
static angle(vector: Vector2D): number {
|
||||
const origin = new Vector2D(0, -1);
|
||||
const radian = Math.acos(Vector2D.dot(vector, origin) / (vector.length * origin.length));
|
||||
return Vector2D.sign(vector, origin) === 1 ? Math.PI * 2 - radian : radian;
|
||||
}
|
||||
|
||||
/**
|
||||
* The function "random" generates a random Vector2D object with x and y values within the specified range.
|
||||
* @param {number} maxX - The maxX parameter represents the maximum value for the x-coordinate of the random vector.
|
||||
* @param {number} maxY - The `maxY` parameter represents the maximum value for the y-coordinate of the generated
|
||||
* random vector.
|
||||
* @returns a new instance of the Vector2D class with random x and y values.
|
||||
*/
|
||||
static random(maxX: number, maxY: number): Vector2D {
|
||||
const randX = Math.floor(Math.random() * maxX - maxX / 2);
|
||||
const randY = Math.floor(Math.random() * maxY - maxY / 2);
|
||||
return new Vector2D(randX, randY);
|
||||
}
|
||||
|
||||
/**
|
||||
* The function sets the values of x and y to zero.
|
||||
*/
|
||||
zero(): void {
|
||||
this.x = 0;
|
||||
this.y = 0;
|
||||
}
|
||||
}
|
|
@ -100,3 +100,8 @@ export const isWeakKey = (input: unknown): input is object => {
|
|||
|
||||
export const calcMinUnitsRequired = (totalQuantity: number, unitSize: number) =>
|
||||
Math.floor((totalQuantity + unitSize - 1) / unitSize);
|
||||
|
||||
export const roundFixed = (num: number, digit: number = 10) => {
|
||||
const multiplier = Math.pow(10, digit);
|
||||
return Math.round(num * multiplier) / multiplier;
|
||||
};
|
||||
|
|
|
@ -7,7 +7,7 @@ describe('Individual package BST operations test', () => {
|
|||
bst.add([11, 11]);
|
||||
bst.add([3, 3]);
|
||||
const idsOrValues = [15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5];
|
||||
bst.addMany(idsOrValues, false);
|
||||
bst.addMany(idsOrValues, undefined);
|
||||
expect(bst.root).toBeInstanceOf(BSTNode);
|
||||
|
||||
if (bst.root) expect(bst.root.key).toBe(11);
|
||||
|
@ -204,7 +204,7 @@ describe('Individual package BST operations test', () => {
|
|||
[5, { key: 5, keyA: 5 }]
|
||||
];
|
||||
|
||||
objBST.addMany(values, false);
|
||||
objBST.addMany(values, undefined);
|
||||
|
||||
expect(objBST.root).toBeInstanceOf(BSTNode);
|
||||
|
||||
|
|
|
@ -1,54 +1,347 @@
|
|||
import { MatrixNTI2D } from '../../../../src';
|
||||
import { Matrix } from '../../../../src';
|
||||
|
||||
describe('MatrixNTI2D', () => {
|
||||
it('should initialize a matrix with rows and columns', () => {
|
||||
const numRows = 3;
|
||||
const numCols = 4;
|
||||
const matrix = new MatrixNTI2D({ row: numRows, col: numCols });
|
||||
describe('Matrix', () => {
|
||||
let matrix: Matrix;
|
||||
|
||||
expect(matrix.toArray().length).toBe(numRows);
|
||||
expect(matrix.toArray()[0].length).toBe(numCols);
|
||||
beforeEach(() => {
|
||||
// Initialize a 3x3 matrix with zeros
|
||||
matrix = new Matrix([
|
||||
[0, 0, 0],
|
||||
[0, 0, 0],
|
||||
[0, 0, 0]
|
||||
]);
|
||||
});
|
||||
|
||||
it('should initialize all elements with the provided initial value', () => {
|
||||
const numRows = 3;
|
||||
const numCols = 4;
|
||||
const initialValue = 42;
|
||||
const matrix = new MatrixNTI2D({ row: numRows, col: numCols, initialVal: initialValue });
|
||||
|
||||
const matrixArray = matrix.toArray();
|
||||
for (let i = 0; i < numRows; i++) {
|
||||
for (let j = 0; j < numCols; j++) {
|
||||
expect(matrixArray[i][j]).toBe(initialValue);
|
||||
}
|
||||
}
|
||||
it('should create a matrix with the correct dimensions and initial values', () => {
|
||||
expect(matrix.rows).toBe(3);
|
||||
expect(matrix.cols).toBe(3);
|
||||
expect(matrix.data).toEqual([
|
||||
[0, 0, 0],
|
||||
[0, 0, 0],
|
||||
[0, 0, 0]
|
||||
]);
|
||||
});
|
||||
|
||||
it('should initialize all elements with 0 if no initial value is provided', () => {
|
||||
const numRows = 3;
|
||||
const numCols = 4;
|
||||
const matrix = new MatrixNTI2D({ row: numRows, col: numCols });
|
||||
|
||||
const matrixArray = matrix.toArray();
|
||||
for (let i = 0; i < numRows; i++) {
|
||||
for (let j = 0; j < numCols; j++) {
|
||||
expect(matrixArray[i][j]).toBe(0);
|
||||
}
|
||||
}
|
||||
it('should get a value at a specific position', () => {
|
||||
expect(matrix.get(1, 1)).toBe(0);
|
||||
expect(matrix.get(2, 2)).toBe(0);
|
||||
});
|
||||
|
||||
it('should convert the matrix to a two-dimensional array', () => {
|
||||
const numRows = 2;
|
||||
const numCols = 3;
|
||||
const matrix = new MatrixNTI2D({ row: numRows, col: numCols, initialVal: 1 });
|
||||
it('should set a value at a specific position', () => {
|
||||
matrix.set(1, 1, 42);
|
||||
expect(matrix.get(1, 1)).toBe(42);
|
||||
});
|
||||
|
||||
const matrixArray = matrix.toArray();
|
||||
expect(matrixArray.length).toBe(numRows);
|
||||
for (let i = 0; i < numRows; i++) {
|
||||
expect(matrixArray[i].length).toBe(numCols);
|
||||
for (let j = 0; j < numCols; j++) {
|
||||
expect(matrixArray[i][j]).toBe(1);
|
||||
}
|
||||
}
|
||||
it('should not allow getting or setting values at invalid positions', () => {
|
||||
expect(matrix.get(-1, 0)).toBeUndefined();
|
||||
expect(matrix.get(0, 10)).toBeUndefined();
|
||||
|
||||
const originalValue = matrix.get(1, 1);
|
||||
matrix.set(-1, 1, 42);
|
||||
matrix.set(1, 10, 42);
|
||||
expect(matrix.get(1, 1)).toBe(originalValue);
|
||||
});
|
||||
|
||||
describe('add', () => {
|
||||
it('should add two matrices correctly', () => {
|
||||
const matrixA = new Matrix([
|
||||
[1, 2],
|
||||
[3, 4]
|
||||
]);
|
||||
const matrixB = new Matrix([
|
||||
[5, 6],
|
||||
[7, 8]
|
||||
]);
|
||||
const expectedResult = new Matrix([
|
||||
[6, 8],
|
||||
[10, 12]
|
||||
]);
|
||||
|
||||
const result = matrixA.add(matrixB);
|
||||
|
||||
expect(result?.data).toEqual(expectedResult.data);
|
||||
});
|
||||
|
||||
it('should throw an error for matrices with mismatched dimensions', () => {
|
||||
const matrixA = new Matrix([
|
||||
[1, 2],
|
||||
[3, 4]
|
||||
]);
|
||||
const matrixB = new Matrix([
|
||||
[5, 6, 7],
|
||||
[8, 9, 10]
|
||||
]);
|
||||
|
||||
expect(() => matrixA.add(matrixB)).toThrowError('Matrix dimensions must match for addition.');
|
||||
});
|
||||
|
||||
it('should throw an error for matrices with mismatched dimensions', () => {
|
||||
const matrixA = new Matrix([
|
||||
[1, 2],
|
||||
[3, 4]
|
||||
]);
|
||||
const matrixB = new Matrix([
|
||||
[5, 6, 7],
|
||||
[8, 9, 10]
|
||||
]);
|
||||
|
||||
expect(() => matrixA.add(matrixB)).toThrowError('Matrix dimensions must match for addition.');
|
||||
});
|
||||
});
|
||||
|
||||
describe('subtract', () => {
|
||||
it('should subtract two matrices with numbers correctly', () => {
|
||||
const matrixA = new Matrix([
|
||||
[5, 6],
|
||||
[7, 8]
|
||||
]);
|
||||
const matrixB = new Matrix([
|
||||
[1, 2],
|
||||
[3, 4]
|
||||
]);
|
||||
const expectedResult = new Matrix([
|
||||
[4, 4],
|
||||
[4, 4]
|
||||
]);
|
||||
|
||||
const result = matrixA.subtract(matrixB);
|
||||
|
||||
expect(result?.data).toEqual(expectedResult.data);
|
||||
});
|
||||
|
||||
it('should subtract two matrices with custom subtract function correctly', () => {
|
||||
const customSubtractFn = (a: number, b: number) => a * 10 - b; // Custom subtraction for arrays
|
||||
const matrixA = new Matrix(
|
||||
[
|
||||
[5, 6],
|
||||
[7, 8]
|
||||
],
|
||||
{ subtractFn: customSubtractFn }
|
||||
);
|
||||
const matrixB = new Matrix(
|
||||
[
|
||||
[1, 2],
|
||||
[3, 4]
|
||||
],
|
||||
{ subtractFn: customSubtractFn }
|
||||
);
|
||||
const expectedResult = new Matrix(
|
||||
[
|
||||
[49, 58],
|
||||
[67, 76]
|
||||
],
|
||||
{ subtractFn: customSubtractFn }
|
||||
);
|
||||
|
||||
const result = matrixA.subtract(matrixB);
|
||||
|
||||
expect(result?.data).toEqual(expectedResult.data);
|
||||
});
|
||||
|
||||
it('should throw an error for matrices with mismatched dimensions', () => {
|
||||
const matrixA = new Matrix([
|
||||
[1, 2],
|
||||
[3, 4]
|
||||
]);
|
||||
const matrixB = new Matrix([
|
||||
[5, 6, 7],
|
||||
[8, 9, 10]
|
||||
]);
|
||||
|
||||
expect(() => matrixA.subtract(matrixB)).toThrowError('Matrix dimensions must match for subtraction.');
|
||||
});
|
||||
});
|
||||
|
||||
describe('multiply', () => {
|
||||
it('should multiply two matrices with numbers correctly', () => {
|
||||
const matrixA = new Matrix([
|
||||
[1, 2],
|
||||
[3, 4]
|
||||
]);
|
||||
const matrixB = new Matrix([
|
||||
[5, 6],
|
||||
[7, 8]
|
||||
]);
|
||||
const expectedResult = new Matrix([
|
||||
[19, 22],
|
||||
[43, 50]
|
||||
]);
|
||||
|
||||
const result = matrixA.multiply(matrixB);
|
||||
|
||||
expect(result?.data).toEqual(expectedResult.data);
|
||||
});
|
||||
|
||||
it('should multiply two matrices with custom multiply function correctly', () => {
|
||||
const customMultiplyFn = (a: number, b: number) => a * 10 * b; // Custom multiplication for arrays
|
||||
const matrixA = new Matrix(
|
||||
[
|
||||
[1, 2],
|
||||
[3, 4]
|
||||
],
|
||||
{ multiplyFn: customMultiplyFn }
|
||||
);
|
||||
const matrixB = new Matrix(
|
||||
[
|
||||
[5, 6],
|
||||
[7, 8]
|
||||
],
|
||||
{ multiplyFn: customMultiplyFn }
|
||||
);
|
||||
const result = matrixA.multiply(matrixB);
|
||||
|
||||
expect(result?.data).toEqual([
|
||||
[190, 220],
|
||||
[430, 500]
|
||||
]);
|
||||
});
|
||||
|
||||
it('should throw an error for matrices with mismatched dimensions', () => {
|
||||
const matrixA = new Matrix([
|
||||
[1, 2, 3],
|
||||
[3, 4, 5]
|
||||
]);
|
||||
const matrixB = new Matrix([
|
||||
[5, 6, 7],
|
||||
[8, 9, 1]
|
||||
]);
|
||||
|
||||
expect(() => matrixA.multiply(matrixB)).toThrowError(
|
||||
'Matrix dimensions must be compatible for multiplication (A.cols = B.rows).'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('transpose', () => {
|
||||
test('should transpose a matrix with numeric values correctly', () => {
|
||||
const originalMatrix = new Matrix([
|
||||
[1, 2, 3],
|
||||
[4, 5, 6],
|
||||
[7, 8, 9]
|
||||
]);
|
||||
|
||||
const transposedMatrix = originalMatrix.transpose();
|
||||
|
||||
expect(transposedMatrix.rows).toBe(originalMatrix.cols);
|
||||
expect(transposedMatrix.cols).toBe(originalMatrix.rows);
|
||||
expect(transposedMatrix.data).toEqual([
|
||||
[1, 4, 7],
|
||||
[2, 5, 8],
|
||||
[3, 6, 9]
|
||||
]);
|
||||
});
|
||||
|
||||
test('should transpose an empty matrix correctly', () => {
|
||||
const originalMatrix = new Matrix([]);
|
||||
|
||||
const transposedMatrix = originalMatrix.transpose();
|
||||
|
||||
expect(transposedMatrix.rows).toBe(0);
|
||||
expect(transposedMatrix.cols).toBe(0);
|
||||
expect(transposedMatrix.data).toEqual([]);
|
||||
});
|
||||
|
||||
test('should throw an error when transposing a non-rectangular matrix', () => {
|
||||
const originalMatrix = new Matrix([
|
||||
[1, 2, 3],
|
||||
[4, 5]
|
||||
]);
|
||||
|
||||
// Using a lambda to call transpose because Jest expects the error to be thrown within a function
|
||||
expect(() => originalMatrix.transpose()).toThrowError('Matrix must be rectangular for transposition.');
|
||||
});
|
||||
});
|
||||
|
||||
describe('inverse', () => {
|
||||
it('should calculate the inverse of a 2x2 matrix', () => {
|
||||
const data: number[][] = [
|
||||
[1, 2],
|
||||
[3, 4]
|
||||
];
|
||||
|
||||
const matrix = new Matrix(data);
|
||||
const inverseMatrix = matrix.inverse();
|
||||
|
||||
const expectedInverse: number[][] = [
|
||||
[-2, 1],
|
||||
[1.5, -0.5]
|
||||
];
|
||||
|
||||
expect(inverseMatrix?.data).toEqual(expectedInverse);
|
||||
});
|
||||
|
||||
it('should calculate the inverse of a 3x3 matrix', () => {
|
||||
const data: number[][] = [
|
||||
[4, 7, 2],
|
||||
[2, 6, 3],
|
||||
[1, 2, 5]
|
||||
];
|
||||
|
||||
const matrix = new Matrix(data);
|
||||
const inverseMatrix = matrix.inverse();
|
||||
|
||||
// const expectedInverse: number[][] = [
|
||||
// [24 / 43, -31 / 43, 9 / 43],
|
||||
// [-7 / 43, 18 / 43, -8 / 43],
|
||||
// [-2 / 43, -1 / 43, 10 / 43],
|
||||
// ];
|
||||
|
||||
expect(inverseMatrix?.data).toEqual([
|
||||
[0.558139534883721, -0.7209302325581396, 0.2093023255813954],
|
||||
[-0.16279069767441862, 0.4186046511627907, -0.18604651162790697],
|
||||
[-0.046511627906976744, -0.023255813953488372, 0.23255813953488372]
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('dot', () => {
|
||||
test('should calculate the dot product of two matrices', () => {
|
||||
const matrix1 = new Matrix([
|
||||
[1, 2],
|
||||
[3, 4]
|
||||
]);
|
||||
const matrix2 = new Matrix([
|
||||
[5, 6],
|
||||
[7, 8]
|
||||
]);
|
||||
|
||||
const resultMatrix = matrix1.dot(matrix2);
|
||||
|
||||
expect(resultMatrix?.data).toEqual([
|
||||
[19, 22],
|
||||
[43, 50]
|
||||
]);
|
||||
});
|
||||
|
||||
test('should throw an error for incompatible matrices', () => {
|
||||
const matrix1 = new Matrix([
|
||||
[1, 2],
|
||||
[3, 4]
|
||||
]);
|
||||
const matrix2 = new Matrix([
|
||||
[5, 6, 7],
|
||||
[8, 9, 10],
|
||||
[18, 19, 110]
|
||||
]);
|
||||
expect(() => matrix1.dot(matrix2)).toThrowError(
|
||||
'Number of columns in the first matrix must be equal to the number of rows in the second matrix for dot product.'
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw an error for incompatible matrices', () => {
|
||||
const matrixA = new Matrix([
|
||||
[1, 2],
|
||||
[3, 4]
|
||||
]);
|
||||
const matrixB = new Matrix([
|
||||
[5, 6],
|
||||
[7, 8],
|
||||
[9, 10]
|
||||
]);
|
||||
|
||||
expect(() => matrixA.dot(matrixB)).toThrowError(
|
||||
'Number of columns in the first matrix must be equal to the number of rows in the second matrix for dot product.'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,345 +0,0 @@
|
|||
import { Matrix2D, Vector2D } from '../../../../src';
|
||||
import { isDebugTest } from '../../../config';
|
||||
|
||||
const isDebug = isDebugTest;
|
||||
describe('Matrix2D', () => {
|
||||
it('should initialize with default identity matrix', () => {
|
||||
const matrix = new Matrix2D();
|
||||
const expectedMatrix = Matrix2D.identity;
|
||||
|
||||
expect(matrix.m).toEqual(expectedMatrix);
|
||||
});
|
||||
|
||||
it('should initialize with provided 2D array', () => {
|
||||
const inputMatrix = [
|
||||
[2, 0, 0],
|
||||
[0, 3, 0],
|
||||
[0, 0, 1]
|
||||
];
|
||||
const matrix = new Matrix2D(inputMatrix);
|
||||
|
||||
expect(matrix.m).toEqual(inputMatrix);
|
||||
});
|
||||
|
||||
it('should initialize with provided Vector2D', () => {
|
||||
expect(true).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should add two matrices correctly', () => {
|
||||
const matrix1 = new Matrix2D([
|
||||
[1, 2, 3],
|
||||
[4, 5, 6],
|
||||
[7, 8, 9]
|
||||
]);
|
||||
const matrix2 = new Matrix2D([
|
||||
[9, 8, 7],
|
||||
[6, 5, 4],
|
||||
[3, 2, 1]
|
||||
]);
|
||||
const expectedMatrix = [
|
||||
[10, 10, 10],
|
||||
[10, 10, 10],
|
||||
[10, 10, 10]
|
||||
];
|
||||
|
||||
const result = Matrix2D.add(matrix1, matrix2);
|
||||
|
||||
expect(result.m).toEqual(expectedMatrix);
|
||||
});
|
||||
|
||||
it('should subtract two matrices correctly', () => {
|
||||
const matrix1 = new Matrix2D([
|
||||
[9, 8, 7],
|
||||
[6, 5, 4],
|
||||
[3, 2, 1]
|
||||
]);
|
||||
const matrix2 = new Matrix2D([
|
||||
[1, 2, 3],
|
||||
[4, 5, 6],
|
||||
[7, 8, 9]
|
||||
]);
|
||||
const expectedMatrix = [
|
||||
[8, 6, 4],
|
||||
[2, 0, -2],
|
||||
[-4, -6, -8]
|
||||
];
|
||||
|
||||
const result = Matrix2D.subtract(matrix1, matrix2);
|
||||
|
||||
expect(result.m).toEqual(expectedMatrix);
|
||||
});
|
||||
|
||||
it('should multiply two matrices correctly', () => {
|
||||
const matrix1 = new Matrix2D([
|
||||
[1, 2, 3],
|
||||
[4, 5, 6],
|
||||
[7, 8, 9]
|
||||
]);
|
||||
const matrix2 = new Matrix2D([
|
||||
[9, 8, 7],
|
||||
[6, 5, 4],
|
||||
[3, 2, 1]
|
||||
]);
|
||||
const expectedMatrix = [
|
||||
[30, 24, 18],
|
||||
[84, 69, 54],
|
||||
[138, 114, 90]
|
||||
];
|
||||
|
||||
const result = Matrix2D.multiply(matrix1, matrix2);
|
||||
|
||||
expect(result.m).toEqual(expectedMatrix);
|
||||
});
|
||||
|
||||
it('should multiply a matrix by a Vector2D correctly', () => {
|
||||
expect(true).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should scale a matrix by a value correctly', () => {
|
||||
expect(true).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should rotate a matrix by radians correctly', () => {
|
||||
expect(true).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should translate a matrix by a Vector2D correctly', () => {
|
||||
const translationVector = new Vector2D(2, 3);
|
||||
const expectedMatrix = [
|
||||
[1, 0, 2],
|
||||
[0, 1, 3],
|
||||
[0, 0, 1]
|
||||
];
|
||||
|
||||
const result = Matrix2D.translate(translationVector);
|
||||
|
||||
expect(result.m).toEqual(expectedMatrix);
|
||||
});
|
||||
|
||||
it('should create a view matrix correctly', () => {
|
||||
expect(true).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should multiply a matrix by a value correctly', () => {
|
||||
const matrix = new Matrix2D([
|
||||
[1, 2, 3],
|
||||
[4, 5, 6],
|
||||
[7, 8, 9]
|
||||
]);
|
||||
const value = 2;
|
||||
const expectedMatrix = [
|
||||
[2, 4, 6],
|
||||
[8, 10, 12],
|
||||
[14, 16, 18]
|
||||
];
|
||||
|
||||
const result = Matrix2D.multiplyByValue(matrix, value);
|
||||
|
||||
expect(result.m).toEqual(expectedMatrix);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Vector2D', () => {
|
||||
it('should create a vector with default values', () => {
|
||||
const vector = new Vector2D();
|
||||
expect(vector.x).toBe(0);
|
||||
expect(vector.y).toBe(0);
|
||||
expect(vector.w).toBe(1);
|
||||
});
|
||||
|
||||
it('should correctly calculate vector length', () => {
|
||||
const vector = new Vector2D(3, 4);
|
||||
expect(vector.length).toBe(5);
|
||||
});
|
||||
|
||||
it('should correctly add two vectors', () => {
|
||||
const vector1 = new Vector2D(2, 3);
|
||||
const vector2 = new Vector2D(1, 2);
|
||||
const result = Vector2D.add(vector1, vector2);
|
||||
expect(result.x).toBe(3);
|
||||
expect(result.y).toBe(5);
|
||||
});
|
||||
|
||||
// Add more test cases for Vector2D methods
|
||||
});
|
||||
|
||||
describe('Matrix2D', () => {
|
||||
it('should create an identity matrix by default', () => {
|
||||
const matrix = new Matrix2D();
|
||||
expect(matrix.m).toEqual(Matrix2D.identity);
|
||||
});
|
||||
|
||||
it('should correctly add two matrices', () => {
|
||||
const matrix1 = new Matrix2D([
|
||||
[1, 2, 3],
|
||||
[4, 5, 6],
|
||||
[7, 8, 9]
|
||||
]);
|
||||
const matrix2 = new Matrix2D([
|
||||
[9, 8, 7],
|
||||
[6, 5, 4],
|
||||
[3, 2, 1]
|
||||
]);
|
||||
const result = Matrix2D.add(matrix1, matrix2);
|
||||
expect(result.m).toEqual([
|
||||
[10, 10, 10],
|
||||
[10, 10, 10],
|
||||
[10, 10, 10]
|
||||
]);
|
||||
});
|
||||
|
||||
// Add more test cases for Matrix2D methods
|
||||
});
|
||||
|
||||
describe('Matrix2D', () => {
|
||||
it('should create a matrix with default identity values', () => {
|
||||
const matrix = new Matrix2D();
|
||||
expect(matrix.m).toEqual(Matrix2D.identity);
|
||||
});
|
||||
|
||||
it('should create a matrix from a Vector2D', () => {
|
||||
const vector = new Vector2D(2, 3);
|
||||
const matrix = new Matrix2D(vector);
|
||||
expect(matrix.m).toEqual([
|
||||
[2, 0, 0],
|
||||
[3, 1, 0],
|
||||
[1, 0, 1]
|
||||
]);
|
||||
});
|
||||
|
||||
it('should correctly add two matrices', () => {
|
||||
const matrix1 = new Matrix2D([
|
||||
[1, 2, 3],
|
||||
[4, 5, 6],
|
||||
[7, 8, 9]
|
||||
]);
|
||||
const matrix2 = new Matrix2D([
|
||||
[9, 8, 7],
|
||||
[6, 5, 4],
|
||||
[3, 2, 1]
|
||||
]);
|
||||
const result = Matrix2D.add(matrix1, matrix2);
|
||||
expect(result.m).toEqual([
|
||||
[10, 10, 10],
|
||||
[10, 10, 10],
|
||||
[10, 10, 10]
|
||||
]);
|
||||
});
|
||||
|
||||
it('should correctly subtract two matrices', () => {
|
||||
const matrix1 = new Matrix2D([
|
||||
[5, 4, 3],
|
||||
[2, 1, 0],
|
||||
[0, 1, 2]
|
||||
]);
|
||||
const matrix2 = new Matrix2D([
|
||||
[4, 3, 2],
|
||||
[1, 0, 1],
|
||||
[2, 1, 0]
|
||||
]);
|
||||
const result = Matrix2D.subtract(matrix1, matrix2);
|
||||
expect(result.m).toEqual([
|
||||
[1, 1, 1],
|
||||
[1, 1, -1],
|
||||
[-2, 0, 2]
|
||||
]);
|
||||
});
|
||||
|
||||
it('should correctly multiply two matrices', () => {
|
||||
const matrix1 = new Matrix2D([
|
||||
[1, 2, 3],
|
||||
[4, 5, 6],
|
||||
[7, 8, 9]
|
||||
]);
|
||||
const matrix2 = new Matrix2D([
|
||||
[9, 8, 7],
|
||||
[6, 5, 4],
|
||||
[3, 2, 1]
|
||||
]);
|
||||
const result = Matrix2D.multiply(matrix1, matrix2);
|
||||
expect(result.m).toEqual([
|
||||
[30, 24, 18],
|
||||
[84, 69, 54],
|
||||
[138, 114, 90]
|
||||
]);
|
||||
});
|
||||
|
||||
it('should correctly multiply a matrix by a value', () => {
|
||||
const matrix = new Matrix2D([
|
||||
[2, 3, 4],
|
||||
[5, 6, 7],
|
||||
[8, 9, 10]
|
||||
]);
|
||||
const value = 2;
|
||||
const result = Matrix2D.multiplyByValue(matrix, value);
|
||||
expect(result.m).toEqual([
|
||||
[4, 6, 8],
|
||||
[10, 12, 14],
|
||||
[16, 18, 20]
|
||||
]);
|
||||
});
|
||||
|
||||
it('should correctly multiply a matrix by a vector', () => {
|
||||
const matrix = new Matrix2D([
|
||||
[2, 3, 4],
|
||||
[5, 6, 7],
|
||||
[8, 9, 10]
|
||||
]);
|
||||
const vector = new Vector2D(2, 3);
|
||||
const result = Matrix2D.multiplyByVector(matrix, vector);
|
||||
isDebug && console.log(JSON.stringify(result));
|
||||
expect(result).toEqual({ x: 17, y: 35, w: 1 });
|
||||
});
|
||||
|
||||
it('should correctly create a view matrix', () => {
|
||||
const width = 800;
|
||||
const height = 600;
|
||||
const viewMatrix = Matrix2D.view(width, height);
|
||||
expect(viewMatrix.m).toEqual([
|
||||
[1, 0, 400],
|
||||
[0, -1, 300],
|
||||
[0, 0, 1]
|
||||
]);
|
||||
});
|
||||
|
||||
it('should correctly scale a matrix', () => {
|
||||
const factor = 3;
|
||||
const scaledMatrix = Matrix2D.scale(factor);
|
||||
expect(scaledMatrix.m).toEqual([
|
||||
[3, 0, 0],
|
||||
[0, 3, 0],
|
||||
[0, 0, 3]
|
||||
]);
|
||||
});
|
||||
|
||||
it('should correctly rotate a matrix', () => {
|
||||
const radians = Math.PI / 4; // 45 degrees
|
||||
const rotationMatrix = Matrix2D.rotate(radians);
|
||||
isDebug && console.log(JSON.stringify(rotationMatrix.m));
|
||||
expect(rotationMatrix.m).toEqual([
|
||||
[0.7071067811865476, -0.7071067811865475, 0],
|
||||
[0.7071067811865475, 0.7071067811865476, 0],
|
||||
[0, 0, 1]
|
||||
]);
|
||||
});
|
||||
|
||||
it('should correctly translate a matrix', () => {
|
||||
const translationVector = new Vector2D(2, 3);
|
||||
const translationMatrix = Matrix2D.translate(translationVector);
|
||||
expect(translationMatrix.m).toEqual([
|
||||
[1, 0, 2],
|
||||
[0, 1, 3],
|
||||
[0, 0, 1]
|
||||
]);
|
||||
});
|
||||
|
||||
it('should correctly convert a matrix to a vector', () => {
|
||||
const matrix = new Matrix2D([
|
||||
[2, 3, 4],
|
||||
[5, 6, 7],
|
||||
[8, 9, 10]
|
||||
]);
|
||||
const vector = matrix.toVector();
|
||||
expect(vector).toEqual(new Vector2D(2, 5));
|
||||
});
|
||||
});
|
|
@ -1,171 +0,0 @@
|
|||
import { Vector2D } from '../../../../src';
|
||||
|
||||
describe('Vector2D', () => {
|
||||
it('should create a vector with default values', () => {
|
||||
const vector = new Vector2D();
|
||||
expect(vector.x).toBe(0);
|
||||
expect(vector.y).toBe(0);
|
||||
expect(vector.w).toBe(1);
|
||||
});
|
||||
|
||||
it('should correctly check if a vector is zero', () => {
|
||||
const nonZeroVector = new Vector2D(3, 4);
|
||||
const zeroVector = new Vector2D(0, 0);
|
||||
expect(nonZeroVector.isZero).toBe(false);
|
||||
expect(zeroVector.isZero).toBe(true);
|
||||
});
|
||||
|
||||
it('should correctly calculate vector length', () => {
|
||||
const vector = new Vector2D(3, 4);
|
||||
expect(vector.length).toBe(5);
|
||||
});
|
||||
|
||||
it('should correctly calculate squared vector length', () => {
|
||||
const vector = new Vector2D(3, 4);
|
||||
expect(vector.lengthSq).toBe(25);
|
||||
});
|
||||
|
||||
it('should correctly round vector components', () => {
|
||||
const vector = new Vector2D(3.6, 4.3);
|
||||
const roundedVector = vector.rounded;
|
||||
expect(roundedVector.x).toBe(4);
|
||||
expect(roundedVector.y).toBe(4);
|
||||
});
|
||||
|
||||
it('should correctly add two vectors', () => {
|
||||
const vector1 = new Vector2D(2, 3);
|
||||
const vector2 = new Vector2D(1, 2);
|
||||
const result = Vector2D.add(vector1, vector2);
|
||||
expect(result.x).toBe(3);
|
||||
expect(result.y).toBe(5);
|
||||
});
|
||||
|
||||
it('should correctly subtract two vectors', () => {
|
||||
const vector1 = new Vector2D(4, 5);
|
||||
const vector2 = new Vector2D(1, 2);
|
||||
const result = Vector2D.subtract(vector1, vector2);
|
||||
expect(result.x).toBe(3);
|
||||
expect(result.y).toBe(3);
|
||||
});
|
||||
|
||||
it('should correctly subtract value from a vector', () => {
|
||||
const vector = new Vector2D(5, 7);
|
||||
const result = Vector2D.subtractValue(vector, 3);
|
||||
expect(result.x).toBe(2);
|
||||
expect(result.y).toBe(4);
|
||||
});
|
||||
|
||||
it('should correctly multiply a vector by a value', () => {
|
||||
const vector = new Vector2D(2, 3);
|
||||
const result = Vector2D.multiply(vector, 4);
|
||||
expect(result.x).toBe(8);
|
||||
expect(result.y).toBe(12);
|
||||
});
|
||||
|
||||
it('should correctly divide a vector by a value', () => {
|
||||
const vector = new Vector2D(6, 8);
|
||||
const result = Vector2D.divide(vector, 2);
|
||||
expect(result.x).toBe(3);
|
||||
expect(result.y).toBe(4);
|
||||
});
|
||||
|
||||
it('should correctly check if two vectors are equal', () => {
|
||||
const vector1 = new Vector2D(3, 4);
|
||||
const vector2 = new Vector2D(3, 4);
|
||||
const vector3 = new Vector2D(4, 5);
|
||||
expect(Vector2D.equals(vector1, vector2)).toBe(true);
|
||||
expect(Vector2D.equals(vector1, vector3)).toBe(false);
|
||||
});
|
||||
|
||||
it('should correctly check if two vectors are equal within a rounding factor', () => {
|
||||
const vector1 = new Vector2D(3, 4);
|
||||
const vector2 = new Vector2D(2.9, 3.9);
|
||||
const vector3 = new Vector2D(4, 5);
|
||||
expect(Vector2D.equalsRounded(vector1, vector2, 0.2)).toBe(true);
|
||||
expect(Vector2D.equalsRounded(vector1, vector3, 0.2)).toBe(false);
|
||||
});
|
||||
|
||||
it('should correctly normalize a vector', () => {
|
||||
const vector = new Vector2D(3, 4);
|
||||
const normalized = Vector2D.normalize(vector);
|
||||
expect(normalized.x).toBeCloseTo(0.6);
|
||||
expect(normalized.y).toBeCloseTo(0.8);
|
||||
});
|
||||
|
||||
it('should correctly truncate a vector', () => {
|
||||
const vector = new Vector2D(3, 4);
|
||||
const truncated = Vector2D.truncate(vector, 4);
|
||||
expect(truncated.length).toBeLessThanOrEqual(4);
|
||||
});
|
||||
|
||||
it('should correctly get the perpendicular vector', () => {
|
||||
const vector = new Vector2D(3, 4);
|
||||
const perpendicular = Vector2D.perp(vector);
|
||||
expect(Vector2D.dot(vector, perpendicular)).toBe(0);
|
||||
});
|
||||
|
||||
it('should correctly reverse the vector', () => {
|
||||
const vector = new Vector2D(3, 4);
|
||||
const reversed = Vector2D.reverse(vector);
|
||||
expect(reversed.x).toBe(-3);
|
||||
expect(reversed.y).toBe(-4);
|
||||
});
|
||||
|
||||
it('should correctly get the absolute vector', () => {
|
||||
const vector = new Vector2D(-3, 4);
|
||||
const absVector = Vector2D.abs(vector);
|
||||
expect(absVector.x).toBe(3);
|
||||
expect(absVector.y).toBe(4);
|
||||
});
|
||||
|
||||
it('should correctly calculate the dot product of two vectors', () => {
|
||||
const vector1 = new Vector2D(2, 3);
|
||||
const vector2 = new Vector2D(4, 5);
|
||||
const dotProduct = Vector2D.dot(vector1, vector2);
|
||||
expect(dotProduct).toBe(23);
|
||||
});
|
||||
|
||||
it('should correctly calculate the distance between two vectors', () => {
|
||||
const vector1 = new Vector2D(1, 1);
|
||||
const vector2 = new Vector2D(4, 5);
|
||||
const distance = Vector2D.distance(vector1, vector2);
|
||||
expect(distance).toBeCloseTo(5);
|
||||
});
|
||||
|
||||
it('should correctly calculate the squared distance between two vectors', () => {
|
||||
const vector1 = new Vector2D(1, 1);
|
||||
const vector2 = new Vector2D(4, 5);
|
||||
const distanceSq = Vector2D.distanceSq(vector1, vector2);
|
||||
expect(distanceSq).toBe(25);
|
||||
});
|
||||
|
||||
it('should correctly determine the sign of the cross product of two vectors', () => {
|
||||
const vector1 = new Vector2D(2, 3);
|
||||
const vector2 = new Vector2D(4, 5);
|
||||
const sign = Vector2D.sign(vector1, vector2);
|
||||
expect(sign).toBe(-1); // Assuming specific vector values, the result may vary
|
||||
});
|
||||
|
||||
it('should correctly calculate the angle between a vector and the negative y-axis', () => {
|
||||
const vector = new Vector2D(3, 4);
|
||||
const angle = Vector2D.angle(vector);
|
||||
expect(angle).toBeCloseTo(2.498091544796509, 3);
|
||||
});
|
||||
|
||||
it('should create a random vector within the specified range', () => {
|
||||
const maxX = 10;
|
||||
const maxY = 10;
|
||||
const randomVector = Vector2D.random(maxX, maxY);
|
||||
expect(randomVector.x).toBeGreaterThanOrEqual(-maxX / 2);
|
||||
expect(randomVector.x).toBeLessThanOrEqual(maxX / 2);
|
||||
expect(randomVector.y).toBeGreaterThanOrEqual(-maxY / 2);
|
||||
expect(randomVector.y).toBeLessThanOrEqual(maxY / 2);
|
||||
});
|
||||
|
||||
it('should zero the vector components', () => {
|
||||
const vector = new Vector2D(3, 4);
|
||||
vector.zero();
|
||||
expect(vector.x).toBe(0);
|
||||
expect(vector.y).toBe(0);
|
||||
});
|
||||
});
|
30
typedoc.json
Normal file
30
typedoc.json
Normal file
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"visibilityFilters": {
|
||||
"protected": false,
|
||||
"private": false,
|
||||
"inherited": true,
|
||||
"external": false,
|
||||
"@alpha": false,
|
||||
"@beta": false
|
||||
},
|
||||
"excludeNotDocumented": true,
|
||||
"excludeNotDocumentedKinds": [
|
||||
"Module",
|
||||
"Namespace",
|
||||
"Enum",
|
||||
"Variable",
|
||||
"Function",
|
||||
"Interface",
|
||||
"Constructor",
|
||||
"Property",
|
||||
"Method",
|
||||
"CallSignature",
|
||||
"IndexSignature",
|
||||
"ConstructorSignature",
|
||||
"Accessor",
|
||||
"GetSignature",
|
||||
"SetSignature",
|
||||
"TypeAlias",
|
||||
"Reference"
|
||||
]
|
||||
}
|
Loading…
Reference in a new issue