2d-physics-engine
Version:
A lightweight, flexible 2D physics engine with ECS architecture, built with TypeScript
145 lines • 5.19 kB
JavaScript
export class Matrix {
constructor(rowsCount, colsCount, fill = 0) {
Object.defineProperty(this, "rowsCount", {
enumerable: true,
configurable: true,
writable: true,
value: rowsCount
});
Object.defineProperty(this, "colsCount", {
enumerable: true,
configurable: true,
writable: true,
value: colsCount
});
Object.defineProperty(this, "_data", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
if (rowsCount <= 0 || colsCount <= 0) {
throw new Error('Matrix dimensions must be positive.');
}
this._data = Array.from({ length: rowsCount }, () => Array.from({ length: colsCount }, () => fill));
}
/** ---------------- STATIC HELPERS ---------------- **/
static isValid(matrix) {
if (!Array.isArray(matrix) || matrix.length === 0)
return false;
const firstRow = matrix[0];
if (!Array.isArray(firstRow) || firstRow.length === 0)
return false;
const cols = firstRow.length;
return matrix.every((row) => Array.isArray(row) && row.length === cols);
}
static identity(size) {
const m = new Matrix(size, size);
for (let i = 0; i < size; i++) {
m._data[i][i] = 1;
}
return m;
}
static rotation2D(radians) {
const m = new Matrix(2, 2);
m._data[0][0] = Math.cos(radians);
m._data[0][1] = -Math.sin(radians);
m._data[1][0] = Math.sin(radians);
m._data[1][1] = Math.cos(radians);
return m;
}
/** ---------------- BASIC OPERATIONS ---------------- **/
add(matrix) {
this._assertSameDimensions(matrix);
const result = new Matrix(this.rowsCount, this.colsCount);
for (let i = 0; i < this.rowsCount; i++) {
for (let j = 0; j < this.colsCount; j++) {
result._data[i][j] = this._data[i][j] + matrix._data[i][j];
}
}
return result;
}
subtract(matrix) {
this._assertSameDimensions(matrix);
const result = new Matrix(this.rowsCount, this.colsCount);
for (let i = 0; i < this.rowsCount; i++) {
for (let j = 0; j < this.colsCount; j++) {
result._data[i][j] = this._data[i][j] - matrix._data[i][j];
}
}
return result;
}
multiplyBy(scalar) {
const result = new Matrix(this.rowsCount, this.colsCount);
for (let i = 0; i < this.rowsCount; i++) {
for (let j = 0; j < this.colsCount; j++) {
result._data[i][j] = this._data[i][j] * scalar;
}
}
return result;
}
multiply(matrix) {
if (this.colsCount !== matrix.rowsCount) {
throw new Error('Invalid dimensions for matrix multiplication.');
}
const result = new Matrix(this.rowsCount, matrix.colsCount);
for (let i = 0; i < this.rowsCount; i++) {
for (let j = 0; j < matrix.colsCount; j++) {
let sum = 0;
for (let k = 0; k < this.colsCount; k++) {
sum += this._data[i][k] * matrix._data[k][j];
}
result._data[i][j] = sum;
}
}
return result;
}
/** ---------------- EXTRA UTILITIES ---------------- **/
transpose() {
const result = new Matrix(this.colsCount, this.rowsCount);
for (let i = 0; i < this.rowsCount; i++) {
for (let j = 0; j < this.colsCount; j++) {
result._data[j][i] = this._data[i][j];
}
}
return result;
}
clone() {
const m = new Matrix(this.rowsCount, this.colsCount);
m._data = this._data.map((row) => [...row]);
return m;
}
equals(matrix, tolerance = 1e-9) {
if (this.rowsCount !== matrix.rowsCount || this.colsCount !== matrix.colsCount) {
return false;
}
for (let i = 0; i < this.rowsCount; i++) {
for (let j = 0; j < this.colsCount; j++) {
if (Math.abs(this._data[i][j] - matrix._data[i][j]) > tolerance) {
return false;
}
}
}
return true;
}
/** ---------------- INTERNAL HELPERS ---------------- **/
_assertSameDimensions(matrix) {
if (this.rowsCount !== matrix.rowsCount || this.colsCount !== matrix.colsCount) {
throw new Error('Matrices must have the same dimensions.');
}
}
/** ---------------- GETTERS / SETTERS ---------------- **/
get data() {
return this._data;
}
set data(newData) {
if (!Matrix.isValid(newData)) {
throw new Error('Invalid matrix data format.');
}
if (newData.length !== this.rowsCount || newData[0].length !== this.colsCount) {
throw new Error('Matrix dimensions mismatch.');
}
this._data = newData.map((row) => [...row]);
}
}
//# sourceMappingURL=Matrix.js.map