package com.google.vrtoolkit.cardboard.sensors.internal; public class So3Util { private static final double M_SQRT1_2 = 0.7071067811865476; private static final double ONE_6TH = 0.1666666716337204; private static final double ONE_20TH = 0.1666666716337204; private static Vector3d temp31; private static Vector3d sO3FromTwoVecN; private static Vector3d sO3FromTwoVecA; private static Vector3d sO3FromTwoVecB; private static Vector3d sO3FromTwoVecRotationAxis; private static Matrix3x3d sO3FromTwoVec33R1; private static Matrix3x3d sO3FromTwoVec33R2; private static Vector3d muFromSO3R2; private static Vector3d rotationPiAboutAxisTemp; public static void sO3FromTwoVec(final Vector3d a, final Vector3d b, final Matrix3x3d result) { Vector3d.cross(a, b, So3Util.sO3FromTwoVecN); if (So3Util.sO3FromTwoVecN.length() == 0.0) { final double dot = Vector3d.dot(a, b); if (dot >= 0.0) { result.setIdentity(); } else { Vector3d.ortho(a, So3Util.sO3FromTwoVecRotationAxis); rotationPiAboutAxis(So3Util.sO3FromTwoVecRotationAxis, result); } return; } So3Util.sO3FromTwoVecA.set(a); So3Util.sO3FromTwoVecB.set(b); So3Util.sO3FromTwoVecN.normalize(); So3Util.sO3FromTwoVecA.normalize(); So3Util.sO3FromTwoVecB.normalize(); final Matrix3x3d r1 = So3Util.sO3FromTwoVec33R1; r1.setColumn(0, So3Util.sO3FromTwoVecA); r1.setColumn(1, So3Util.sO3FromTwoVecN); Vector3d.cross(So3Util.sO3FromTwoVecN, So3Util.sO3FromTwoVecA, So3Util.temp31); r1.setColumn(2, So3Util.temp31); final Matrix3x3d r2 = So3Util.sO3FromTwoVec33R2; r2.setColumn(0, So3Util.sO3FromTwoVecB); r2.setColumn(1, So3Util.sO3FromTwoVecN); Vector3d.cross(So3Util.sO3FromTwoVecN, So3Util.sO3FromTwoVecB, So3Util.temp31); r2.setColumn(2, So3Util.temp31); r1.transpose(); Matrix3x3d.mult(r2, r1, result); } private static void rotationPiAboutAxis(final Vector3d v, final Matrix3x3d result) { So3Util.rotationPiAboutAxisTemp.set(v); So3Util.rotationPiAboutAxisTemp.scale(3.141592653589793 / So3Util.rotationPiAboutAxisTemp.length()); final double invTheta = 0.3183098861837907; final double kA = 0.0; final double kB = 0.20264236728467558; rodriguesSo3Exp(So3Util.rotationPiAboutAxisTemp, kA, kB, result); } public static void sO3FromMu(final Vector3d w, final Matrix3x3d result) { final double thetaSq = Vector3d.dot(w, w); final double theta = Math.sqrt(thetaSq); double kA; double kB; if (thetaSq < 1.0E-8) { kA = 1.0 - 0.1666666716337204 * thetaSq; kB = 0.5; } else if (thetaSq < 1.0E-6) { kB = 0.5 - 0.0416666679084301 * thetaSq; kA = 1.0 - thetaSq * 0.1666666716337204 * (1.0 - 0.1666666716337204 * thetaSq); } else { final double invTheta = 1.0 / theta; kA = Math.sin(theta) * invTheta; kB = (1.0 - Math.cos(theta)) * (invTheta * invTheta); } rodriguesSo3Exp(w, kA, kB, result); } public static void muFromSO3(final Matrix3x3d so3, final Vector3d result) { final double cosAngle = (so3.get(0, 0) + so3.get(1, 1) + so3.get(2, 2) - 1.0) * 0.5; result.set((so3.get(2, 1) - so3.get(1, 2)) / 2.0, (so3.get(0, 2) - so3.get(2, 0)) / 2.0, (so3.get(1, 0) - so3.get(0, 1)) / 2.0); final double sinAngleAbs = result.length(); if (cosAngle > 0.7071067811865476) { if (sinAngleAbs > 0.0) { result.scale(Math.asin(sinAngleAbs) / sinAngleAbs); } } else if (cosAngle > -0.7071067811865476) { final double angle = Math.acos(cosAngle); result.scale(angle / sinAngleAbs); } else { final double angle = 3.141592653589793 - Math.asin(sinAngleAbs); final double d0 = so3.get(0, 0) - cosAngle; final double d = so3.get(1, 1) - cosAngle; final double d2 = so3.get(2, 2) - cosAngle; final Vector3d r2 = So3Util.muFromSO3R2; if (d0 * d0 > d * d && d0 * d0 > d2 * d2) { r2.set(d0, (so3.get(1, 0) + so3.get(0, 1)) / 2.0, (so3.get(0, 2) + so3.get(2, 0)) / 2.0); } else if (d * d > d2 * d2) { r2.set((so3.get(1, 0) + so3.get(0, 1)) / 2.0, d, (so3.get(2, 1) + so3.get(1, 2)) / 2.0); } else { r2.set((so3.get(0, 2) + so3.get(2, 0)) / 2.0, (so3.get(2, 1) + so3.get(1, 2)) / 2.0, d2); } if (Vector3d.dot(r2, result) < 0.0) { r2.scale(-1.0); } r2.normalize(); r2.scale(angle); result.set(r2); } } private static void rodriguesSo3Exp(final Vector3d w, final double kA, final double kB, final Matrix3x3d result) { final double wx2 = w.x * w.x; final double wy2 = w.y * w.y; final double wz2 = w.z * w.z; result.set(0, 0, 1.0 - kB * (wy2 + wz2)); result.set(1, 1, 1.0 - kB * (wx2 + wz2)); result.set(2, 2, 1.0 - kB * (wx2 + wy2)); double a = kA * w.z; double b = kB * (w.x * w.y); result.set(0, 1, b - a); result.set(1, 0, b + a); a = kA * w.y; b = kB * (w.x * w.z); result.set(0, 2, b + a); result.set(2, 0, b - a); a = kA * w.x; b = kB * (w.y * w.z); result.set(1, 2, b - a); result.set(2, 1, b + a); } public static void generatorField(final int i, final Matrix3x3d pos, final Matrix3x3d result) { result.set(i, 0, 0.0); result.set((i + 1) % 3, 0, -pos.get((i + 2) % 3, 0)); result.set((i + 2) % 3, 0, pos.get((i + 1) % 3,0)); } static { So3Util.temp31 = new Vector3d(); So3Util.sO3FromTwoVecN = new Vector3d(); So3Util.sO3FromTwoVecA = new Vector3d(); So3Util.sO3FromTwoVecB = new Vector3d(); So3Util.sO3FromTwoVecRotationAxis = new Vector3d(); So3Util.sO3FromTwoVec33R1 = new Matrix3x3d(); So3Util.sO3FromTwoVec33R2 = new Matrix3x3d(); So3Util.muFromSO3R2 = new Vector3d(); So3Util.rotationPiAboutAxisTemp = new Vector3d(); } }