/** * 平移矩阵 * @param {*} x * @param {*} y */ export function getTranslateMatrix(x, y) { return new Matrix([ [1, 0, x], [0, 1, y], [0, 0, 1], ]); } /** * 缩放矩阵。 * @param {*} xScale * @param {*} yScale */ export function getScaleMatrix(xScale, yScale) { const xs = xScale || 1; const ys = yScale || 1; return new Matrix([ [xs, 0, 0], [0, ys, 0], [0, 0, 1], ]); } /** * 旋转矩阵。 * @param {*} xScale * @param {*} yScale */ export function getRotateMatrix(angle) { const sina = Math.sin(angle); const cosa = Math.cos(angle); return new Matrix([ [cosa, -sina, 0], [sina, cosa, 0], [0, 0, 1], ]); } /** * 镜像翻转矩阵。 * @param {*} mirrorAxis */ export function getMirrorMatrix(mirrorAxis) { const matrix = mirrorAxis === 'x' ? [ [-1, 0, 0], [0, 1, 0], [0, 0, 1], ] : [ [1, 0, 0], [0, -1, 0], [0, 0, 1], ]; return new Matrix(matrix); } /** * 求逆矩阵 * @param {*} matrix */ export function getInverseMatrix(matrix) { const determinant = getDeterminant(matrix.data, matrix.data.length); if (determinant === 0) { return new Matrix(); } const adjugateMatrix = getAdjugateMatrix(matrix, matrix.data.length); return matrixScale(adjugateMatrix, 1 / determinant); } /** * 伴随矩阵 * @param {*} matrix * @param {*} length */ function getAdjugateMatrix(matrix, length) { const m = new Matrix(); for (let i = 0; i < length; i++) { for (let j = 0; j < length; j++) { const subnetData = getCofactor(matrix.data, i, j); const determinant = getDeterminant(subnetData, length - 1); if ((j + i) % 2 === 0) { m.data[j][i] = determinant; } else { m.data[j][i] = -determinant; } } } return m; } /** * 求代数余子式 * @param {*} data * @param {*} n * @param {*} m */ function getCofactor(data, n, m) { const arrays: any = []; for (let i = 0; i < data.length; i++) { if (i !== n) { const array: any = []; for (let j = 0; j < data[i].length; j++) { if (j !== m) { array.push(data[i][j]); } } arrays.push(array); } } return arrays; } /** * 求n阶矩阵行列式 * @param {*} data n行n列二维数组 * @param {*} n */ function getDeterminant(data, n) { let result; if (n === 2) { return data[0][0] * data[1][1] - data[0][1] * data[1][0]; } if (n > 2) { result = 0; for (let i = 0; i < n; i++) { const arrays: any = []; for (let j = 1; j < n; j++) { const array: any = []; for (let k = 0; k < n; k++) { if (k !== i) { array.push(data[j][k]); } } arrays.push(array); } let value = data[0][i] * getDeterminant(arrays, n - 1); if (i % 2 === 1) { value = -value; } result += value; } return result; } } /** * 两个矩阵相乘 * @param {*} matrix1 * @param {*} matrix2 */ function matrixMultiplyMatrix(matrix1, matrix2) { const result = new Matrix(); for (let i = 0; i < 3; i++) { for (let j = 0; j < 3; j++) { result.data[i][j] = 0; for (let m = 0; m < 3; m++) { result.data[i][j] = result.data[i][j] + matrix1.data[i][m] * matrix2.data[m][j]; } } } return result; } /** * 生成矩阵对象 * @param {*} param n行n列二维数组 */ export class Matrix { public data; public constructor(param: any = undefined) { this.data = [ [1, 0, 0], [0, 1, 0], [0, 0, 1], ]; if (param) { for (let i = 0; i < 3; i++) { for (let j = 0; j < 3; j++) { this.data[i][j] = param[i][j]; } } } } public clone() { const result: any = []; for (let i = 0; i < this.data.length; i++) { const array: any = []; const arr = this.data[i]; for (let j = 0; j < arr.length; j++) { array.push(arr[j]); } result.push(array); } const matrix = new Matrix(result); return matrix; } } /** * 多个矩阵相乘(左乘), matrixMutiply(matrix1, matrix2,matrix3...) * @param {*} args */ export function matrixMutiply(...args) { const matrixs = args; if (matrixs && matrixs.length > 1) { let result = matrixs[0]; for (let i = 1; i < matrixs.length; i++) { result = matrixMultiplyMatrix(result, matrixs[i]); } return result; } } /** * 点乘以矩阵 * @param {*} point * @param {*} matrix */ export function pointMultiplyMatrix(point: { x: number; y: number }, matrix) { return { x: point.x * matrix.data[0][0] + point.y * matrix.data[0][1] + matrix.data[0][2], y: point.x * matrix.data[1][0] + point.y * matrix.data[1][1] + matrix.data[1][2], }; } /** * 缩放矩阵 * @param {*} matrix * @param {*} scale */ export function matrixScale(matrix, scale) { const m = new Matrix(); const arrays = matrix.data; for (let i = 0; i < arrays.length; i++) { for (let j = 0; j < arrays[i].length; j++) { m.data[i][j] = arrays[i][j] * scale; } } return m; } /** * 矩阵转置 * @param matrix */ export function matrixTranspose(matrix) { if (matrix instanceof Matrix) { const result = new Matrix(); const source = matrix.data; const target = result.data; for (let i = 0; i < 3; i++) { for (let j = 0; j < 3; j++) { target.data[j][i] = source.data[i][j]; } } return result; } } /** * 根据缩放比例、位移计算矩阵 * @param {*} source * @param {*} target * @param {*} yMirror */ export function createWorldMatrix( source, target, yMirror, xyChange = false, xMirror = false, ) { // 缩放比例 let sWidth = source.width; let sHeight = source.height; if (xyChange) { const temp = sWidth; sWidth = sHeight; sHeight = temp; } const yScale = source.height === 0 || target.height === 0 ? 0 : target.height / sHeight; const xScale = source.width === 0 || target.width === 0 ? 0 : target.width / sWidth; // 用于中心平移回原点 const translateMatrix = getTranslateMatrix(-source.x, -source.y); const scaleMatrix = getScaleMatrix(xScale, yScale); // 用于将中心移动到targetRect.x, tagetRect.y const translateRecoverMatrix = getTranslateMatrix(target.x, target.y); let worldMatrix = new Matrix(); if (yMirror) { // 改变y值符号 worldMatrix = new Matrix([ [1, 0, 0], [0, -1, 0], [0, 0, 1], ]); } let xMirrorMatrix = new Matrix(); let xTranslateAfterMirror = new Matrix(); if (xMirror) { xMirrorMatrix = new Matrix([ [-1, 0, 0], [0, 1, 0], [0, 0, 1], ]); xTranslateAfterMirror = getTranslateMatrix(source.width, 0); } if (xyChange) { worldMatrix = getRotateMatrix(-Math.PI / 2); } worldMatrix = matrixMutiply( translateMatrix, xMirrorMatrix, xTranslateAfterMirror, worldMatrix, scaleMatrix, translateRecoverMatrix, ); return worldMatrix; } export function equal(value1, value2, strict = false): boolean { let result; if (strict) { result = value1 === value2; } else { // @ts-ignore result = value1 == value2; } if (result) { return result; } return ( typeof value1 === 'number' && typeof value2 === 'number' && isNaN(value1) && isNaN(value2) ); } export function matrixCompare(m1, m2) { if (m1 === m2) { return true; } if (equal(m1, null) && equal(m2, null)) { return true; } if (!equal(m1, null) && !equal(m2, null)) { if (!equal(m1.data.length, m2.data.length)) { return false; } const data1 = m1.data; const data2 = m2.data; for (let i = 0; i < data1.length; i++) { const array1 = data1[i]; const array2 = data2[i]; if (!equal(array1.length, array2.length)) { return false; } for (let j = 0; j < array1.length; j++) { const v1 = array1[j]; const v2 = array2[j]; if (v1 !== v2) { return false; } } } return true; } return false; } /** * 地图的计算矩阵。根据缩放比例、位移计算矩阵 * @param {*} source 数据矩阵 * @param {*} target 画布矩阵 */ export function createWorldMatrixMap(source, target) { // 缩放比例 const sWidth = source.width; const sHeight = source.height; const yScale = source.height == 0 || target.height == 0 ? 0 : target.height / sHeight; const xScale = source.width == 0 || target.width == 0 ? 0 : target.width / sWidth; // 1. 先进行缩放 const scaleMatrix = getScaleMatrix(xScale, yScale); // 2. 再进行y轴的映射(旋转)。 因为维度的值和canvas坐标轴的值刚好相反 let worldMatrix = new Matrix([ [1, 0, 0], [0, -1, 0], [0, 0, 1], ]); // 3. 用于中心平移回原点。因为做了旋转,所以需要平移回去 const translateMatrix = getTranslateMatrix( target.x, target.height + target.y, ); worldMatrix = matrixMutiply(scaleMatrix, worldMatrix, translateMatrix); return worldMatrix; } /** * 地图的计算矩阵。根据缩放比例、位移计算矩阵 * @param {*} source * @param {*} target * @param {*} yMirror */ export function createWorldMatrixTopo(source, target) { // 缩放比例 const sWidth = source.width; const sHeight = source.height; const yScale = source.height == 0 || target.height == 0 ? 0 : target.height / sHeight; const xScale = source.width == 0 || target.width == 0 ? 0 : target.width / sWidth; // 进行缩放 const scaleMatrix = getScaleMatrix(xScale, yScale); // 用于中心平移回原点。因为做了旋转,所以需要平移回去 const translateMatrix = getTranslateMatrix(100, 100); const worldMatrix = matrixMutiply(scaleMatrix, translateMatrix); return worldMatrix; } /** * TODO: * 目前按照这片文章的介绍进行矩阵计算 * https://km.woa.com/articles/show/513878 * * 和t1的计算矩阵和计算顺序相反,后续待讨论 * https://km.woa.com/group/17746/articles/show/425498在这里未作说明 */