Files
lit-examples/lit-components/lit-node-editor/canvas/matrix.ts
T
rodydavis b9e42c3ef4
Build and Deploy / build-and-deploy (push) Has been cancelled
fix(lit-components): fix rendering and interaction issues in force-graph, html-table, and draggable-dom
- lit-force-graph: Refactored for instance reuse, better lifecycle management, and added dynamic imports for AR/VR modes.
- lit-html-table: Improved visual design, readability of headers, and fixed reactivity in editable mode.
- lit-draggable-dom: Fixed clipping issues by switching to relative/absolute positioning and resolved a panning speed bug caused by CSS variable inheritance.
2026-05-17 00:41:14 -07:00

193 lines
4.0 KiB
TypeScript

import { Offset } from "./utils";
/**
* Matrix
*/
export type Matrix = number[];
const DEFAULT_MATRIX: Matrix = [1, 0, 0, 1, 0, 0];
const DEFAULT_INVERSE_MATRIX: Matrix = [1, 0, 0, 1];
/**
* Matrix context
*/
export interface MatrixContext {
matrix: Matrix;
inverseMatrix: Matrix;
}
/**
* Default matrix context
*/
export const defaultMatrix = createMatrix({ x: 0, y: 0 }, 1, 0);
/**
* Create a matrix and inverse matrix
*
* @param offset - offset
* @param scale - scale factor
* @param rotation - in radians
*
* @link https://stackoverflow.com/a/34598847/7303311
*/
export function createMatrix(
offset: Offset,
scale: number,
rotation: number
): MatrixContext {
const m = [...DEFAULT_MATRIX];
const im = [...DEFAULT_INVERSE_MATRIX];
m[3] = m[0] = Math.cos(rotation) * scale;
m[2] = -(m[1] = Math.sin(rotation) * scale);
m[4] = offset.x;
m[5] = offset.y;
const cross = m[0] * m[3] - m[1] * m[2];
im[0] = m[3] / cross;
im[1] = -m[1] / cross;
im[2] = -m[2] / cross;
im[3] = m[0] / cross;
return {
matrix: m,
inverseMatrix: im,
};
}
/**
* Create a local offset from a global offset
*
* @param matrix - matrix
* @param inverseMatrix - inverse matrix
* @param offset - offset
*
* @link https://stackoverflow.com/a/34598847/7303311
*/
export function toWorld(context: MatrixContext, offset: Offset): Offset {
let xx, yy, m;
m = context.inverseMatrix;
xx = offset.x - context.matrix[4];
yy = offset.y - context.matrix[5];
const localX = xx * m[0] + yy * m[2];
const localY = xx * m[1] + yy * m[3];
return {
x: localX,
y: localY,
};
}
/**
* Matrix info
*
* @param matrix - matrix
* @returns scale, offset, rotation
*/
export function matrixInfo(context: MatrixContext) {
const rotation = rotationFromMatrix(context);
const { scale } = scaleFromMatrix(context);
const offset = offsetFromMatrix(context);
return {
rotation,
scale,
offset,
};
}
/**
* Get rotation from matrix
*
* @param matrix - matrix
* @returns rotation in radians
*/
export function rotationFromMatrix(context: MatrixContext) {
const matrix = context.matrix;
const rad = Math.atan2(matrix[1], matrix[0]);
return rad;
}
/**
* Get scale factor from matrix
*
* @param matrix - matrix
* @returns scale factor
*/
export function scaleFromMatrix(context: MatrixContext): {
scaleX: number;
scaleY: number;
scale: number;
} {
const matrix = context.matrix;
const scaleX = Math.sqrt(matrix[0] * matrix[0] + matrix[1] * matrix[1]);
const scaleY = Math.sqrt(matrix[2] * matrix[2] + matrix[3] * matrix[3]);
return {
scaleX,
scaleY,
scale: Math.max(scaleX, scaleY),
};
}
/**
* Get offset from matrix
*
* @param matrix - matrix
* @returns offset
*/
export function offsetFromMatrix(context: MatrixContext) {
const matrix = context.matrix;
return {
x: matrix[4],
y: matrix[5],
};
}
/**
* Apply matrix to canvas
*
* @param ctx - canvas context
* @param matrix - matrix
*/
export function applyMatrix(
ctx: CanvasRenderingContext2D,
context: MatrixContext
) {
const m = context.matrix;
ctx.setTransform(m[0], m[1], m[2], m[3], m[4], m[5]);
}
/**
* Apply default matrix to canvas
*
* @param ctx - canvas context
*/
export function applyDefaultMatrix(ctx: CanvasRenderingContext2D) {
const m = DEFAULT_MATRIX;
ctx.setTransform(m[0], m[1], m[2], m[3], m[4], m[5]);
}
/**
* Get matrix from canvas
*
* @param ctx - canvas context
* @returns Matrix context
*/
export function getMatrixFromCanvas(
ctx: CanvasRenderingContext2D
): MatrixContext {
const transform = ctx.getTransform();
const m = [...DEFAULT_MATRIX];
const im = [...DEFAULT_INVERSE_MATRIX];
m[0] = transform.a;
m[1] = transform.b;
m[2] = transform.c;
m[3] = transform.d;
m[4] = transform.e;
m[5] = transform.f;
const cross = m[0] * m[3] - m[1] * m[2];
im[0] = m[3] / cross;
im[1] = -m[1] / cross;
im[2] = -m[2] / cross;
im[3] = m[0] / cross;
return {
matrix: m,
inverseMatrix: im,
};
}