2016-04-15 15 views
5

Sto cercando di ottenere il cambio di orientamento tra due eventi deviceorientation lungo l'asse sinistra-destra e l'asse in alto-basso, solitamente definito come il telefono x e y asse (https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Orientation_and_motion_data_explained)html5 - Ottieni rotazione dell'orientamento del dispositivo in coordinata relativa

cioè tra istanti t1 e t2 in cui tali assi si spostano da telefono (x1, y1) a (x2, y2), si vorrebbe ottenere (angle(x2-x1), angle(y1-y2)).

Quando il dispositivo è in modalità verticale (in opposizione alla modalità orizzontale), tali assi sembrano corrispondere a beta e gamma. Tuttavia quando il telefono è verticale (parte inferiore rivolta verso il suolo), il valore gamma diventa estremamente instabile, e salta da 90 a -90 gradi (alla stessa occasione, l'alfa salti di 180 gradi) Si può facilmente vedere che here on your phone

Vorrei evitarlo e ottenere anche valori nell'intervallo 360. Ecco quello che ho finora:

// assuming portrait mode 
var beta0, gamma0; 
window.addEventListener('deviceorientation', function(orientation) { 
    if (typeof beta0 === 'undefined') { 
    beta0 = beta; 
    gamma0 = gamma; 
    } 

    console.log('user has moved to the left by', gamma - gamma0, ' and to the top by', beta - beta0); 
}); 

che funziona bene quando il dispositivo è in gran parte orizzontale, e non a tutti quando si è verticale

risposta

3

Va bene. In primo luogo, una semplice spiegazione dell'ingresso orientamento del dispositivo:

Il sistema di coordinate assoluto, (X, Y, Z) è tale che X è East, Y è Nord e Z è in alto. Il sistema di coordinate relative del dispositivo, (x, y, z) è tale che x è corretto, è in alto e z è attivo. Poi gli angoli di orientamento, (alpha, beta, gamma) sono gli angoli che descrivono la successione di tre semplici rotazioni che cambiano (X, Y, Z) a (x, y, z) come così:

  • ruotano intorno Z da alpha gradi, che trasforma (X, Y, Z) a (X', Y', Z') con Z' = Z
  • ruota intorno a X' di beta gradi, che trasforma (X', Y', Z') a (X'', Y'', Z'') con X'' = X'
  • ruotare intorno Y'' da gamma gradi, che trasforma (X'', Y'', Z'') a (x, y, z) con y = Y''

(vengono chiamati intrinseche angoli Tait-Bryan di tipo Z-X'-Y'')

Ora possiamo ottenere la matrice di rotazione corrispondente componendo semplice matrice di rotazione che corrisponde ciascuna a una delle tre rotazioni.

        [ 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 ] 

dove A, B, C sono brevi per alpha, beta, gamma e s, c per sin, cos.

Ora, ci interessa angoli di destra-sinistra (y asse) e top-down (x asse) rotazioni delta tra due posizioni (x, y, z) e (x', y', z') che corrispondono agli orientamenti (A, B, C) e (A', B', C')

L' coordinate di (x', y', z') in termini di (x, y, z) sono date da R(A', B', C') * R(A, B, C)^-1 = R(A', B', C') * R(A, B, C)^T poiché l'inversa è la trasposizione per matrice ortogonale (rotazione). Infine, se z' = p*x + q*y + r*z, l'angolo di quelle rotazioni è p attorno all'asse destra-sinistra e q attorno a quello in alto (questo è vero per piccoli angoli, che presuppongono l'aggiornamento frequente dell'orientamento, altrimenti asin(p) e asin(r) sono più vicini alla verità)

ecco alcuni javascript per ottenere la matrice di rotazione:

/* 
* gl-matrix is a nice library that handles rotation stuff efficiently 
* The 3x3 matrix is a 9 element array 
* such that indexes 0-2 correspond to the first column, 3-5 to the second column and 6-8 to the third 
*/ 
import {mat3} from 'gl-matrix'; 

let _x, _y, _z; 
let cX, cY, cZ, sX, sY, sZ; 
/* 
* return the rotation matrix corresponding to the orientation angles 
*/ 
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, // row 1, col 1 
    out[1] = cX * sZ,     // row 2, col 1 
    out[2] = - cZ * sY + sZ * sX * cY , // row 3, col 1 

    out[3] = - cY * sZ + cZ * sX * sY, // row 1, col 2 
    out[4] = cZ * cX,     // row 2, col 2 
    out[5] = sZ * sY + cZ * cY * sX, // row 3, col 2 

    out[6] = cX * sY,     // row 1, col 3 
    out[7] = - sX,      // row 2, col 3 
    out[8] = cX * cY     // row 3, col 3 
}; 

e ora otteniamo i delta angolari:

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; 
} 

Infine, per tenere conto di orientamento dello schermo, dobbiamo sostituire

_z = alpha; 
    _x = beta; 
    _y = gamma; 

da

const getScreenOrientation =() => { 
    switch (window.screen.orientation || window.screen.mozOrientation) { 
    case 'landscape-primary': 
     return 90; 
    case 'landscape-secondary': 
     return -90; 
    case 'portrait-secondary': 
     return 180; 
    case 'portrait-primary': 
     return 0; 
    } 
    if (window.orientation !== undefined) 
    return window.orientation; 
}; 

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; 
} 

noti che gli angoli di destra-sinistra e dall'alto verso il basso cumulative dipenderà del percorso scelto dall'utente, e non può essere dedotto direttamente dall'orientamento del dispositivo, ma deve essere tracciato attraverso il movimento. Si può arrivare alla stessa posizione con diversi movimenti:

  • Metodo 1:

    • tenere il telefono in orizzontale e ruotare di 90 gradi in senso orario. (questa non è né una rotazione sinistra-destra né una rotazione dall'alto in basso)
    • mantieni il telefono in modalità orizzontale e ruotalo di 90 verso di te. (questa non è una rotazione sinistra-destra di 90 gradi)
    • mantieni il telefono di fronte a te e ruota di 90 in modo che sia attivo. (Questo non è né a 90 gradi di rotazione sinistra-destra)
  • Metodo 2:

    • ruotare il telefono di 90 gradi in modo che esso si affronta e verticale (questo è a 90 gradi top- rotazione in basso)
+0

ho provato attuazione di questo approccio - vedere https://stackoverflow.com/questions/46975452/adjusting-mobile-accelerometer-data-to-account-for-phone-rotation - I sto diventando diverso risultati mentre accendo il telefono. Puoi commentare per vedere se mi manca qualcosa? grazie – user1361529

Problemi correlati