我正在学习线性代数,并试图用基本的线性变换(旋转、缩放、平移)制作一个小程序。
这是完整的工作示例:
https://codesandbox.io/embed/determined-diffie-t2iy5?fontsize=14&hidenavigation=1&theme=dark
我编写了用于为每个变换生成每个矩阵的函数和计算它们的函数(将矩阵与一个点相乘,将矩阵相乘)。
export const multiplyMatrixWithPoint = (matrix, point) => {
return point.map((dimension, index) => {
let result = 0;
for (let i = 0; i < 4; i++) {
const matrixIndex = index * 4 + i;
result += dimension * matrix[matrixIndex];
}
return +result.toFixed(2);
})
};
// Just creating 2D array to make it easy to calculate the matrix
export const matrixToPoints = matrix => {
const result = [];
for (let i = 0; i < 4; i++) {
const onePoint = [];
for (let j = 0; j < 4; j++) {
onePoint.push(matrix[4 * i + j]);
}
result.push(onePoint);
}
return result;
};
// Just making 2D array to 1D
export const pointsToMatrix = points => points.reduce((acc, points) => [...acc, ...points], []);
// Transpose function to make the matrix multiplication correct
export const transposeCSSMatrixToTransform = matrix => matrix[0].map((column, index) => matrix.map(row => row[index]));
export const multiplyMatrices = (matrixA, matrixB) => {
const separatePoints = transposeCSSMatrixToTransform(matrixToPoints(matrixB));
return pointsToMatrix(transposeCSSMatrixToTransform(separatePoints.map(point => multiplyMatrixWithPoint(matrixA, point))));
};
export const rotationMatrixGenerator = (matrix, angle) => {
const radians = degreeToRadian(angle);
matrix[0] = Math.cos(radians);
matrix[1] = -Math.sin(radians);
matrix[4] = Math.sin(radians);
matrix[5] = Math.cos(radians);
return matrix;
};
export const scaleMatrixGenerator = (matrix, { x, y }) => {
matrix[0] = x;
matrix[5] = y;
return matrix;
};
export const translateMatrixGenerator = (matrix, { x, y }) => {
matrix[12] = x;
matrix[13] = y;
return matrix;
};
为了显示转换,我使用了 CSS 的matrix3d
export const matrixToCSSMatrix = matrix => `matrix3d(${matrix.join(',')})`;
这是代码的执行部分
...
const [matrix, setMatrix] = useState(DEFAULT_MATRIX);
const [rotationAngle, setRotationAngle] = useState(0);
const [scale, setScale] = useState(DEFAULT_XY);
const [translate, setTranslate] = useState(DEFAULT_XY);
const classes = useStyles();
const { app } = classes;
// ---------------------- Rotate --------------------- //
const rotate = useCallback(e => {
const { value: angle } = e.target;
const rotationMatrix = rotationMatrixGenerator(matrix, +angle);
const updatedMatrix = multiplyMatrices(matrix, rotationMatrix);
setMatrix(updatedMatrix);
setRotationAngle(angle);
}, [matrix]);
// ---------------------- Scale --------------------- //
const changeScale = useCallback((e, dimension) => {
const { value } = e.target;
const updatedScale = {
...scale,
[dimension]: +value
};
const scaleMatrix = scaleMatrixGenerator(matrix, updatedScale);
const updatedMatrix = multiplyMatrices(matrix, scaleMatrix);
setMatrix(scaleMatrix);
setScale(updatedScale);
}, [matrix, scale]);
// ------------------ Translate --------------------- //
const changeTranslation = useCallback((e, position) => {
const { value } = e.target;
const updatedTranslation = {
...translate,
[position]: +value
};
const translateMatrix = translateMatrixGenerator(matrix, updatedTranslation);
const updatedMatrix = multiplyMatrices(matrix, translateMatrix);
setMatrix(translateMatrix);
setTranslate(updatedTranslation);
}, [matrix, translate]);
...
首先我认为这是因为我没有转置矩阵,我尝试转置它,它有一点帮助但没有修复所有错误。此外,我尝试收集所有转换并立即应用它们,但这是一个非常糟糕的解决方案并且效果不佳。
我需要单独的旋转、缩放和过渡,但旋转和过渡效果不佳,因为它们使用相同的矩阵段。此外,我无法实现对 Z 轴的正常旋转。