webxr-test-math-helper.js (9410B)
1 'use strict'; 2 3 // Math helper - used mainly in hit test implementation done by webxr-test.js 4 class XRMathHelper { 5 static toString(p) { 6 return "[" + p.x + "," + p.y + "," + p.z + "," + p.w + "]"; 7 } 8 9 static transform_by_matrix(matrix, point) { 10 return { 11 x : matrix[0] * point.x + matrix[4] * point.y + matrix[8] * point.z + matrix[12] * point.w, 12 y : matrix[1] * point.x + matrix[5] * point.y + matrix[9] * point.z + matrix[13] * point.w, 13 z : matrix[2] * point.x + matrix[6] * point.y + matrix[10] * point.z + matrix[14] * point.w, 14 w : matrix[3] * point.x + matrix[7] * point.y + matrix[11] * point.z + matrix[15] * point.w, 15 }; 16 } 17 18 static neg(p) { 19 return {x : -p.x, y : -p.y, z : -p.z, w : p.w}; 20 } 21 22 static sub(lhs, rhs) { 23 // .w is treated here like an entity type, 1 signifies points, 0 signifies vectors. 24 // point - point, point - vector, vector - vector are ok, vector - point is not. 25 if (lhs.w != rhs.w && lhs.w == 0.0) { 26 throw new Error("vector - point not allowed: " + toString(lhs) + "-" + toString(rhs)); 27 } 28 29 return {x : lhs.x - rhs.x, y : lhs.y - rhs.y, z : lhs.z - rhs.z, w : lhs.w - rhs.w}; 30 } 31 32 static add(lhs, rhs) { 33 if (lhs.w == rhs.w && lhs.w == 1.0) { 34 throw new Error("point + point not allowed", p1, p2); 35 } 36 37 return {x : lhs.x + rhs.x, y : lhs.y + rhs.y, z : lhs.z + rhs.z, w : lhs.w + rhs.w}; 38 } 39 40 static cross(lhs, rhs) { 41 if (lhs.w != 0.0 || rhs.w != 0.0) { 42 throw new Error("cross product not allowed: " + toString(lhs) + "x" + toString(rhs)); 43 } 44 45 return { 46 x : lhs.y * rhs.z - lhs.z * rhs.y, 47 y : lhs.z * rhs.x - lhs.x * rhs.z, 48 z : lhs.x * rhs.y - lhs.y * rhs.x, 49 w : 0 50 }; 51 } 52 53 static dot(lhs, rhs) { 54 if (lhs.w != 0 || rhs.w != 0) { 55 throw new Error("dot product not allowed: " + toString(lhs) + "x" + toString(rhs)); 56 } 57 58 return lhs.x * rhs.x + lhs.y * rhs.y + lhs.z * rhs.z; 59 } 60 61 static mul(scalar, vector) { 62 if (vector.w != 0) { 63 throw new Error("scalar * vector not allowed", scalar, vector); 64 } 65 66 return {x : vector.x * scalar, y : vector.y * scalar, z : vector.z * scalar, w : vector.w}; 67 } 68 69 static length(vector) { 70 return Math.sqrt(XRMathHelper.dot(vector, vector)); 71 } 72 73 static normalize(vector) { 74 const l = XRMathHelper.length(vector); 75 return XRMathHelper.mul(1.0/l, vector); 76 } 77 78 // All |face|'s points and |point| must be co-planar. 79 static pointInFace(point, face) { 80 const normalize = XRMathHelper.normalize; 81 const sub = XRMathHelper.sub; 82 const length = XRMathHelper.length; 83 const cross = XRMathHelper.cross; 84 85 let onTheRight = null; 86 let previous_point = face[face.length - 1]; 87 88 // |point| is in |face| if it's on the same side of all the edges. 89 for (let i = 0; i < face.length; ++i) { 90 const current_point = face[i]; 91 92 const edge_direction = normalize(sub(current_point, previous_point)); 93 const turn_direction = normalize(sub(point, current_point)); 94 95 const sin_turn_angle = length(cross(edge_direction, turn_direction)); 96 97 if (onTheRight == null) { 98 onTheRight = sin_turn_angle >= 0; 99 } else { 100 if (onTheRight && sin_turn_angle < 0) return false; 101 if (!onTheRight && sin_turn_angle > 0) return false; 102 } 103 104 previous_point = current_point; 105 } 106 107 return true; 108 } 109 110 static det2x2(m00, m01, m10, m11) { 111 return m00 * m11 - m01 * m10; 112 } 113 114 static det3x3( 115 m00, m01, m02, 116 m10, m11, m12, 117 m20, m21, m22 118 ){ 119 const det2x2 = XRMathHelper.det2x2; 120 121 return m00 * det2x2(m11, m12, m21, m22) 122 - m01 * det2x2(m10, m12, m20, m22) 123 + m02 * det2x2(m10, m11, m20, m21); 124 } 125 126 static det4x4( 127 m00, m01, m02, m03, 128 m10, m11, m12, m13, 129 m20, m21, m22, m23, 130 m30, m31, m32, m33 131 ) { 132 const det3x3 = XRMathHelper.det3x3; 133 134 return m00 * det3x3(m11, m12, m13, 135 m21, m22, m23, 136 m31, m32, m33) 137 - m01 * det3x3(m10, m12, m13, 138 m20, m22, m23, 139 m30, m32, m33) 140 + m02 * det3x3(m10, m11, m13, 141 m20, m21, m23, 142 m30, m31, m33) 143 - m03 * det3x3(m10, m11, m12, 144 m20, m21, m22, 145 m30, m31, m32); 146 } 147 148 static inv2(m) { 149 // mij - i-th column, j-th row 150 const m00 = m[0], m01 = m[1], m02 = m[2], m03 = m[3]; 151 const m10 = m[4], m11 = m[5], m12 = m[6], m13 = m[7]; 152 const m20 = m[8], m21 = m[9], m22 = m[10], m23 = m[11]; 153 const m30 = m[12], m31 = m[13], m32 = m[14], m33 = m[15]; 154 155 const det = det4x4( 156 m00, m01, m02, m03, 157 m10, m11, m12, m13, 158 m20, m21, m22, m23, 159 m30, m31, m32, m33 160 ); 161 } 162 163 static transpose(m) { 164 const result = Array(16); 165 for (let i = 0; i < 4; i++) { 166 for (let j = 0; j < 4; j++) { 167 result[i * 4 + j] = m[j * 4 + i]; 168 } 169 } 170 return result; 171 } 172 173 // Inverts the matrix, ported from transformation_matrix.cc. 174 static inverse(m) { 175 const det3x3 = XRMathHelper.det3x3; 176 177 // mij - i-th column, j-th row 178 const m00 = m[0], m01 = m[1], m02 = m[2], m03 = m[3]; 179 const m10 = m[4], m11 = m[5], m12 = m[6], m13 = m[7]; 180 const m20 = m[8], m21 = m[9], m22 = m[10], m23 = m[11]; 181 const m30 = m[12], m31 = m[13], m32 = m[14], m33 = m[15]; 182 183 const det = XRMathHelper.det4x4( 184 m00, m01, m02, m03, 185 m10, m11, m12, m13, 186 m20, m21, m22, m23, 187 m30, m31, m32, m33 188 ); 189 190 if (Math.abs(det) < 0.0001) { 191 return null; 192 } 193 194 const invDet = 1.0 / det; 195 // Calculate `comatrix * 1/det`: 196 const result2 = [ 197 // First column (m0r): 198 invDet * det3x3(m11, m12, m13, m21, m22, m23, m32, m32, m33), 199 -invDet * det3x3(m10, m12, m13, m20, m22, m23, m30, m32, m33), 200 invDet * det3x3(m10, m11, m13, m20, m21, m23, m30, m31, m33), 201 -invDet * det3x3(m10, m11, m12, m20, m21, m22, m30, m31, m32), 202 // Second column (m1r): 203 -invDet * det3x3(m01, m02, m03, m21, m22, m23, m32, m32, m33), 204 invDet * det3x3(m00, m02, m03, m20, m22, m23, m30, m32, m33), 205 -invDet * det3x3(m00, m01, m03, m20, m21, m23, m30, m31, m33), 206 invDet * det3x3(m00, m01, m02, m20, m21, m22, m30, m31, m32), 207 // Third column (m2r): 208 invDet * det3x3(m01, m02, m03, m11, m12, m13, m31, m32, m33), 209 -invDet * det3x3(m00, m02, m03, m10, m12, m13, m30, m32, m33), 210 invDet * det3x3(m00, m01, m03, m10, m11, m13, m30, m31, m33), 211 -invDet * det3x3(m00, m01, m02, m10, m11, m12, m30, m31, m32), 212 // Fourth column (m3r): 213 -invDet * det3x3(m01, m02, m03, m11, m12, m13, m21, m22, m23), 214 invDet * det3x3(m00, m02, m03, m10, m12, m13, m20, m22, m23), 215 -invDet * det3x3(m00, m01, m03, m10, m11, m13, m20, m21, m23), 216 invDet * det3x3(m00, m01, m02, m10, m11, m12, m20, m21, m22), 217 ]; 218 219 // Actual inverse is `1/det * transposed(comatrix)`: 220 return XRMathHelper.transpose(result2); 221 } 222 223 static mul4x4(m1, m2) { 224 if (m1 == null || m2 == null) { 225 return null; 226 } 227 228 const result = Array(16); 229 230 for (let row = 0; row < 4; row++) { 231 for (let col = 0; col < 4; col++) { 232 result[4 * col + row] = 0; 233 for(let i = 0; i < 4; i++) { 234 result[4 * col + row] += m1[4 * i + row] * m2[4 * col + i]; 235 } 236 } 237 } 238 239 return result; 240 } 241 242 // Decomposes a matrix, with the assumption that the passed in matrix is 243 // a rigid transformation (i.e. position and rotation *only*!). 244 // The result is an object with `position` and `orientation` keys, which should 245 // be compatible with FakeXRRigidTransformInit. 246 // The implementation should match the behavior of gfx::Transform, but assumes 247 // that scale, skew & perspective are not present in the matrix so it could be 248 // simplified. 249 static decomposeRigidTransform(m) { 250 const m00 = m[0], m01 = m[1], m02 = m[2], m03 = m[3]; 251 const m10 = m[4], m11 = m[5], m12 = m[6], m13 = m[7]; 252 const m20 = m[8], m21 = m[9], m22 = m[10], m23 = m[11]; 253 const m30 = m[12], m31 = m[13], m32 = m[14], m33 = m[15]; 254 255 const position = [m30, m31, m32]; 256 const orientation = [0, 0, 0, 0]; 257 258 const trace = m00 + m11 + m22; 259 if (trace > 0) { 260 const S = Math.sqrt(trace + 1) * 2; 261 orientation[3] = 0.25 * S; 262 orientation[0] = (m12 - m21) / S; 263 orientation[1] = (m20 - m02) / S; 264 orientation[2] = (m01 - m10) / S; 265 } else if (m00 > m11 && m00 > m22) { 266 const S = Math.sqrt(1.0 + m00 - m11 - m22) * 2; 267 orientation[3] = (m12 - m21) / S; 268 orientation[0] = 0.25 * S; 269 orientation[1] = (m01 + m10) / S; 270 orientation[2] = (m20 + m02) / S; 271 } else if (m11 > m22) { 272 const S = Math.sqrt(1.0 + m11 - m00 - m22) * 2; 273 orientation[3] = (m20 - m02) / S; 274 orientation[0] = (m01 + m10) / S; 275 orientation[1] = 0.25 * S; 276 orientation[2] = (m12 + m21) / S; 277 } else { 278 const S = Math.sqrt(1.0 + m22 - m00 - m11) * 2; 279 orientation[3] = (m01 - m10) / S; 280 orientation[0] = (m20 + m02) / S; 281 orientation[1] = (m12 + m21) / S; 282 orientation[2] = 0.25 * S; 283 } 284 285 return { position, orientation }; 286 } 287 288 static identity() { 289 return [ 290 1, 0, 0, 0, 291 0, 1, 0, 0, 292 0, 0, 1, 0, 293 0, 0, 0, 1 294 ]; 295 }; 296 } 297 298 XRMathHelper.EPSILON = 0.001;