Excellent. First, a simple explanation of the input orientation device:
The absolute coordinate system (X, Y, Z) is such that X is east, Y is north, and Z is up. The relative coordinate system of the device (x, y, z) is such that x on the right, y on the top, and z up. Then the orientation angles (alpha, beta, gamma) are the angles that describe the sequence of three simple rotations that change (X, Y, Z) to (x, y, z) as follows:
- rotate around
Z by alpha degree, which converts (X, Y, Z) to (X', Y', Z') with Z' = Z - rotate around
X' by beta degrees, which turns (X', Y', Z') into (X'', Y'', Z'') with X'' = X' - rotate around
Y'' by gamma degrees that convert (X'', Y'', Z'') to (x, y, z) with y = Y''
(they are called Tate-Brian inner angles of type Z-X'-Y'' )
Now we can get the corresponding rotation matrix by composing a simple rotation matrix, each of which corresponds to one of the three rotations.
[ cC 0 sC ] [ 1 0 0 ] [ cA -sA 0 ] R(A, B, C) = Ry(C)*Rx(B)*Rz(A) = | 0 1 0 |*| 0 cB -sB |*[ sA cA 0 ] [ -sC 0 cC ] [ 0 sB cB ] [ 0 0 1 ]
where A, B, C is the abbreviation for alpha, beta, gamma and s, c sin, cos .
Now we are interested in the angles of rotation from left to right ( y axis) and from top to bottom ( x axis) between two positions (x, y, z) and (x', y', z') which correspond in orientation (A, B, C) and (A', B', C')
The coordinates (x', y', z') in terms of (x, y, z) are given as R(A', B', C') * R(A, B, C)^-1 = R(A', B', C') * R(A, B, C)^T since the converse is the transposition for the orthogonal (rotating) matrix. Finally, if z' = p*x + q*y + r*z , the angle of these rotations is equal to p around the right-left axis and q around the downward axis (this is true for small angles that require frequent orientation updates, otherwise asin(p) and asin(r) closer to the truth)
So here is some JavaScript to get the rotation matrix:
import {mat3} from 'gl-matrix'; let _x, _y, _z; let cX, cY, cZ, sX, sY, sZ; const fromOrientation = function(out, alpha, beta, gamma) { _z = alpha; _x = beta; _y = gamma; cX = Math.cos( _x ); cY = Math.cos( _y ); cZ = Math.cos( _z ); sX = Math.sin( _x ); sY = Math.sin( _y ); sZ = Math.sin( _z ); out[0] = cZ * cY + sZ * sX * sY,
and now we get the angular deltas:
const deg2rad = Math.PI / 180; // Degree-to-Radian conversion let currentRotMat, previousRotMat, inverseMat, relativeRotationDelta, totalRightAngularMovement=0, totalTopAngularMovement=0; window.addEventListener('deviceorientation', ({alpha, beta, gamma}) => { // init values if necessary if (!previousRotMat) { previousRotMat = mat3.create(); currentRotMat = mat3.create(); relativeRotationDelta = mat3.create(); fromOrientation(currentRotMat, alpha * deg2rad, beta * deg2rad, gamma * deg2rad); } // save last orientation mat3.copy(previousRotMat, currentRotMat); // get rotation in the previous orientation coordinate fromOrientation(currentRotMat, alpha * deg2rad, beta * deg2rad, gamma * deg2rad); mat3.transpose(inverseMat, previousRotMat); // for rotation matrix, inverse is transpose mat3.multiply(relativeRotationDelta, currentRotMat, inverseMat); // add the angular deltas to the cummulative rotation totalRightAngularMovement += Math.asin(relativeRotationDelta[6]) / deg2rad; totalTopAngularMovement += Math.asin(relativeRotationDelta[7]) / deg2rad; }
Finally, in order to take into account the orientation of the screen, we must replace
_z = alpha; _x = beta; _y = gamma;
from
const screen = window.screen; const getScreenOrientation = () => { const oriented = screen && (screen.orientation || screen.mozOrientation); if (oriented) switch (oriented.type || oriented) { case 'landscape-primary': return 90; case 'landscape-secondary': return -90; case 'portrait-secondary': return 180; case 'portrait-primary': return 0; } return window.orientation|0; // defaults to zero if orientation is unsupported }; const screenOrientation = getScreenOrientation(); _z = alpha; if (screenOrientation === 90) { _x = - gamma; _y = beta; } else if (screenOrientation === -90) { _x = gamma; _y = - beta; } else if (screenOrientation === 180) { _x = - beta; _y = - gamma; } else if (screenOrientation === 0) { _x = beta; _y = gamma; }
Please note that the aggregate right left and upper lower corners will depend on the path chosen by the user and cannot be derived directly from the orientation of the device, but should be tracked by movement. You can arrive at the same position with different movements:
method 1:
- Hold the phone horizontally and rotate it 90 degrees clockwise. (this is not rotation left-right and not from top to bottom)
- Hold your phone in landscape mode and rotate it 90 degrees to you. (this is not a 90 degree turn left-right)
- Hold the phone facing you and rotate it 90 degrees. (this is not a 90 degree turn left-right)
method 2:
- rotate the phone 90 degrees so that it faces you and is upright (this is a 90 degree turn from top to bottom)