UNPKG

@omjs/matrix2d

Version:

CSS tranform-2D matrix

205 lines (190 loc) 5.66 kB
/** * Matrix2D矩阵操作函数 */ import { map } from '@omjs/iterable'; import { Matrix2D } from './core'; import Angle from '@omjs/angle'; import { round } from './util'; /** * 判断是否是类Matrix2D数据结构 * @param {*} mat */ export function isMatrix2DLike(mat) { return Matrix2D.isMatrix2D(mat) || (Object.keys(mat || {}).join('') === '012345'); } /** * 简化数值,根据指定的小数位数四舍五入 * @param {number} digit 小数位数 */ export function simplify(digit = 3) { let m = isMatrix2DLike(this) ? this : new Matrix2D(this); return new Matrix2D(m::map(v => round(digit)(v))); } /** * 左乘 * @param {Matrix2D} mat1 被乘矩阵 * @param {Matrix2D} mat2 乘矩阵 * @returns {Matrix2D} */ export function lmul(mat) { let m1 = isMatrix2DLike(this) ? this : new Matrix2D(this); let m2 = isMatrix2DLike(mat) ? mat : new Matrix2D(mat); return new Matrix2D( m1[0] * m2[0] + m1[1] * m2[2], m1[0] * m2[1] + m1[1] * m2[3], m1[2] * m2[0] + m1[3] * m2[2], m1[2] * m2[1] + m1[3] * m2[3], m1[4] * m2[0] + m1[5] * m2[2] + m2[4], m1[4] * m2[1] + m1[5] * m2[3] + m2[5] ); } /** * 右乘 * @param {Matrix2D} mat 被乘矩阵 * @returns {Matrix2D} */ export function rmul(mat) { return mat::lmul(this); } /** * 乘 * @param {Matrix2D} mat 被乘矩阵 * @returns {Matrix2D} */ export function mul(mat, dir = Matrix2D.MUL_DIR.L) { if (dir === Matrix2D.MUL_DIR.L) { return this::lmul(mat); } return this::rmul(mat); } /** * 旋转变换 * @public * @param {Angle | number | string} angle 旋转角,默认 0rad,当传入为数值时单位为deg,当传入为字符串时 * @param {Symbol} dir 旋转方向,默认 顺时针方向 * @returns {Matrix2D} */ export function rotate(angle = new Angle(), dir = Matrix2D.ROTATE_DIR.CW) { let rad = Angle.from(angle).rad; let cos = Math.cos(rad); let sin = Math.sin(rad); let mat = [cos, sin, -sin, cos, 0, 0]; if (dir === Matrix2D.ROTATE_DIR.ACW) { mat = [cos, -sin, sin, cos, 0, 0]; } return this::lmul(mat); } /** * 缩放变换 * @public * @param {number} x x方向缩放量 * @param {number} y y方向缩放量 * @returns {Matrix2D} */ export function scale(x = 1, y = 1) { return this::lmul([x, 0, 0, y, 0, 0]); } /** * 倾斜变换 * @public * @param {Angle | string | number} angX x轴倾斜角 * @param {Angle | string | number} angY y轴倾斜角 * @returns {Matrix2D} */ export function skew(angX = new Angle(), angY = new Angle()) { let radX = Angle.from(angX).rad; let radY = Angle.from(angY).rad; return this::lmul([1, Math.tan(radY), Math.tan(radX), 1, 0, 0]); } /** * 平移 * @param {number} x 水平方向平移量 * @param {number} y 垂直方向平移量 * @returns {Matrix2D} */ export function translate(x = 0, y = 0) { return this::lmul([1, 0, 0, 1, x, y]); } /** * 平移,同translate * @param {number} x 水平方向平移量 * @param {number} y 垂直方向平移量 * @returns {Matrix2D} */ export function move(x = 0, y = 0) { return this::translate(x, y); } /** * 对点应用矩阵变换 * @param {number[]} pt [x, y] 变换点坐标 * @returns {number[]} [x, y] 变换后点坐标 */ export function exec([x = 0, y = 0] = []) { let m = isMatrix2DLike(this) ? this : new Matrix2D(this); return [ m[0] * x + m[2] * y + m[4], m[1] * x + m[3] * y + m[5] ]; } /** * 对矩形应用矩阵变换 * @param {Object} rect {x, y, w, h} 矩形描述对象 * @param {number[]} center [cx, cy] 变换中心点 * @returns {Object} {x, y, w, h} 变换后的矩形描述对象 */ export function execRect( { x = 0, y = 0, w = 0, h = 0 } = {}, [cx = 0, cy = 0] = [] ) { let bfr = [ [x - cx, y - cy], [x + w - cx, y - cy], [x + w - cx, y + h - cy], [x - cx, y + h - cy] ]; // 变换前矩形顶点坐标(左上、右上、右下、左下) let aft = bfr.map(v => this::exec(v)); let xArr = aft.map(v => v[0]); let yArr = aft.map(v => v[1]); let xMin = Math.min(...xArr); let xMax = Math.max(...xArr); let yMin = Math.min(...yArr); let yMax = Math.max(...yArr); return { x: xMin + cx, y: yMin + cy, w: xMax - xMin, h: yMax - yMin }; } /** * 由Matrix2D推导rotate、scale、skew、translate等值 * 先rotate,再scale,再skew,再translate * @todo 已知bug: 当rotate不是90的倍数时,计算值不准确 * @see http://www.wolframalpha.com/input/?i=%7B%7B1,0%7D,%7Bt1,1%7D%7D*%7B%7Bs1,0%7D,%7B0,s2%7D%7D*%7B%7Bcos%5Br%5D,+-sin%5Br%5D%7D,%7Bsin%5Br%5D,+cos%5Br%5D%7D%7D+%3D+%7B%7Bm0,m2%7D,%7Bm1,m3%7D%7D+and+m0*m3!%3Dm1*m2 * @returns {object} {rotate, scale, scaleX, scaleY, skew, skewX, skewY, translate, translateX, translateY} */ export function decompose() { let m = isMatrix2DLike(this) ? this : new Matrix2D(this); let dm = m[0] * m[3] - m[1] * m[2]; if (dm === 0) { throw new Error('Only an invertible matrix can be decomposed.'); } let sm = m[0] ** 2 + m[2] ** 2; let rotate = Angle.fromRad(2 * Math.atan2(m[0] - Math.sqrt(sm), m[2])); let scaleX = Math.sqrt(sm); let scaleY = dm / scaleX; let skewX = Angle.fromRad(Math.atan2(m[0] * m[1] + m[2] * m[3], sm)); let skewY = Angle.fromRad(0); return { rotate, scaleX, scaleY, scale: [scaleX, scaleY], skewX, skewY, skew: [skewX, skewY], translateX: m[4], translateY: m[5], translate: [m[4], m[5]] }; }