package com.nbs.client.assassins.sensors;
public class SensorManagerStub implements Spatial {
private static final float[] mTempMatrix = new float[16];
public SensorManagerStub(){}
/**
* Computes the inclination matrix <b>I</b> as well as the rotation
* matrix <b>R</b> transforming a vector from the
* device coordinate system to the world's coordinate system which is
* defined as a direct orthonormal basis, where:
*
* <li>X is defined as the vector product <b>Y.Z</b> (It is tangential to
* the ground at the device's current location and roughly points East).</li>
* <li>Y is tangential to the ground at the device's current location and
* points towards the magnetic North Pole.</li>
* <li>Z points towards the sky and is perpendicular to the ground.</li>
* <p>
* <hr>
* <p>By definition:
* <p>[0 0 g] = <b>R</b> * <b>gravity</b> (g = magnitude of gravity)
* <p>[0 m 0] = <b>I</b> * <b>R</b> * <b>geomagnetic</b>
* (m = magnitude of geomagnetic field)
* <p><b>R</b> is the identity matrix when the device is aligned with the
* world's coordinate system, that is, when the device's X axis points
* toward East, the Y axis points to the North Pole and the device is facing
* the sky.
*
* <p><b>I</b> is a rotation matrix transforming the geomagnetic
* vector into the same coordinate space as gravity (the world's coordinate
* space). <b>I</b> is a simple rotation around the X axis.
* The inclination angle in radians can be computed with
* {@link #getInclination}.
* <hr>
*
* <p> Each matrix is returned either as a 3x3 or 4x4 row-major matrix
* depending on the length of the passed array:
* <p><u>If the array length is 16:</u>
* <pre>
* / M[ 0] M[ 1] M[ 2] M[ 3] \
* | M[ 4] M[ 5] M[ 6] M[ 7] |
* | M[ 8] M[ 9] M[10] M[11] |
* \ M[12] M[13] M[14] M[15] /
*</pre>
* This matrix is ready to be used by OpenGL ES's
* {@link javax.microedition.khronos.opengles.GL10#glLoadMatrixf(float[], int)
* glLoadMatrixf(float[], int)}.
* <p>Note that because OpenGL matrices are column-major matrices you must
* transpose the matrix before using it. However, since the matrix is a
* rotation matrix, its transpose is also its inverse, conveniently, it is
* often the inverse of the rotation that is needed for rendering; it can
* therefore be used with OpenGL ES directly.
* <p>
* Also note that the returned matrices always have this form:
* <pre>
* / M[ 0] M[ 1] M[ 2] 0 \
* | M[ 4] M[ 5] M[ 6] 0 |
* | M[ 8] M[ 9] M[10] 0 |
* \ 0 0 0 1 /
*</pre>
* <p><u>If the array length is 9:</u>
* <pre>
* / M[ 0] M[ 1] M[ 2] \
* | M[ 3] M[ 4] M[ 5] |
* \ M[ 6] M[ 7] M[ 8] /
*</pre>
*
* <hr>
* <p>The inverse of each matrix can be computed easily by taking its
* transpose.
*
* <p>The matrices returned by this function are meaningful only when the
* device is not free-falling and it is not close to the magnetic north.
* If the device is accelerating, or placed into a strong magnetic field,
* the returned matrices may be inaccurate.
*
* @param R is an array of 9 floats holding the rotation matrix <b>R</b>
* when this function returns. R can be null.<p>
* @param I is an array of 9 floats holding the rotation matrix <b>I</b>
* when this function returns. I can be null.<p>
* @param gravity is an array of 3 floats containing the gravity vector
* expressed in the device's coordinate. You can simply use the
* {@link android.hardware.SensorEvent#values values}
* returned by a {@link android.hardware.SensorEvent SensorEvent} of a
* {@link android.hardware.Sensor Sensor} of type
* {@link android.hardware.Sensor#TYPE_ACCELEROMETER TYPE_ACCELEROMETER}.<p>
* @param geomagnetic is an array of 3 floats containing the geomagnetic
* vector expressed in the device's coordinate. You can simply use the
* {@link android.hardware.SensorEvent#values values}
* returned by a {@link android.hardware.SensorEvent SensorEvent} of a
* {@link android.hardware.Sensor Sensor} of type
* {@link android.hardware.Sensor#TYPE_MAGNETIC_FIELD TYPE_MAGNETIC_FIELD}.
* @return
* true on success<p>
* false on failure (for instance, if the device is in free fall).
* On failure the output matrices are not modified.
*/
@Override
public boolean getRotationMatrix(float[] R, float[] I,
float[] gravity, float[] geomagnetic) {
// TODO: move this to native code for efficiency
float Ax = gravity[0];
float Ay = gravity[1];
float Az = gravity[2];
final float Ex = geomagnetic[0];
final float Ey = geomagnetic[1];
final float Ez = geomagnetic[2];
float Hx = Ey*Az - Ez*Ay;
float Hy = Ez*Ax - Ex*Az;
float Hz = Ex*Ay - Ey*Ax;
final float normH = (float)Math.sqrt(Hx*Hx + Hy*Hy + Hz*Hz);
if (normH < 0.1f) {
// device is close to free fall (or in space?), or close to
// magnetic north pole. Typical values are > 100.
return false;
}
final float invH = 1.0f / normH;
Hx *= invH;
Hy *= invH;
Hz *= invH;
final float invA = 1.0f / (float)Math.sqrt(Ax*Ax + Ay*Ay + Az*Az);
Ax *= invA;
Ay *= invA;
Az *= invA;
final float Mx = Ay*Hz - Az*Hy;
final float My = Az*Hx - Ax*Hz;
final float Mz = Ax*Hy - Ay*Hx;
if (R != null) {
if (R.length == 9) {
R[0] = Hx; R[1] = Hy; R[2] = Hz;
R[3] = Mx; R[4] = My; R[5] = Mz;
R[6] = Ax; R[7] = Ay; R[8] = Az;
} else if (R.length == 16) {
R[0] = Hx; R[1] = Hy; R[2] = Hz; R[3] = 0;
R[4] = Mx; R[5] = My; R[6] = Mz; R[7] = 0;
R[8] = Ax; R[9] = Ay; R[10] = Az; R[11] = 0;
R[12] = 0; R[13] = 0; R[14] = 0; R[15] = 1;
}
}
if (I != null) {
// compute the inclination matrix by projecting the geomagnetic
// vector onto the Z (gravity) and X (horizontal component
// of geomagnetic vector) axes.
final float invE = 1.0f / (float)Math.sqrt(Ex*Ex + Ey*Ey + Ez*Ez);
final float c = (Ex*Mx + Ey*My + Ez*Mz) * invE;
final float s = (Ex*Ax + Ey*Ay + Ez*Az) * invE;
if (I.length == 9) {
I[0] = 1; I[1] = 0; I[2] = 0;
I[3] = 0; I[4] = c; I[5] = s;
I[6] = 0; I[7] =-s; I[8] = c;
} else if (I.length == 16) {
I[0] = 1; I[1] = 0; I[2] = 0;
I[4] = 0; I[5] = c; I[6] = s;
I[8] = 0; I[9] =-s; I[10]= c;
I[3] = I[7] = I[11] = I[12] = I[13] = I[14] = 0;
I[15] = 1;
}
}
return true;
}
/**
* Computes the geomagnetic inclination angle in radians from the
* inclination matrix <b>I</b> returned by {@link #getRotationMatrix}.
* @param I inclination matrix see {@link #getRotationMatrix}.
* @return The geomagnetic inclination angle in radians.
*/
public static float getInclination(float[] I) {
if (I.length == 9) {
return (float)Math.atan2(I[5], I[4]);
} else {
return (float)Math.atan2(I[6], I[5]);
}
}
/**
* Rotates the supplied rotation matrix so it is expressed in a
* different coordinate system. This is typically used when an application
* needs to compute the three orientation angles of the device (see
* {@link #getOrientation}) in a different coordinate system.
*
* <p>When the rotation matrix is used for drawing (for instance with
* OpenGL ES), it usually <b>doesn't need</b> to be transformed by this
* function, unless the screen is physically rotated, such as when used
* in landscape mode.
*
* <p><u>Examples:</u><p>
*
* <li>Using the camera (Y axis along the camera's axis) for an augmented
* reality application where the rotation angles are needed :</li><p>
*
* <code>remapCoordinateSystem(inR, AXIS_X, AXIS_Z, outR);</code><p>
*
* <li>Using the device as a mechanical compass in landscape mode:</li><p>
*
* <code>remapCoordinateSystem(inR, AXIS_Y, AXIS_MINUS_X, outR);</code><p>
*
* Beware of the above example. This call is needed only if the device is
* physically used in landscape mode to calculate the rotation angles (see
* {@link #getOrientation}).
* If the rotation matrix is also used for rendering, it may not need to
* be transformed, for instance if your {@link android.app.Activity
* Activity} is running in landscape mode.
*
* <p>Since the resulting coordinate system is orthonormal, only two axes
* need to be specified.
*
* @param inR the rotation matrix to be transformed. Usually it is the
* matrix returned by {@link #getRotationMatrix}.
* @param X defines on which world axis and direction the X axis of the
* device is mapped.
* @param Y defines on which world axis and direction the Y axis of the
* device is mapped.
* @param outR the transformed rotation matrix. inR and outR can be the same
* array, but it is not recommended for performance reason.
* @return true on success. false if the input parameters are incorrect, for
* instance if X and Y define the same axis. Or if inR and outR don't have
* the same length.
*/
public boolean remapCoordinateSystem(float[] inR, int X, int Y,
float[] outR)
{
if (inR == outR) {
final float[] temp = mTempMatrix;
synchronized(temp) {
// we don't expect to have a lot of contention
if (remapCoordinateSystemImpl(inR, X, Y, temp)) {
final int size = outR.length;
for (int i=0 ; i<size ; i++)
outR[i] = temp[i];
return true;
}
}
}
return remapCoordinateSystemImpl(inR, X, Y, outR);
}
private boolean remapCoordinateSystemImpl(float[] inR, int X, int Y,
float[] outR)
{
/*
* X and Y define a rotation matrix 'r':
*
* (X==1)?((X&0x80)?-1:1):0 (X==2)?((X&0x80)?-1:1):0 (X==3)?((X&0x80)?-1:1):0
* (Y==1)?((Y&0x80)?-1:1):0 (Y==2)?((Y&0x80)?-1:1):0 (Y==3)?((X&0x80)?-1:1):0
* r[0] ^ r[1]
*
* where the 3rd line is the vector product of the first 2 lines
*
*/
final int length = outR.length;
if (inR.length != length)
return false; // invalid parameter
if ((X & 0x7C)!=0 || (Y & 0x7C)!=0)
return false; // invalid parameter
if (((X & 0x3)==0) || ((Y & 0x3)==0))
return false; // no axis specified
if ((X & 0x3) == (Y & 0x3))
return false; // same axis specified
// Z is "the other" axis, its sign is either +/- sign(X)*sign(Y)
// this can be calculated by exclusive-or'ing X and Y; except for
// the sign inversion (+/-) which is calculated below.
int Z = X ^ Y;
// extract the axis (remove the sign), offset in the range 0 to 2.
final int x = (X & 0x3)-1;
final int y = (Y & 0x3)-1;
final int z = (Z & 0x3)-1;
// compute the sign of Z (whether it needs to be inverted)
final int axis_y = (z+1)%3;
final int axis_z = (z+2)%3;
if (((x^axis_y)|(y^axis_z)) != 0)
Z ^= 0x80;
final boolean sx = (X>=0x80);
final boolean sy = (Y>=0x80);
final boolean sz = (Z>=0x80);
// Perform R * r, in avoiding actual muls and adds.
final int rowLength = ((length==16)?4:3);
for (int j=0 ; j<3 ; j++) {
final int offset = j*rowLength;
for (int i=0 ; i<3 ; i++) {
if (x==i) outR[offset+i] = sx ? -inR[offset+0] : inR[offset+0];
if (y==i) outR[offset+i] = sy ? -inR[offset+1] : inR[offset+1];
if (z==i) outR[offset+i] = sz ? -inR[offset+2] : inR[offset+2];
}
}
if (length == 16) {
outR[3] = outR[7] = outR[11] = outR[12] = outR[13] = outR[14] = 0;
outR[15] = 1;
}
return true;
}
/**
* Computes the device's orientation based on the rotation matrix.
* <p> When it returns, the array values is filled with the result:
* <li>values[0]: <i>azimuth</i>, rotation around the Z axis.</li>
* <li>values[1]: <i>pitch</i>, rotation around the X axis.</li>
* <li>values[2]: <i>roll</i>, rotation around the Y axis.</li>
* <p>
*
* @param R rotation matrix see {@link #getRotationMatrix}.
* @param values an array of 3 floats to hold the result.
* @return The array values passed as argument.
*/
@Override
public float[] getOrientation(float[] R, float values[]) {
/*
* 4x4 (length=16) case:
* / R[ 0] R[ 1] R[ 2] 0 \
* | R[ 4] R[ 5] R[ 6] 0 |
* | R[ 8] R[ 9] R[10] 0 |
* \ 0 0 0 1 /
*
* 3x3 (length=9) case:
* / R[ 0] R[ 1] R[ 2] \
* | R[ 3] R[ 4] R[ 5] |
* \ R[ 6] R[ 7] R[ 8] /
*
*/
if (R.length == 9) {
values[0] = (float)Math.atan2(R[1], R[4]);
values[1] = (float)Math.asin(-R[7]);
values[2] = (float)Math.atan2(-R[6], R[8]);
} else {
values[0] = (float)Math.atan2(R[1], R[5]);
values[1] = (float)Math.asin(-R[9]);
values[2] = (float)Math.atan2(-R[8], R[10]);
}
return values;
}
}