Matrix4的平移、旋转缩放
2026-04-30 12:59:52
238
分类:Algorithm
代码测试
var matrix4 = new Matrix4();
var vector3 = new Vector3(1, 1, 0); // 空间上一点
matrix4.makeTranslation(10, 8, 6); // 矩阵平移变换
var newVector = vector3.applyMatrix4(matrix4); // 从矩阵获取位置(相对位置变换)
console.log(newVector); // 结果:Vector3 {x: 11, y: 9, z: 6}Vector3
class Vector3 {
constructor(x = 0, y = 0, z = 0) {
this.x = x;
this.y = y;
this.z = z;
}
copy(v) {
this.x = v.x;
this.y = v.y;
this.z = v.z;
return this;
}
clone() {
return new this.constructor(this.x, this.y, this.z);
}
set(x, y, z) {
this.x = x;
this.y = y;
this.z = z;
return this;
}
zero() {
this.x = 0;
this.y = 0;
this.z = 0;
return this;
}
/**
* 左加向量
* @param v
* @returns {Vector3}
*/
add(v) {
this.x += v.x;
this.y += v.y;
this.z += v.z;
return this;
}
/**
* 左加标量
* @param s
* @returns {Vector3}
*/
addScalar(s) {
this.x += s;
this.y += s;
this.z += s;
return this;
}
/**
* 两向量相加
* @param a
* @param b
* @returns {Vector3}
*/
addVectors(a, b) {
this.x = a.x + b.x;
this.y = a.y + b.y;
this.z = a.z + b.z;
return this;
}
sub(v) {
this.x -= v.x;
this.y -= v.y;
this.z -= v.z;
return this;
}
subScalar(s) {
this.x -= s;
this.y -= s;
this.z -= s;
return this;
}
subVectors(a, b) {
this.x = a.x - b.x;
this.y = a.y - b.y;
this.z = a.z - b.z;
return this;
}
multiply(v) {
this.x *= v.x;
this.y *= v.y;
this.z *= v.z;
return this;
}
multiplyScalar(scalar) {
this.x *= scalar;
this.y *= scalar;
this.z *= scalar;
return this;
}
multiplyVectors(a, b) {
this.x = a.x * b.x;
this.y = a.y * b.y;
this.z = a.z * b.z;
return this;
}
divide(v) {
this.x /= v.x;
this.y /= v.y;
this.z /= v.z;
return this;
}
divideScalar(scalar) {
return this.multiplyScalar(1 / scalar);
}
/**
* 标准化向量,长度为1
* @returns {*}
*/
normalize() {
return this.divideScalar(this.length() || 1);
}
/**
* 反转向量
* @returns {Vector2}
*/
negate() {
this.x = -this.x;
this.y = -this.y;
this.z = -this.z;
return this;
}
length() {
return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
}
lengthSq() {
return this.x * this.x + this.y * this.y + this.z * this.z;
}
// 与向量的角度
angleTo(v) {
let theta = this.dot(v) / (Math.sqrt(this.lengthSq() * v.lengthSq()));
return Math.acos(_Math.clamp(theta, -1, 1));
}
distanceTo(v) {
return Math.sqrt(this.distanceToSquared(v));
}
distanceToSquared(v) {
let dx = this.x - v.x,
dy = this.y - v.y,
dz = this.z - v.z;
return dx * dx + dy * dy + dz * dz;
}
/**
* 点乘
* @param v
* @returns {number}
*/
dot(v) {
return this.x * v.x + this.y * v.y + this.z * v.z;
}
/**
* 叉乘
* @param v
* @returns {Vector3}
*/
cross(v) {
let x = this.x;
let y = this.y;
let z = this.z;
this.x = y * v.z - z * v.y;
this.y = z * v.x - x * v.z;
this.z = x * v.y - this.y * v.x;
return this;
}
crossVectors(a, b) {
let ax = a.x, ay = a.y, az = a.z;
let bx = b.x, by = b.y, bz = b.z;
this.x = ay * bz - az * by;
this.y = az * bx - ax * bz;
this.z = ax * by - ay * bx;
return this;
}
/**
* 将当前向量乘以一个3x3的矩阵
* @param m
* @returns {Vector3}
*/
applyMatrix3(m) {
let x = this.x, y = this.y, z = this.z;
let e = m.elements;
this.x = e[0] * x + e[3] * y + e[6] * z;
this.y = e[1] * x + e[4] * y + e[7] * z;
this.z = e[2] * x + e[5] * y + e[8] * z;
return this;
}
/**
* 将当前向量乘以一个4x3的矩阵
* @param m
* @returns {Vector3}
*/
applyMatrix4(m) {
let x = this.x, y = this.y, z = this.z;
let e = m.elements;
let w = 1 / (e[3] * x + e[7] * y + e[11] * z + e[15]);
this.x = (e[0] * x + e[4] * y + e[8] * z + e[12]) * w;
this.y = (e[1] * x + e[5] * y + e[9] * z + e[13]) * w;
this.z = (e[2] * x + e[6] * y + e[10] * z + e[14]) * w;
return this;
}
/**
* 从矩阵中获取位置向量(原getFromMatrixPosition方法)
* @param m
* @returns {Vector3}
*/
setFromMatrixPosition(m) {
let e = m.elements;
this.x = e[12];
this.y = e[13];
this.z = e[14];
return this;
}
/**
* 从矩阵中获取缩放向量
* @param m
* @returns {Vector3}
*/
setFromMatrixScale(m) {
let sx = this.setFromMatrixColumn(m, 0).length();
let sy = this.setFromMatrixColumn(m, 1).length();
let sz = this.setFromMatrixColumn(m, 2).length();
this.x = sx;
this.y = sy;
this.z = sz;
return this;
}
setFromMatrixColumn(m, index) {
return this.fromArray(m.elements, index * 4);
}
min(v) {
this.x = Math.min(this.x, v.x);
this.y = Math.min(this.y, v.y);
this.z = Math.min(this.z, v.z);
return this;
}
max(v) {
this.x = Math.max(this.x, v.x);
this.y = Math.max(this.y, v.y);
this.z = Math.max(this.z, v.z);
return this;
}
equals(v) {
return ((v.x === this.x) && (v.y === this.y) && (v.z === this.z));
}
fromArray(array, offset) {
if (offset === undefined) offset = 0;
this.x = array[offset];
this.y = array[offset + 1];
this.z = array[offset + 2];
return this;
}
toArray(array, offset) {
if (array === undefined) array = [];
if (offset === undefined) offset = 0;
array[offset] = this.x;
array[offset + 1] = this.y;
array[offset + 2] = this.z;
return array;
}
}Euler
class Euler {
constructor(x = 0, y = 0, z = 0, order = Euler.DefaultOrder) {
this.isEuler = true;
this._x = x;
this._y = y;
this._z = z;
this._order = order;
}
get x() {
return this._x;
}
set x(value) {
this._x = value;
this.onChangeCallback();
}
get y() {
return this._y;
}
set y(value) {
this._y = value;
this.onChangeCallback();
}
get z() {
return this._z;
}
set z(value) {
this._z = value;
this.onChangeCallback();
}
set(x, y, z, order) {
this._x = x;
this._y = y;
this._z = z;
this._order = order || this._order;
}
clone() {
return new this.constructor(this._x, this._y, this._z, this._order);
}
copy(euler) {
this._x = euler._x;
this._y = euler._y;
this._z = euler._z;
this._order = euler._order;
return this;
}
/**
* 通过Matrix4设置Euler
* @param m
* @param order
* @param update
* @returns {Euler}
*/
setFromRotationMatrix(m, order, update) {
let clamp = _Math.clamp;
// assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
let te = m.elements;
let m11 = te[0], m12 = te[4], m13 = te[8];
let m21 = te[1], m22 = te[5], m23 = te[9];
let m31 = te[2], m32 = te[6], m33 = te[10];
order = order || this._order;
if (order === 'XYZ') {
this._y = Math.asin(clamp(m13, -1, 1));
if (Math.abs(m13) < 0.99999) {
this._x = Math.atan2(-m23, m33);
this._z = Math.atan2(-m12, m11);
} else {
this._x = Math.atan2(m32, m22);
this._z = 0;
}
} else if (order === 'YXZ') {
this._x = Math.asin(-clamp(m23, -1, 1));
if (Math.abs(m23) < 0.99999) {
this._y = Math.atan2(m13, m33);
this._z = Math.atan2(m21, m22);
} else {
this._y = Math.atan2(-m31, m11);
this._z = 0;
}
} else if (order === 'ZXY') {
this._x = Math.asin(clamp(m32, -1, 1));
if (Math.abs(m32) < 0.99999) {
this._y = Math.atan2(-m31, m33);
this._z = Math.atan2(-m12, m22);
} else {
this._y = 0;
this._z = Math.atan2(m21, m11);
}
} else if (order === 'ZYX') {
this._y = Math.asin(-clamp(m31, -1, 1));
if (Math.abs(m31) < 0.99999) {
this._x = Math.atan2(m32, m33);
this._z = Math.atan2(m21, m11);
} else {
this._x = 0;
this._z = Math.atan2(-m12, m22);
}
} else if (order === 'YZX') {
this._z = Math.asin(clamp(m21, -1, 1));
if (Math.abs(m21) < 0.99999) {
this._x = Math.atan2(-m23, m22);
this._y = Math.atan2(-m31, m11);
} else {
this._x = 0;
this._y = Math.atan2(m13, m33);
}
} else if (order === 'XZY') {
this._z = Math.asin(-clamp(m12, -1, 1));
if (Math.abs(m12) < 0.99999) {
this._x = Math.atan2(m32, m22);
this._y = Math.atan2(m13, m11);
} else {
this._x = Math.atan2(-m23, m33);
this._y = 0;
}
} else {
console.warn('THREE.Euler: .setFromRotationMatrix() given unsupported order: ' + order);
}
this._order = order;
if (update !== false) this.onChangeCallback();
return this;
}
/**
* 通过Quaternion设置Euler
* @param q
* @param order
* @param update
* @returns {Quaternion}
*/
setFromQuaternion(q, order, update) {
matrix.makeRotationFromQuaternion(q);
return this.setFromRotationMatrix(matrix, order, update);
}
onChange(callback) {
this.onChangeCallback = callback;
return this;
}
onChangeCallback() {
}
}
Euler.RotationOrders = ['XYZ', 'YZX', 'ZXY', 'XZY', 'YXZ', 'ZYX'];
Euler.DefaultOrder = 'XYZ';Quaternion
class Quaternion {
constructor(x = 0, y = 0, z = 0, w = 1) {
this.isQuaternion = true;
this._x = x;
this._y = y;
this._z = z;
this._w = w;
}
get x() {
return this._x;
}
set x(value) {
this._x = value;
this.onChangeCallback();
}
get y() {
return this._y;
}
set y(value) {
this._y = value;
this.onChangeCallback();
}
get z() {
return this._z;
}
set z(value) {
this._z = value;
this.onChangeCallback();
}
get w() {
return this._w;
}
set w(value) {
this._w = value;
this.onChangeCallback();
}
set(x, y, z, w) {
this._x = x;
this._y = y;
this._z = z;
this._w = w;
return this;
}
clone() {
return new this.constructor(this._x, this._y, this._z, this._w);
}
copy(quaternion) {
this._x = quaternion._x;
this._y = quaternion._y;
this._z = quaternion._z;
this._w = quaternion._w;
return this;
}
/**
* 从欧拉角设置Quaternion
* @param euler
* @param update
* @returns {Quaternion}
*/
setFromEuler(euler, update) {
if (!(euler && euler.isEuler)) {
throw new Error('THREE.Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order.');
}
let x = euler._x, y = euler._y, z = euler._z, order = euler._order;
let cos = Math.cos;
let sin = Math.sin;
let c1 = cos(x / 2);
let c2 = cos(y / 2);
let c3 = cos(z / 2);
let s1 = sin(x / 2);
let s2 = sin(y / 2);
let s3 = sin(z / 2);
if (order === 'XYZ') {
this._x = s1 * c2 * c3 + c1 * s2 * s3;
this._y = c1 * s2 * c3 - s1 * c2 * s3;
this._z = c1 * c2 * s3 + s1 * s2 * c3;
this._w = c1 * c2 * c3 - s1 * s2 * s3;
} else if (order === 'YXZ') {
this._x = s1 * c2 * c3 + c1 * s2 * s3;
this._y = c1 * s2 * c3 - s1 * c2 * s3;
this._z = c1 * c2 * s3 - s1 * s2 * c3;
this._w = c1 * c2 * c3 + s1 * s2 * s3;
} else if (order === 'ZXY') {
this._x = s1 * c2 * c3 - c1 * s2 * s3;
this._y = c1 * s2 * c3 + s1 * c2 * s3;
this._z = c1 * c2 * s3 + s1 * s2 * c3;
this._w = c1 * c2 * c3 - s1 * s2 * s3;
} else if (order === 'ZYX') {
this._x = s1 * c2 * c3 - c1 * s2 * s3;
this._y = c1 * s2 * c3 + s1 * c2 * s3;
this._z = c1 * c2 * s3 - s1 * s2 * c3;
this._w = c1 * c2 * c3 + s1 * s2 * s3;
} else if (order === 'YZX') {
this._x = s1 * c2 * c3 + c1 * s2 * s3;
this._y = c1 * s2 * c3 + s1 * c2 * s3;
this._z = c1 * c2 * s3 - s1 * s2 * c3;
this._w = c1 * c2 * c3 - s1 * s2 * s3;
} else if (order === 'XZY') {
this._x = s1 * c2 * c3 - c1 * s2 * s3;
this._y = c1 * s2 * c3 - s1 * c2 * s3;
this._z = c1 * c2 * s3 + s1 * s2 * c3;
this._w = c1 * c2 * c3 + s1 * s2 * s3;
}
if (update !== false) this.onChangeCallback();
return this;
}
/**
* 从轴和角度设置Quaternion
* @param axis
* @param angle
* @returns {Quaternion}
*/
setFromAxisAngle(axis, angle) {
let halfAngle = angle / 2, s = Math.sin(halfAngle);
this._x = axis._x * s;
this._y = axis._y * s;
this._z = axis._z * s;
this._w = Math.cos(halfAngle);
this.onChangeCallback();
return this;
}
/**
* 从Matrix4设置Quaternion
* @param m
* @returns {Quaternion}
*/
setFromRotationMatrix(m) {
let te = m.elements,
m11 = te[0], m12 = te[4], m13 = te[8],
m21 = te[1], m22 = te[5], m23 = te[9],
m31 = te[2], m32 = te[6], m33 = te[10],
trace = m11 + m22 + m33, s;
if (trace > 0) {
s = 0.5 / Math.sqrt(trace + 1.0);
this._w = 0.25 / s;
this._x = (m32 - m23) * s;
this._y = (m13 - m31) * s;
this._z = (m21 - m12) * s;
} else if (m11 > m22 && m11 > m33) {
s = 2.0 * Math.sqrt(1.0 + m11 - m22 - m33);
this._w = (m32 - m23) / s;
this._x = 0.25 * s;
this._y = (m12 + m21) / s;
this._z = (m13 + m31) / s;
} else if (m22 > m33) {
s = 2.0 * Math.sqrt(1.0 + m22 - m11 - m33);
this._w = (m13 - m31) / s;
this._x = (m12 + m21) / s;
this._y = 0.25 * s;
this._z = (m23 + m32) / s;
} else {
s = 2.0 * Math.sqrt(1.0 + m33 - m11 - m22);
this._w = (m21 - m12) / s;
this._x = (m13 + m31) / s;
this._y = (m23 + m32) / s;
this._z = 0.25 * s;
}
this.onChangeCallback();
return this;
}
/**
* 左乘四元素
* @param q
* @returns {Quaternion}
*/
multiply(q) {
return this.multiplyQuaternions(this, q);
}
/**
* 右乘四元素
* @param q
* @returns {*}
*/
premultiply(q) {
return this.multiplyQuaternions(q, this);
}
/**
* 两个四元素相乘
* @param a
* @param b
* @returns {Quaternion}
*/
multiplyQuaternions(a, b) {
let qax = a._x, qay = a._y, qaz = a._z, qaw = a._w;
let qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w;
this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby;
this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz;
this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx;
this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz;
this.onChangeCallback();
return this;
}
onChange(callback) {
this.onChangeCallback = callback;
return this;
}
onChangeCallback() {
}
}Matrix4
/**
* 4*4矩阵原理可以参考这篇文章:http://blog.vr-seesee.com/detail/185
* 矩阵是用于表示变换而不是坐标,4*4矩阵的核心是变换:平移、旋转、缩放
*/
class Matrix4 {
constructor() {
this.elements = [
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
];
}
set(n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44) {
let te = this.elements;
te[0] = n11;te[4] = n12;te[8] = n13;te[12] = n14;
te[1] = n21;te[5] = n22;te[9] = n23;te[13] = n24;
te[2] = n31;te[6] = n32;te[10] = n33;te[14] = n34;
te[3] = n41;te[7] = n42;te[11] = n43;te[15] = n44;
return this;
}
identity() {
this.set(
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
);
return this;
}
clone() {
return new this.constructor(this.elements);
}
copy(m) {
let te = this.elements;
let me = m.elements;
te[0] = me[0]; te[1] = me[1]; te[2] = me[2]; te[3] = me[3];
te[4] = me[4]; te[5] = me[5]; te[6] = me[6]; te[7] = me[7];
te[8] = me[8]; te[9] = me[9]; te[10] = me[10];te[11] = me[11];
te[12] = me[12];te[13] = me[13];te[14] = me[14];te[15] = me[15];
return this;
}
// 平移
makeTranslation(x, y, z) {
this.set(
1, 0, 0, x,
0, 1, 0, y,
0, 0, 1, z,
0, 0, 0, 1
);
return this;
}
// 绕X轴旋转
makeRotationX(theta) {
let c = Math.cos(theta), s = Math.sin(theta);
this.set(
1, 0, 0, 0,
0, c, -s, 0,
0, s, c, 0,
0, 0, 0, 1
);
return this;
}
makeRotationY(theta) {
let c = Math.cos(theta), s = Math.sin(theta);
this.set(
c, 0, s, 0,
0, 1, 0, 0,
-s, 0, c, 0,
0, 0, 0, 1
);
return this;
}
makeRotationZ(theta) {
let c = Math.cos(theta), s = Math.sin(theta);
this.set(
c, -s, 0, 0,
s, c, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
);
return this;
}
makeRotationAxis(axis, angle) {
let c = Math.cos(angle);
let s = Math.sin(angle);
let t = 1 - c;
let x = axis.x, y = axis.y, z = axis.z;
let tx = t * x, ty = t * y;
this.set(
tx * x + c, tx * y - s * z, tx * z + s * y, 0,
tx * y + s * z, ty * y + c, ty * z - s * x, 0,
tx * z - s * y, ty * z + s * x, t * z * z + c, 0,
0, 0, 0, 1
);
return this;
}
// 缩放
makeScale(x, y, z) {
this.set(
x, 0, 0, 0,
0, y, 0, 0,
0, 0, z, 0,
0, 0, 0, 1
);
return this;
}
}