import { ItemTransform } from "../attributes/ItemTransform";
import "./PointType";
/**
* @typedef {Float32Array} Matrix3
*/
/**
* Matrix calculation utilities
* @typedef {Object} Matrix3Utilities
* @property {function():Matrix3} identity Create irentity matrix
* @property {function(Matrix3, Object)} projection Update projection matrix
* @property {function(ItemTransform, Matrix3)} transformLocal Calculate local transformation
* @property {function(Matrix3, ItemTransform, Matrix3)} transform Calculate global transformation
* @property {function(Matrix3, Matrix3)} inverse Create inverse matrix
* @property {function(matrix, Point):boolean} isPointInMatrix Returns true if the point in the matrix
* @property {function(Matrix3, array, Object)} calcCorners Calculate cornrers
*/
export const Matrix3Utilities = {
/**
* Create irentity matrix
* @returns {Matrix3}
*/
identity: () => new Float32Array([1, 0, 0, 1, 0, 0]),
/**
* Update projection matrix
* @param {Matrix3} destinationMatrix
* @param {Object} resolution
* @param {number} resolution.width
* @param {number} resolution.height
*/
projection: (destinationMatrix, resolution) => {
destinationMatrix[0] = 2 / resolution.width;
destinationMatrix[1] = destinationMatrix[2] = 0;
destinationMatrix[3] = -2 / resolution.height;
destinationMatrix[4] = -1;
destinationMatrix[5] = 1;
},
/**
* Calculate local transformation
* @param {Matrix3} destinationMatrix
* @param {ItemTransform} itemTransform
*/
transformLocal: (destinationMatrix, itemTransform) => {
const { anchorX, anchorY, scaledWidth, scaledHeight } = itemTransform;
destinationMatrix[0] = itemTransform.cosRotationA * scaledWidth;
destinationMatrix[1] = itemTransform.sinRotationA * scaledWidth;
destinationMatrix[2] = -itemTransform.sinRotationB * scaledHeight;
destinationMatrix[3] = itemTransform.cosRotationB * scaledHeight;
destinationMatrix[4] = itemTransform.x - anchorX * destinationMatrix[0] - anchorY * destinationMatrix[2];
destinationMatrix[5] = itemTransform.y - anchorX * destinationMatrix[1] - anchorY * destinationMatrix[3];
},
/**
* Calculate global transformation
* @param {Matrix3} destinationMatrix
* @param {Matrix3} matrix
* @param {ItemTransform} itemTransform
*/
transform: (destinationMatrix, matrix, itemTransform) => {
const {
x,
y,
anchorX,
anchorY,
sinRotationA,
sinRotationB,
cosRotationA,
cosRotationB,
scaledWidth,
scaledHeight,
} = itemTransform;
destinationMatrix[0] = (cosRotationA * matrix[0] + sinRotationA * matrix[2]) * scaledWidth;
destinationMatrix[1] = (cosRotationA * matrix[1] + sinRotationA * matrix[3]) * scaledWidth;
destinationMatrix[2] = (cosRotationB * matrix[2] - sinRotationB * matrix[0]) * scaledHeight;
destinationMatrix[3] = (cosRotationB * matrix[3] - sinRotationB * matrix[1]) * scaledHeight;
// prettier-ignore
destinationMatrix[4] =
-anchorX * destinationMatrix[0] -
anchorY * destinationMatrix[2] +
x * matrix[0] +
y * matrix[2] +
matrix[4];
// prettier-ignore
destinationMatrix[5] =
-anchorX * destinationMatrix[1] -
anchorY * destinationMatrix[3] +
x * matrix[1] +
y * matrix[3] +
matrix[5];
},
/**
* Create inverse matrix
* @param {Matrix3} destinationMatrix
* @param {Matrix3} matrix
*/
inverse: (destinationMatrix, matrix) => {
const det = 1 / (matrix[0] * matrix[3] - matrix[2] * matrix[1]);
destinationMatrix[0] = det * matrix[3];
destinationMatrix[1] = -det * matrix[1];
destinationMatrix[2] = -det * matrix[2];
destinationMatrix[3] = det * matrix[0];
destinationMatrix[4] = det * (matrix[2] * matrix[5] - matrix[3] * matrix[4]);
destinationMatrix[5] = -det * (matrix[0] * matrix[5] - matrix[1] * matrix[4]);
},
/**
* Returns true if the point in the matrix
* @param {Matrix3} matrix
* @param {Point} point
* @returns {boolean}
*/
isPointInMatrix: (matrix, point) => {
const x = point.x * matrix[0] + point.y * matrix[2] + matrix[4];
const y = point.x * matrix[1] + point.y * matrix[3] + matrix[5];
return x >= 0 && x <= 1 && y >= 0 && y <= 1;
},
/**
* Calculate cornrers
* @param {Array<Point>} corners
* @param {Matrix3} matrix
* @param {Object} resolution
* @param {number} resolution.widthHalf
* @param {number} resolution.heightHalf
*/
calcCorners: (corners, matrix, resolution) => {
const widthHalf = resolution.widthHalf;
const heightHalf = resolution.heightHalf;
corners[0].x = widthHalf + matrix[4] * widthHalf;
corners[0].y = resolution.height - (heightHalf + matrix[5] * heightHalf);
corners[1].x = corners[0].x + (matrix[0] + matrix[2]) * widthHalf;
corners[1].y = corners[0].y - (matrix[1] + matrix[3]) * heightHalf;
corners[2].x = corners[0].x + (matrix[0] + widthHalf * matrix[2]);
corners[2].y = corners[0].y - (matrix[1] + heightHalf * matrix[3]);
corners[3].x = corners[0].x + (widthHalf * matrix[0] + matrix[2]);
corners[3].y = corners[0].y - (heightHalf * matrix[1] + matrix[3]);
},
};