b9e42c3ef4
Build and Deploy / build-and-deploy (push) Has been cancelled
- 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.
193 lines
4.0 KiB
TypeScript
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,
|
|
};
|
|
}
|