Release package version 0.0.3
All checks were successful
CI / build (push) Successful in 29s

Added Matrix2d class, update Transform class
This commit is contained in:
Andrey Kernichniy 2026-03-15 00:14:45 +07:00
parent e5f5f3a08e
commit 4dbba31ed5
7 changed files with 159 additions and 1 deletions

View file

@ -1 +1,15 @@
import { Rectangle, Transform } from "../models";
export const degToRad = (deg: number) => (deg * Math.PI) / 180; export const degToRad = (deg: number) => (deg * Math.PI) / 180;
export const radToDeg = (radians: number) => radians * (180 / Math.PI);
export const getBounds = (rectangle: Rectangle, transform: Transform) => {
const { top, left, bottom, right } = rectangle;
const matrix2d = transform.toMatrix2D();
const topLeft = matrix2d.transformPoint({ x: left, y: top });
const bottomRight = matrix2d.transformPoint({ x: right, y: bottom });
return Rectangle.fromPoints(topLeft.x, topLeft.y, bottomRight.x, bottomRight.y);
};

View file

@ -7,3 +7,16 @@ export interface IPoint {
x: number; x: number;
y: number; y: number;
} }
export interface ITransform {
scaleX: number;
scaleY: number;
translateY: number;
translateX: number;
skewY: number;
skewX: number;
rotate: number;
}

View file

@ -1,5 +1,6 @@
import { IPoint } from "../interfaces"; import { IPoint } from "../interfaces";
import { Point } from "./point"; import { Point } from "./point";
import { Rectangle } from "./rectangle";
export class Ellipse { export class Ellipse {
center = new Point(); center = new Point();
@ -19,4 +20,17 @@ export class Ellipse {
this.radiusX = radiusX; this.radiusX = radiusX;
this.radiusY = radiusY; this.radiusY = radiusY;
} }
static fromRectangle(rectangle: Rectangle) {
return new Ellipse(rectangle.center, rectangle.width / 2, rectangle.height / 2);
}
toRectangle() {
return Rectangle.fromPoints(
this.center.x - this.radiusX,
this.center.y - this.radiusY,
this.center.x + this.radiusX,
this.center.y + this.radiusY,
);
}
} }

View file

@ -4,3 +4,4 @@ export * from "./size";
export * from "./ellipse"; export * from "./ellipse";
export * from "./rectangle"; export * from "./rectangle";
export * from "./transform"; export * from "./transform";
export * from "./matrix-2d";

View file

@ -0,0 +1,78 @@
import { IPoint } from "../interfaces";
import { Point } from "./point";
export type Matrix = [[number, number, number], [number, number, number], [number, number, number]];
const DEF_MATRIX: Matrix = [
[1, 0, 0],
[0, 1, 0],
[0, 0, 1],
];
export class Matrix2D {
private _matrix: Matrix;
constructor(matrix?: Matrix) {
this._matrix = matrix ?? DEF_MATRIX;
}
clone(): Matrix2D {
return new Matrix2D(this.getData());
}
multiply(matrix: Matrix2D): Matrix2D {
const m = structuredClone(DEF_MATRIX);
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
m[i][j] = 0;
for (let k = 0; k < 3; k++) {
m[i][j] += this._matrix[i][k] * matrix._matrix[k][j];
}
}
}
return new Matrix2D(m);
}
translate(x: number, y: number): Matrix2D {
const translationMatrix = new Matrix2D([
[1, 0, x],
[0, 1, y],
[0, 0, 1],
]);
return this.multiply(translationMatrix);
}
rotate(angle: number): Matrix2D {
const cos = Math.cos(angle);
const sin = Math.sin(angle);
const rotationMatrix = new Matrix2D([
[cos, -sin, 0],
[sin, cos, 0],
[0, 0, 1],
]);
return this.multiply(rotationMatrix);
}
scale(sx: number, sy: number): Matrix2D {
const scaleMatrix = new Matrix2D([
[sx, 0, 0],
[0, sy, 0],
[0, 0, 1],
]);
return this.multiply(scaleMatrix);
}
transformPoint(point: IPoint): Point {
const x = point.x;
const y = point.y;
const w = this._matrix[2][0] * x + this._matrix[2][1] * y + this._matrix[2][2];
return new Point(
(this._matrix[0][0] * x + this._matrix[0][1] * y + this._matrix[0][2]) / w,
(this._matrix[1][0] * x + this._matrix[1][1] * y + this._matrix[1][2]) / w,
);
}
getData(): Matrix {
return structuredClone(this._matrix);
}
}

View file

@ -1,3 +1,7 @@
import { degToRad, radToDeg } from "../functions";
import { ITransform } from "../interfaces";
import { Matrix2D } from "./matrix-2d";
export class Transform { export class Transform {
scaleX = 1; scaleX = 1;
scaleY = 1; scaleY = 1;
@ -9,4 +13,38 @@ export class Transform {
skewX = 0; skewX = 0;
rotate = 0; rotate = 0;
constructor(transform?: Partial<ITransform>) {
this.scaleX = transform?.scaleX ?? 1;
this.scaleY = transform?.scaleY ?? 1;
this.translateX = transform?.translateX ?? 0;
this.translateY = transform?.translateY ?? 0;
this.skewY = transform?.scaleY ?? 0;
this.skewX = transform?.scaleX ?? 0;
this.rotate = transform?.rotate ?? 0;
}
toMatrix2D(): Matrix2D {
const matrix = new Matrix2D();
matrix.translate(this.translateX, this.translateY).rotate(degToRad(this.rotate)).scale(this.scaleX, this.scaleY);
return matrix;
}
static fromMatrix2D(matrix: Matrix2D): Transform {
const m = matrix.getData();
const transform = new Transform();
// Извлечение масштаба
transform.scaleX = Math.sqrt(m[0][0] * m[0][0] + m[0][1] * m[0][1]);
transform.scaleY = Math.sqrt(m[1][0] * m[1][0] + m[1][1] * m[1][1]);
// Извлечение поворота
transform.rotate = radToDeg(Math.atan2(m[0][1], m[0][0]));
// Извлечение сдвига
transform.translateX = m[0][2];
transform.translateY = m[1][2];
return transform;
}
} }

View file

@ -1,6 +1,6 @@
{ {
"name": "@gxc-solutions/math", "name": "@gxc-solutions/math",
"version": "0.0.2", "version": "0.0.3",
"main": "index.js", "main": "index.js",
"author": "GXC Solutions", "author": "GXC Solutions",
"publishConfig": { "publishConfig": {