Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- export { Matrix };
- import {
- Vector
- } from './vector';
- class Matrix {
- public static makeIdentityMatrix(
- {
- numberOfRows
- , numberOfColumns
- }: {
- numberOfRows: number
- , numberOfColumns: number
- }
- ): Matrix {
- Matrix.checkSize(
- {
- numberOfRows: numberOfRows
- , numberOfColumns: numberOfColumns
- }
- );
- let elements = [];
- for (
- let ir = 0;
- ir < numberOfRows;
- ir++
- ) {
- elements[ ir ] = [];
- for (
- let ic = 0;
- ic < numberOfColumns;
- ic++
- ) {
- if (
- ir === ic
- ) {
- elements[ ir ][ ic ] = 1;
- }
- else {
- elements[ ir ][ ic ] = 0;
- }
- } // column loop
- } // row loop
- let matrix = new Matrix(
- elements
- );
- return matrix;
- }
- public static makeEmptyMatrix(
- {
- numberOfRows
- , numberOfColumns
- }: {
- numberOfRows: number
- , numberOfColumns: number
- }
- ): Matrix {
- Matrix.checkSize(
- {
- numberOfRows: numberOfRows
- , numberOfColumns: numberOfColumns
- }
- );
- let elements = [];
- for (
- let ir = 0;
- ir < numberOfRows;
- ir++
- ) {
- elements[ ir ] = [];
- for (
- let ic = 0;
- ic < numberOfColumns;
- ic++
- ) {
- elements[ ir ][ ic ] = 0;
- } // column loop
- } // row loop
- let matrix = new Matrix(
- elements
- );
- return matrix;
- }
- public static addMatrices(
- {
- matrixA
- , matrixB
- }: {
- matrixA: Matrix
- , matrixB: Matrix
- }
- ): Matrix {
- Matrix.checkDimensionsSame(
- {
- matrixA: matrixA
- , matrixB: matrixB
- }
- );
- let rows = matrixA.getNumberOfRows();
- let columns = matrixA.getNumberOfColumns();
- let aElements = matrixA.getElements();
- let bElements = matrixB.getElements();
- let cElements = [];
- for (
- let ir = 0;
- ir < rows;
- ir++
- ) {
- cElements[ ir ] = [];
- for (
- let ic = 0;
- ic < columns;
- ic++
- ) {
- cElements[ ir ][ ic ] = (
- aElements[ ir ][ ic ]
- + bElements[ ir ][ ic ]
- );
- } // column loop
- } // row loop
- let matrixC = new Matrix(
- cElements
- );
- return matrixC;
- }
- public static subtractMatrices(
- {
- matrixA
- , matrixB
- }: {
- matrixA: Matrix
- , matrixB: Matrix
- }
- ): Matrix {
- Matrix.checkDimensionsSame(
- {
- matrixA: matrixA
- , matrixB: matrixB
- }
- );
- let rows = matrixA.getNumberOfRows();
- let columns = matrixA.getNumberOfColumns();
- let aElements = matrixA.getElements();
- let bElements = matrixB.getElements();
- let cElements = [];
- for (
- let ir = 0;
- ir < rows;
- ir++
- ) {
- cElements[ ir ] = [];
- for (
- let ic = 0;
- ic < columns;
- ic++
- ) {
- cElements[ ir ][ ic ] = (
- aElements[ ir ][ ic ]
- - bElements[ ir ][ ic ]
- );
- } // column loop
- } // row loop
- let matrixC = new Matrix(
- cElements
- );
- return matrixC;
- }
- public static multiplyMatrices(
- {
- matrixA
- , matrixB
- }: {
- matrixA: Matrix
- , matrixB: Matrix
- }
- ): Matrix {
- Matrix.checkDimensionsMatchUp(
- {
- matrixA: matrixA
- , matrixB: matrixB
- }
- );
- let aRows = matrixA.getNumberOfRows();
- let aColumns = matrixA.getNumberOfColumns();
- let bColumns = matrixB.getNumberOfColumns();
- let aElements = matrixA.getElements();
- let bElements = matrixB.getElements();
- let cElements = [];
- for (
- let icr = 0;
- icr < aRows;
- icr++
- ) {
- cElements[ icr ] = [];
- for (
- let icc = 0;
- icc < bColumns;
- icc++
- ) {
- let cElementSum = 0;
- for (
- let iac = 0;
- iac < aColumns;
- iac++
- ) {
- cElementSum = (
- cElementSum
- + (
- aElements[ icr ][ iac ]
- * bElements[ iac ][ icc ]
- )
- );
- } // a column loop
- cElements[ icr ][ icc ] = cElementSum;
- } // c column loop
- } // c row loop
- let matrixC = new Matrix(
- cElements
- );
- return matrixC;
- }
- public static lerpBetweenMatrices(
- {
- matrixA
- , matrixB
- , progress // ranges from 0.0 to 1.0
- }: {
- matrixA: Matrix
- , matrixB: Matrix
- , progress: number
- }
- ): Matrix {
- Matrix.checkLerpProgress(
- progress
- );
- Matrix.checkDimensionsSame(
- {
- matrixA: matrixA
- , matrixB: matrixB
- }
- );
- let delta = matrixB.subtractMatrix(
- matrixA
- );
- delta = delta.multiplyMatrixByScalar(
- progress
- );
- let result = matrixA.addMatrix(
- delta
- );
- return result;
- }
- private static checkSize(
- {
- numberOfRows
- , numberOfColumns
- }: {
- numberOfRows: number
- , numberOfColumns: number
- }
- ): void {
- if (
- numberOfColumns < 2
- || numberOfRows < 2
- ) {
- throw Error(
- 'matrix dimensions below minimum of 2'
- );
- }
- }
- private static checkDimensionsMatchUp(
- {
- matrixA
- , matrixB
- }: {
- matrixA: Matrix
- , matrixB: Matrix
- }
- ): void {
- let aColumns = matrixA.getNumberOfColumns();
- let bRows = matrixB.getNumberOfRows();
- if (
- aColumns !== bRows
- ) {
- throw Error(
- 'number of columns and rows do not match up right'
- );
- }
- }
- private static checkDimensionsSame(
- {
- matrixA
- , matrixB
- }: {
- matrixA: Matrix
- , matrixB: Matrix
- }
- ): void {
- let aColumns = matrixA.getNumberOfColumns();
- let aRows = matrixA.getNumberOfRows();
- let bColumns = matrixB.getNumberOfColumns();
- let bRows = matrixB.getNumberOfRows();
- if (
- aColumns !== bColumns
- || aRows !== bRows
- ) {
- throw Error(
- 'matrices have different dimensions'
- );
- }
- }
- private static checkLerpProgress(
- progress
- ): void {
- if (
- progress < 0
- || progress > 1
- ) {
- throw Error(
- 'progress out of valid range'
- );
- }
- }
- private readonly elements: number[][];
- private readonly numberOfColumns: number;
- private readonly numberOfRows: number;
- private readonly square: boolean;
- private determinant: number;
- // to decide: cache results of transpose, inverse, cofactors, minor matrices?
- public constructor(
- elements: number[][]
- ) {
- this.checkElements(
- elements
- );
- this.elements = elements;
- // calculate number of rows and columns
- this.numberOfRows = elements.length;
- this.numberOfColumns = elements[ 0 ].length;
- // calculate is square
- this.square = (
- this.numberOfColumns
- === this.numberOfRows
- );
- }
- public getNumberOfColumns(): number {
- return this.numberOfColumns;
- }
- public getNumberOfRows(): number {
- return this.numberOfRows;
- }
- public isSquare(): boolean {
- return this.square;
- }
- public getElements(): number[][] {
- return this.elements;
- }
- public getElement(
- {
- rowIndex
- , columnIndex
- }: {
- rowIndex: number
- , columnIndex: number
- }
- ): number {
- this.checkElementIndex(
- {
- columnIndex: columnIndex
- , rowIndex: rowIndex
- }
- );
- return this.elements[ rowIndex ][ columnIndex ];
- }
- public getColumnAsVector(
- columnIndex: number
- ) {
- this.checkElementIndex(
- {
- rowIndex: 0
- , columnIndex: columnIndex
- }
- );
- let components = [];
- for (
- let ir = 0;
- ir < this.numberOfRows;
- ir++
- ) {
- components[ ir ] = this.elements[ ir ][ columnIndex ];
- }
- let vector = new Vector(
- components
- );
- return vector;
- }
- public getRowAsVector(
- rowIndex: number
- ) {
- this.checkElementIndex(
- {
- rowIndex: rowIndex
- , columnIndex: 0
- }
- );
- let components = [];
- for (
- let ic = 0;
- ic < this.numberOfColumns;
- ic++
- ) {
- components[ ic ] = this.elements[ rowIndex ][ ic ];
- }
- let vector = new Vector(
- components
- );
- return vector;
- }
- public getTranspose(): Matrix {
- let elements = [];
- for (
- let ir = 0;
- ir < this.numberOfColumns;
- ir++
- ) {
- elements[ ir ] = [];
- for (
- let ic = 0;
- ic < this.numberOfRows;
- ic++
- ) {
- elements[ ir ][ ic ] = this.elements[ ic ][ ir ];
- } // column loop
- } // row loop
- let transpose = new Matrix(
- elements
- );
- return transpose;
- }
- public getInverse(): Matrix {
- if (
- !this.isSquare()
- ) {
- throw Error(
- 'matrix is not square'
- );
- }
- let determinant = this.getDeterminant();
- if ( determinant === 0 ) {
- console.log(
- 'determinant is zero, no inverse'
- );
- return null;
- }
- let elements = [];
- for (
- let ir = 0;
- ir < this.numberOfRows;
- ir++
- ) {
- elements[ ir ] = [];
- for (
- let ic = 0;
- ic < this.numberOfColumns;
- ic++
- ) {
- elements[ ir ][ ic ] = this.getCofactorFor(
- {
- rowIndex: ir,
- columnIndex: ic
- }
- );
- } // column loop
- } // row loop
- let cofactorMatrix = new Matrix(
- elements
- );
- let inverse = cofactorMatrix.getTranspose();
- inverse = inverse.divideMatrixByScalar(
- determinant
- );
- return inverse;
- }
- public getDeterminant(): number {
- if (
- this.determinant === undefined
- ) {
- this.calculateDeterminant();
- }
- return this.determinant;
- }
- public getCofactorFor(
- {
- rowIndex
- , columnIndex
- }: {
- rowIndex: number
- , columnIndex: number
- }
- ): number {
- this.checkElementIndex(
- {
- rowIndex: rowIndex
- , columnIndex: columnIndex
- }
- );
- if (
- this.numberOfColumns === 2
- || this.numberOfRows === 2
- ) {
- throw Error(
- 'get cofactor unsupported for matrix 2'
- );
- }
- let minor = this.getMinorMatrixFor(
- {
- rowIndex: rowIndex,
- columnIndex: columnIndex
- }
- );
- let determinant = minor.getDeterminant();
- // todo: check whether sign uses indices starting at 0 or 1
- let sign = Math.pow(
- -1,
- ( rowIndex + columnIndex )
- );
- let cofactor = sign * determinant;
- return cofactor;
- }
- public getMinorMatrixFor(
- {
- rowIndex
- , columnIndex
- }: {
- rowIndex: number
- , columnIndex: number
- }
- ): Matrix {
- this.checkElementIndex(
- {
- rowIndex: rowIndex
- , columnIndex: columnIndex
- }
- );
- if (
- this.numberOfColumns === 2
- || this.numberOfRows === 2
- ) {
- throw Error(
- 'get minor matrix unsupported for matrix 2'
- );
- }
- let elements = [];
- let mRows = this.numberOfRows - 1;
- let mColumns = this.numberOfColumns - 1;
- for (
- let ir = 0;
- ir < mRows;
- ir++
- ) {
- elements[ ir ] = [];
- for (
- let ic = 0;
- ic < mColumns;
- ic++
- ) {
- let r;
- let c;
- if ( // todo: check whether > or >=
- ir >= rowIndex
- ) {
- r = ir + 1;
- }
- else {
- r = ir;
- }
- if ( // todo: check whether > or >=
- ic >= columnIndex
- ) {
- c = ic + 1;
- }
- else {
- c = ic;
- }
- elements[ ir ][ ic ] = this.elements[ r ][ c ];
- } // column loop
- } // row loop
- let minor = new Matrix(
- elements
- );
- return minor;
- }
- public setElement(
- {
- rowIndex
- , columnIndex
- , value
- }: {
- rowIndex: number
- , columnIndex: number
- , value: number
- }
- ): Matrix {
- this.checkElementIndex(
- {
- columnIndex: columnIndex
- , rowIndex: rowIndex
- }
- );
- let elements = [];
- for (
- let ir = 0;
- ir < this.numberOfRows;
- ir++
- ) {
- elements[ ir ] = [];
- for (
- let ic = 0;
- ic < this.numberOfColumns;
- ic++
- ) {
- if (
- ic === columnIndex
- && ir === rowIndex
- ) {
- elements[ ir ][ ic ] = value;
- }
- else {
- elements[ ir ][ ic ] = this.elements[ ir ][ ic ];
- }
- } // column loop
- } // row loop
- let matrix = new Matrix(
- elements
- );
- return matrix;
- }
- public setColumnFromVector(
- {
- columnIndex
- , vector
- }: {
- columnIndex: number
- , vector: Vector
- }
- ): Matrix {
- this.checkElementIndex(
- {
- rowIndex: 0
- , columnIndex: columnIndex
- }
- );
- this.checkComponentsMatchColumns(
- vector
- );
- let elements = [];
- for (
- let ir = 0;
- ir < this.numberOfRows;
- ir++
- ) {
- elements[ ir ] = [];
- for (
- let ic = 0;
- ic < this.numberOfColumns;
- ic++
- ) {
- if (
- ic === columnIndex
- ) {
- elements[ ir ][ ic ] = vector.getComponent( ir );
- }
- else {
- elements[ ir ][ ic ] = this.elements[ ir ][ ic ];
- }
- } // column loop
- } // row loop
- let matrix = new Matrix(
- elements
- );
- return matrix;
- }
- public setRowFromVector(
- {
- rowIndex
- , vector
- }: {
- rowIndex: number
- , vector: Vector
- }
- ): Matrix {
- this.checkElementIndex(
- {
- rowIndex: rowIndex
- , columnIndex: 0
- }
- );
- this.checkComponentsMatchRows(
- vector
- );
- let elements = [];
- for (
- let ir = 0;
- ir < this.numberOfRows;
- ir++
- ) {
- elements[ ir ] = [];
- for (
- let ic = 0;
- ic < this.numberOfColumns;
- ic++
- ) {
- if (
- ir === rowIndex
- ) {
- elements[ ir ][ ic ] = vector.getComponent( ic );
- }
- else {
- elements[ ir ][ ic ] = this.elements[ ir ][ ic ];
- }
- } // column loop
- } // row loop
- let matrix = new Matrix(
- elements
- );
- return matrix;
- }
- // scalar
- public addScalarToMatrix(
- scalar: number
- ): Matrix {
- let elements = [];
- for (
- let ir = 0;
- ir < this.numberOfRows;
- ir++
- ) {
- for (
- let ic = 0;
- ic < this.numberOfColumns;
- ic++
- ) {
- elements[ ir ][ ic ] = (
- this.elements[ ir ][ ic ]
- + scalar
- );
- } // column loop
- } // row loop
- let result = new Matrix(
- elements
- );
- return result;
- }
- public subtractScalarFromMatrix(
- scalar: number
- ): Matrix {
- let elements = [];
- for (
- let ir = 0;
- ir < this.numberOfRows;
- ir++
- ) {
- for (
- let ic = 0;
- ic < this.numberOfColumns;
- ic++
- ) {
- elements[ ir ][ ic ] = (
- this.elements[ ir ][ ic ]
- - scalar
- );
- } // column loop
- } // row loop
- let result = new Matrix(
- elements
- );
- return result;
- }
- public multiplyMatrixByScalar(
- scalar: number
- ): Matrix {
- let elements = [];
- for (
- let ir = 0;
- ir < this.numberOfRows;
- ir++
- ) {
- for (
- let ic = 0;
- ic < this.numberOfColumns;
- ic++
- ) {
- elements[ ir ][ ic ] = (
- this.elements[ ir ][ ic ]
- * scalar
- );
- } // column loop
- } // row loop
- let result = new Matrix(
- elements
- );
- return result;
- }
- public divideMatrixByScalar(
- scalar: number
- ): Matrix {
- let inverseScalar = 1 / scalar;
- let result = this.multiplyMatrixByScalar(
- inverseScalar
- );
- return result;
- }
- // vector
- public addVectorToColumn(
- {
- columnIndex
- , vector
- }: {
- columnIndex: number
- , vector: Vector
- }
- ): Matrix {
- this.checkElementIndex(
- {
- rowIndex: 0
- , columnIndex: columnIndex
- }
- );
- this.checkComponentsMatchColumns(
- vector
- );
- let elements = [];
- for (
- let ir = 0;
- ir < this.numberOfRows;
- ir++
- ) {
- elements[ ir ] = [];
- for (
- let ic = 0;
- ic < this.numberOfColumns;
- ic++
- ) {
- if (
- ic === columnIndex
- ) {
- elements[ ir ][ ic ] = (
- this.elements[ ir ][ ic ]
- + vector.getComponent( ir )
- );
- }
- else {
- elements[ ir ][ ic ] = this.elements[ ir ][ ic ];
- }
- } // column loop
- } // row loop
- let matrix = new Matrix(
- elements
- );
- return matrix;
- }
- public addVectorToRow(
- {
- rowIndex
- , vector
- }: {
- rowIndex: number
- , vector: Vector
- }
- ): Matrix {
- this.checkElementIndex(
- {
- rowIndex: rowIndex
- , columnIndex: 0
- }
- );
- this.checkComponentsMatchRows(
- vector
- );
- let elements = [];
- for (
- let ir = 0;
- ir < this.numberOfRows;
- ir++
- ) {
- elements[ ir ] = [];
- for (
- let ic = 0;
- ic < this.numberOfColumns;
- ic++
- ) {
- if (
- ir === rowIndex
- ) {
- elements[ ir ][ ic ] = (
- this.elements[ ir ][ ic ]
- + vector.getComponent( ic )
- );
- }
- else {
- elements[ ir ][ ic ] = this.elements[ ir ][ ic ];
- }
- } // column loop
- } // row loop
- let matrix = new Matrix(
- elements
- );
- return matrix;
- }
- public subtractVectorFromColumn(
- {
- columnIndex
- , vector
- }: {
- columnIndex: number
- , vector: Vector
- }
- ): Matrix {
- this.checkElementIndex(
- {
- rowIndex: 0
- , columnIndex: columnIndex
- }
- );
- this.checkComponentsMatchColumns(
- vector
- );
- let elements = [];
- for (
- let ir = 0;
- ir < this.numberOfRows;
- ir++
- ) {
- elements[ ir ] = [];
- for (
- let ic = 0;
- ic < this.numberOfColumns;
- ic++
- ) {
- if (
- ic === columnIndex
- ) {
- elements[ ir ][ ic ] = (
- this.elements[ ir ][ ic ]
- - vector.getComponent( ir )
- );
- }
- else {
- elements[ ir ][ ic ] = this.elements[ ir ][ ic ];
- }
- } // column loop
- } // row loop
- let matrix = new Matrix(
- elements
- );
- return matrix;
- }
- public subtractVectorFromRow(
- {
- rowIndex
- , vector
- }: {
- rowIndex: number
- , vector: Vector
- }
- ): Matrix {
- this.checkElementIndex(
- {
- rowIndex: rowIndex
- , columnIndex: 0
- }
- );
- this.checkComponentsMatchRows(
- vector
- );
- let elements = [];
- for (
- let ir = 0;
- ir < this.numberOfRows;
- ir++
- ) {
- elements[ ir ] = [];
- for (
- let ic = 0;
- ic < this.numberOfColumns;
- ic++
- ) {
- if (
- ir === rowIndex
- ) {
- elements[ ir ][ ic ] = (
- this.elements[ ir ][ ic ]
- - vector.getComponent( ic )
- );
- }
- else {
- elements[ ir ][ ic ] = this.elements[ ir ][ ic ];
- }
- } // column loop
- } // row loop
- let matrix = new Matrix(
- elements
- );
- return matrix;
- }
- public multiplyVectorByMatrix(
- vector: Vector
- ): Vector {
- this.checkComponentsMatchColumns(
- vector
- );
- let components = vector.getComponents();
- let resultComponents = [];
- for (
- let ic = 0;
- ic < this.numberOfColumns;
- ic++
- ) {
- let componentSum = 0;
- for (
- let ir = 0;
- ir < this.numberOfRows;
- ir++
- ) {
- componentSum = (
- componentSum
- + (
- this.elements[ ir ][ ic ]
- * components[ ic ]
- )
- );
- } // row loop
- resultComponents[ ic ] = componentSum;
- } // column loop
- let result = new Vector(
- resultComponents
- );
- return result;
- }
- // matrix
- public addMatrix(
- matrix: Matrix
- ): Matrix {
- let result = Matrix.addMatrices(
- {
- matrixA: this
- , matrixB: matrix
- }
- );
- return result;
- }
- public subtractMatrix(
- matrix: Matrix
- ): Matrix {
- let result = Matrix.subtractMatrices(
- {
- matrixA: this
- , matrixB: matrix
- }
- );
- return result;
- }
- public preMultiplyMatrix(
- matrix: Matrix
- ): Matrix {
- let result = Matrix.subtractMatrices(
- {
- matrixA: this
- , matrixB: matrix
- }
- );
- return result;}
- public postMultiplyMatrix(
- matrix: Matrix
- ): Matrix {
- let result = Matrix.multiplyMatrices(
- {
- matrixA: matrix,
- matrixB: this
- }
- );
- return result;
- }
- private calculateDeterminant(): void {
- if (
- !this.isSquare()
- ) {
- throw Error(
- 'matrix is not square'
- );
- }
- let determinant;
- if (
- this.numberOfRows === 2
- ) {
- determinant = (
- (
- this.elements[ 0 ][ 0 ]
- * this.elements[ 1 ][ 1 ]
- )
- - (
- this.elements[ 0 ][ 1 ]
- * this.elements[ 1 ][ 0 ]
- )
- );
- }
- else {
- determinant = 0;
- for (
- let ic = 0;
- ic < this.numberOfColumns;
- ic++
- ) {
- determinant = (
- determinant
- + this.getCofactorFor(
- {
- rowIndex: 0,
- columnIndex: ic
- }
- )
- );
- }
- }
- return determinant;
- }
- private checkElements(
- elements: number[][]
- ): void {
- let numberOfRows = elements.length;
- let numberOfColumns = elements[ 0 ].length;
- Matrix.checkSize(
- {
- numberOfRows: numberOfRows
- , numberOfColumns: numberOfColumns
- }
- );
- for (
- let ir = 0;
- ir < numberOfRows;
- ir++
- ) {
- if (
- elements[ ir ].length !== numberOfColumns
- ) {
- throw Error(
- 'column length varies across rows'
- );
- }
- } // row loop
- }
- private checkElementIndex(
- {
- rowIndex
- , columnIndex
- }: {
- rowIndex: number
- , columnIndex: number
- }
- ): void {
- if (
- columnIndex >= this.numberOfColumns
- || columnIndex < 0
- || rowIndex >= this.numberOfRows
- || rowIndex < 0
- ) {
- throw Error(
- 'invalid element index'
- );
- }
- }
- private checkComponentsMatchColumns(
- vector: Vector
- ): void {
- let numberOfColumns = this.numberOfColumns;
- let numberOfComponents = vector.getNumberOfComponents();
- if (
- numberOfColumns !== numberOfComponents
- ) {
- throw Error(
- 'matrix columns do not match number vector components'
- );
- }
- }
- private checkComponentsMatchRows(
- vector: Vector
- ): void {
- let numberOfRows = this.numberOfRows;
- let numberOfComponents = vector.getNumberOfComponents();
- if (
- numberOfRows !== numberOfComponents
- ) {
- throw Error(
- 'matrix rows do not match number vector components'
- );
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement