| Index: content/renderer/device_sensors/device_orientation_util.cc
|
| diff --git a/content/renderer/device_sensors/device_orientation_util.cc b/content/renderer/device_sensors/device_orientation_util.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..15f27bfa8e9ed88a11fa6ac29fe58d91544b4e19
|
| --- /dev/null
|
| +++ b/content/renderer/device_sensors/device_orientation_util.cc
|
| @@ -0,0 +1,254 @@
|
| +// Copyright 2017 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#define _USE_MATH_DEFINES // For VC++ to get M_PI. This has to be first.
|
| +
|
| +#include "content/renderer/device_sensors/device_orientation_util.h"
|
| +
|
| +#include <cmath>
|
| +#include <vector>
|
| +
|
| +#include "base/logging.h"
|
| +#include "third_party/WebKit/public/platform/modules/device_orientation/WebDeviceOrientationData.h"
|
| +
|
| +namespace {
|
| +
|
| +bool IsAngleDifferent(bool has_angle1,
|
| + double angle1,
|
| + bool has_angle2,
|
| + double angle2) {
|
| + if (has_angle1 != has_angle2)
|
| + return true;
|
| + return (has_angle1 &&
|
| + std::fabs(angle1 - angle2) >= content::kOrientationThreshold);
|
| +}
|
| +
|
| +// Helper function to convert a quaternion to a rotation matrix. x, y, z, w
|
| +// are values of a quaternion representing the orientation of the device in
|
| +// 3D space. Returns a 9 element rotation matrix:
|
| +// r[ 0] r[ 1] r[ 2]
|
| +// r[ 3] r[ 4] r[ 5]
|
| +// r[ 6] r[ 7] r[ 8]
|
| +std::vector<double> ComputeRotationMatrixFromQuaternion(double x,
|
| + double y,
|
| + double z,
|
| + double w) {
|
| + std::vector<double> r(9);
|
| +
|
| + double sq_x = 2 * x * x;
|
| + double sq_y = 2 * y * y;
|
| + double sq_z = 2 * z * z;
|
| + double x_y = 2 * x * y;
|
| + double z_w = 2 * z * w;
|
| + double x_z = 2 * x * z;
|
| + double y_w = 2 * y * w;
|
| + double y_z = 2 * y * z;
|
| + double x_w = 2 * x * w;
|
| +
|
| + r[0] = 1 - sq_y - sq_z;
|
| + r[1] = x_y - z_w;
|
| + r[2] = x_z + y_w;
|
| + r[3] = x_y + z_w;
|
| + r[4] = 1 - sq_x - sq_z;
|
| + r[5] = y_z - x_w;
|
| + r[6] = x_z - y_w;
|
| + r[7] = y_z + x_w;
|
| + r[8] = 1 - sq_x - sq_y;
|
| +
|
| + return r;
|
| +}
|
| +
|
| +// Helper function to compute the rotation matrix using gravity and geomagnetic
|
| +// data. Returns a 9 element rotation matrix:
|
| +// r[ 0] r[ 1] r[ 2]
|
| +// r[ 3] r[ 4] r[ 5]
|
| +// r[ 6] r[ 7] r[ 8]
|
| +// r is 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 matricx may be inaccurate.
|
| +//
|
| +// Free fall is defined as condition when the magnitude of the gravity is less
|
| +// than 1/10 of the nominal value.
|
| +bool ComputeRotationMatrixFromGravityAndGeomagnetic(double gravity_x,
|
| + double gravity_y,
|
| + double gravity_z,
|
| + double geomagnetic_x,
|
| + double geomagnetic_y,
|
| + double geomagnetic_z,
|
| + std::vector<double>* r) {
|
| + double gravity_squared =
|
| + (gravity_x * gravity_x + gravity_y * gravity_y + gravity_z * gravity_z);
|
| + double g = 9.81;
|
| + double free_fall_gravity_squared = 0.01 * g * g;
|
| + if (gravity_squared < free_fall_gravity_squared) {
|
| + // gravity less than 10% of normal value
|
| + return false;
|
| + }
|
| +
|
| + double hx = geomagnetic_y * gravity_z - geomagnetic_z * gravity_y;
|
| + double hy = geomagnetic_z * gravity_x - geomagnetic_x * gravity_z;
|
| + double hz = geomagnetic_x * gravity_y - geomagnetic_y * gravity_x;
|
| + double norm_h = std::sqrt(hx * hx + hy * hy + hz * hz);
|
| + if (norm_h < 0.1) {
|
| + // device is close to free fall, or close to magnetic north pole.
|
| + // Typical values are > 100.
|
| + return false;
|
| + }
|
| +
|
| + double inv_h = 1.0 / norm_h;
|
| + hx *= inv_h;
|
| + hy *= inv_h;
|
| + hz *= inv_h;
|
| + double inv_gravity = 1.0 / std::sqrt(gravity_squared);
|
| + gravity_x *= inv_gravity;
|
| + gravity_y *= inv_gravity;
|
| + gravity_z *= inv_gravity;
|
| + double mx = gravity_y * hz - gravity_z * hy;
|
| + double my = gravity_z * hx - gravity_x * hz;
|
| + double mz = gravity_x * hy - gravity_y * hx;
|
| +
|
| + r->resize(9);
|
| +
|
| + (*r)[0] = hx;
|
| + (*r)[1] = hy;
|
| + (*r)[2] = hz;
|
| + (*r)[3] = mx;
|
| + (*r)[4] = my;
|
| + (*r)[5] = mz;
|
| + (*r)[6] = gravity_x;
|
| + (*r)[7] = gravity_y;
|
| + (*r)[8] = gravity_z;
|
| +
|
| + return true;
|
| +}
|
| +
|
| +// Returns orientation angles from a rotation matrix, such that the angles are
|
| +// according to spec http://dev.w3.org/geo/api/spec-source-orientation.html}.
|
| +//
|
| +// It is assumed the rotation matrix transforms a 3D column vector from device
|
| +// coordinate system to the world's coordinate system.
|
| +//
|
| +// In particular we compute the decomposition of a given rotation matrix r such
|
| +// that
|
| +// r = rz(alpha) * rx(beta) * ry(gamma)
|
| +// where rz, rx and ry are rotation matrices around z, x and y axes in the world
|
| +// coordinate reference frame respectively. The reference frame consists of
|
| +// three orthogonal axes x, y, z where x points East, y points north and z
|
| +// points upwards perpendicular to the ground plane. The computed angles alpha,
|
| +// beta and gamma are in degrees and clockwise-positive when viewed along the
|
| +// positive direction of the corresponding axis. Except for the special case
|
| +// when the beta angle is +-pi/2 these angles uniquely define the orientation of
|
| +// a mobile device in 3D space. The alpha-beta-gamma representation resembles
|
| +// the yaw-pitch-roll convention used in vehicle dynamics, however it does not
|
| +// exactly match it. One of the differences is that the 'pitch' angle beta is
|
| +// allowed to be within [-pi, pi). A mobile device with pitch angle greater than
|
| +// pi/2 could correspond to a user lying down and looking upward at the screen.
|
| +//
|
| +// r is a 9 element rotation matrix:
|
| +// r[ 0] r[ 1] r[ 2]
|
| +// r[ 3] r[ 4] r[ 5]
|
| +// r[ 6] r[ 7] r[ 8]
|
| +//
|
| +// alpha: rotation around the z axis, in [0, 2*pi)
|
| +// beta: rotation around the x axis, in [-pi, pi)
|
| +// gamma: rotation around the y axis, in [-pi/2, pi/2)
|
| +void ComputeDeviceOrientationFromRotationMatrix(const std::vector<double>& r,
|
| + double* alpha,
|
| + double* beta,
|
| + double* gamma) {
|
| + DCHECK_EQ(9u, r.size());
|
| +
|
| + if (r[8] > 0) { // cos(beta) > 0
|
| + *alpha = std::atan2(-r[1], r[4]);
|
| + *beta = std::asin(r[7]); // beta (-pi/2, pi/2)
|
| + *gamma = std::atan2(-r[6], r[8]); // gamma (-pi/2, pi/2)
|
| + } else if (r[8] < 0) { // cos(beta) < 0
|
| + *alpha = std::atan2(r[1], -r[4]);
|
| + *beta = -std::asin(r[7]);
|
| + *beta += (*beta >= 0) ? -M_PI : M_PI; // beta [-pi,-pi/2) U (pi/2,pi)
|
| + *gamma = std::atan2(r[6], -r[8]); // gamma (-pi/2, pi/2)
|
| + } else { // r[8] == 0
|
| + if (r[6] > 0) { // cos(gamma) == 0, cos(beta) > 0
|
| + *alpha = std::atan2(-r[1], r[4]);
|
| + *beta = std::asin(r[7]); // beta [-pi/2, pi/2]
|
| + *gamma = -M_PI / 2; // gamma = -pi/2
|
| + } else if (r[6] < 0) { // cos(gamma) == 0, cos(beta) < 0
|
| + *alpha = std::atan2(r[1], -r[4]);
|
| + *beta = -std::asin(r[7]);
|
| + *beta += (*beta >= 0) ? -M_PI : M_PI; // beta [-pi,-pi/2) U (pi/2,pi)
|
| + *gamma = -M_PI / 2; // gamma = -pi/2
|
| + } else { // r[6] == 0, cos(beta) == 0
|
| + // gimbal lock discontinuity
|
| + *alpha = std::atan2(r[3], r[0]);
|
| + *beta = (r[7] > 0) ? M_PI / 2 : -M_PI / 2; // beta = +-pi/2
|
| + *gamma = 0; // gamma = 0
|
| + }
|
| + }
|
| +
|
| + // alpha is in [-pi, pi], make sure it is in [0, 2*pi).
|
| + if (*alpha < 0)
|
| + *alpha += 2 * M_PI; // alpha [0, 2*pi)
|
| +}
|
| +
|
| +double RadiansToDegrees(double radians) {
|
| + return (180.0 * radians) / M_PI;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +namespace content {
|
| +
|
| +bool IsSignificantlyDifferent(const blink::WebDeviceOrientationData& data1,
|
| + const blink::WebDeviceOrientationData& data2) {
|
| + return IsAngleDifferent(data1.has_alpha, data1.alpha, data2.has_alpha,
|
| + data2.alpha) ||
|
| + IsAngleDifferent(data1.has_beta, data1.beta, data2.has_beta,
|
| + data2.beta) ||
|
| + IsAngleDifferent(data1.has_gamma, data1.gamma, data2.has_gamma,
|
| + data2.gamma);
|
| +}
|
| +
|
| +void ComputeDeviceOrientationFromQuaternion(double x,
|
| + double y,
|
| + double z,
|
| + double w,
|
| + double* alpha,
|
| + double* beta,
|
| + double* gamma) {
|
| + std::vector<double> rotation_matrix =
|
| + ComputeRotationMatrixFromQuaternion(x, y, z, w);
|
| + double alpha_radians, beta_radians, gamma_radians;
|
| + ComputeDeviceOrientationFromRotationMatrix(rotation_matrix, &alpha_radians,
|
| + &beta_radians, &gamma_radians);
|
| + *alpha = RadiansToDegrees(alpha_radians);
|
| + *beta = RadiansToDegrees(beta_radians);
|
| + *gamma = RadiansToDegrees(gamma_radians);
|
| +}
|
| +
|
| +bool ComputeDeviceOrientationFromGravityAndGeomagnetic(double gravity_x,
|
| + double gravity_y,
|
| + double gravity_z,
|
| + double geomagnetic_x,
|
| + double geomagnetic_y,
|
| + double geomagnetic_z,
|
| + double* alpha,
|
| + double* beta,
|
| + double* gamma) {
|
| + std::vector<double> rotation_matrix;
|
| + if (!ComputeRotationMatrixFromGravityAndGeomagnetic(
|
| + gravity_x, gravity_y, gravity_z, geomagnetic_x, geomagnetic_y,
|
| + geomagnetic_z, &rotation_matrix)) {
|
| + return false;
|
| + }
|
| +
|
| + double alpha_radians, beta_radians, gamma_radians;
|
| + ComputeDeviceOrientationFromRotationMatrix(rotation_matrix, &alpha_radians,
|
| + &beta_radians, &gamma_radians);
|
| + *alpha = RadiansToDegrees(alpha_radians);
|
| + *beta = RadiansToDegrees(beta_radians);
|
| + *gamma = RadiansToDegrees(gamma_radians);
|
| + return true;
|
| +}
|
| +
|
| +} // namespace content
|
|
|