Author: luc Date: Fri Aug 5 15:05:33 2011 New Revision: 1154257 URL: http://svn.apache.org/viewvc?rev=1154257&view=rev Log: Fixed a wrong detection of rotation axis versus vectors plane in Rotation constructor using two vectors pairs.
JIRA: MATH-639 Modified: commons/proper/math/trunk/src/main/java/org/apache/commons/math/geometry/euclidean/threed/Rotation.java commons/proper/math/trunk/src/site/xdoc/changes.xml commons/proper/math/trunk/src/test/java/org/apache/commons/math/geometry/euclidean/threed/RotationTest.java Modified: commons/proper/math/trunk/src/main/java/org/apache/commons/math/geometry/euclidean/threed/Rotation.java URL: http://svn.apache.org/viewvc/commons/proper/math/trunk/src/main/java/org/apache/commons/math/geometry/euclidean/threed/Rotation.java?rev=1154257&r1=1154256&r2=1154257&view=diff ============================================================================== --- commons/proper/math/trunk/src/main/java/org/apache/commons/math/geometry/euclidean/threed/Rotation.java (original) +++ commons/proper/math/trunk/src/main/java/org/apache/commons/math/geometry/euclidean/threed/Rotation.java Fri Aug 5 15:05:33 2011 @@ -313,92 +313,51 @@ public class Rotation implements Seriali public Rotation(Vector3D u1, Vector3D u2, Vector3D v1, Vector3D v2) { // norms computation - double u1u1 = Vector3D.dotProduct(u1, u1); - double u2u2 = Vector3D.dotProduct(u2, u2); - double v1v1 = Vector3D.dotProduct(v1, v1); - double v2v2 = Vector3D.dotProduct(v2, v2); + double u1u1 = u1.getNormSq(); + double u2u2 = u2.getNormSq(); + double v1v1 = v1.getNormSq(); + double v2v2 = v2.getNormSq(); if ((u1u1 == 0) || (u2u2 == 0) || (v1v1 == 0) || (v2v2 == 0)) { throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.ZERO_NORM_FOR_ROTATION_DEFINING_VECTOR); } - double u1x = u1.getX(); - double u1y = u1.getY(); - double u1z = u1.getZ(); - - double u2x = u2.getX(); - double u2y = u2.getY(); - double u2z = u2.getZ(); - // normalize v1 in order to have (v1'|v1') = (u1|u1) - double coeff = FastMath.sqrt (u1u1 / v1v1); - double v1x = coeff * v1.getX(); - double v1y = coeff * v1.getY(); - double v1z = coeff * v1.getZ(); - v1 = new Vector3D(v1x, v1y, v1z); - - // adjust v2 in order to have (u1|u2) = (v1|v2) and (v2'|v2') = (u2|u2) - double u1u2 = Vector3D.dotProduct(u1, u2); - double v1v2 = Vector3D.dotProduct(v1, v2); + v1 = new Vector3D(FastMath.sqrt(u1u1 / v1v1), v1); + + // adjust v2 in order to have (u1|u2) = (v1'|v2') and (v2'|v2') = (u2|u2) + double u1u2 = u1.dotProduct(u2); + double v1v2 = v1.dotProduct(v2); double coeffU = u1u2 / u1u1; double coeffV = v1v2 / u1u1; double beta = FastMath.sqrt((u2u2 - u1u2 * coeffU) / (v2v2 - v1v2 * coeffV)); double alpha = coeffU - beta * coeffV; - double v2x = alpha * v1x + beta * v2.getX(); - double v2y = alpha * v1y + beta * v2.getY(); - double v2z = alpha * v1z + beta * v2.getZ(); - v2 = new Vector3D(v2x, v2y, v2z); - - // preliminary computation (we use explicit formulation instead - // of relying on the Vector3D class in order to avoid building lots - // of temporary objects) - Vector3D uRef = u1; - Vector3D vRef = v1; - double dx1 = v1x - u1.getX(); - double dy1 = v1y - u1.getY(); - double dz1 = v1z - u1.getZ(); - double dx2 = v2x - u2.getX(); - double dy2 = v2y - u2.getY(); - double dz2 = v2z - u2.getZ(); - Vector3D k = new Vector3D(dy1 * dz2 - dz1 * dy2, - dz1 * dx2 - dx1 * dz2, - dx1 * dy2 - dy1 * dx2); - double c = k.getX() * (u1y * u2z - u1z * u2y) + - k.getY() * (u1z * u2x - u1x * u2z) + - k.getZ() * (u1x * u2y - u1y * u2x); + v2 = new Vector3D(alpha, v1, beta, v2); - if (c == 0) { - // the (q1, q2, q3) vector is in the (u1, u2) plane + // preliminary computation + Vector3D uRef = u1; + Vector3D vRef = v1; + Vector3D v1Su1 = v1.subtract(u1); + Vector3D v2Su2 = v2.subtract(u2); + Vector3D k = v1Su1.crossProduct(v2Su2); + Vector3D u3 = u1.crossProduct(u2); + double c = k.dotProduct(u3); + final double inPlaneThreshold = 0.001; + if (c <= inPlaneThreshold * k.getNorm() * u3.getNorm()) { + // the (q1, q2, q3) vector is close to the (u1, u2) plane // we try other vectors - Vector3D u3 = Vector3D.crossProduct(u1, u2); Vector3D v3 = Vector3D.crossProduct(v1, v2); - double u3x = u3.getX(); - double u3y = u3.getY(); - double u3z = u3.getZ(); - double v3x = v3.getX(); - double v3y = v3.getY(); - double v3z = v3.getZ(); - - double dx3 = v3x - u3x; - double dy3 = v3y - u3y; - double dz3 = v3z - u3z; - k = new Vector3D(dy1 * dz3 - dz1 * dy3, - dz1 * dx3 - dx1 * dz3, - dx1 * dy3 - dy1 * dx3); - c = k.getX() * (u1y * u3z - u1z * u3y) + - k.getY() * (u1z * u3x - u1x * u3z) + - k.getZ() * (u1x * u3y - u1y * u3x); - - if (c == 0) { - // the (q1, q2, q3) vector is aligned with u1: - // we try (u2, u3) and (v2, v3) - k = new Vector3D(dy2 * dz3 - dz2 * dy3, - dz2 * dx3 - dx2 * dz3, - dx2 * dy3 - dy2 * dx3); - c = k.getX() * (u2y * u3z - u2z * u3y) + - k.getY() * (u2z * u3x - u2x * u3z) + - k.getZ() * (u2x * u3y - u2y * u3x); + Vector3D v3Su3 = v3.subtract(u3); + k = v1Su1.crossProduct(v3Su3); + Vector3D u2Prime = u1.crossProduct(u3); + c = k.dotProduct(u2Prime); + + if (c <= inPlaneThreshold * k.getNorm() * u2Prime.getNorm()) { + // the (q1, q2, q3) vector is also close to the (u1, u3) plane, + // it is almost aligned with u1: we try (u2, u3) and (v2, v3) + k = v2Su2.crossProduct(v3Su3);; + c = k.dotProduct(u2.crossProduct(u3));; - if (c == 0) { + if (c <= 0) { // the (q1, q2, q3) vector is aligned with everything // this is really the identity rotation q0 = 1.0; @@ -427,8 +386,7 @@ public class Rotation implements Seriali k = new Vector3D(uRef.getY() * q3 - uRef.getZ() * q2, uRef.getZ() * q1 - uRef.getX() * q3, uRef.getX() * q2 - uRef.getY() * q1); - c = Vector3D.dotProduct(k, k); - q0 = Vector3D.dotProduct(vRef, k) / (c + c); + q0 = vRef.dotProduct(k) / (2 * k.getNormSq()); } @@ -452,7 +410,7 @@ public class Rotation implements Seriali throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.ZERO_NORM_FOR_ROTATION_DEFINING_VECTOR); } - double dot = Vector3D.dotProduct(u, v); + double dot = u.dotProduct(v); if (dot < ((2.0e-15 - 1.0) * normProduct)) { // special case u = -v: we select a PI angle rotation around @@ -467,9 +425,10 @@ public class Rotation implements Seriali // the shortest possible rotation: axis orthogonal to this plane q0 = FastMath.sqrt(0.5 * (1.0 + dot / normProduct)); double coeff = 1.0 / (2.0 * q0 * normProduct); - q1 = coeff * (v.getY() * u.getZ() - v.getZ() * u.getY()); - q2 = coeff * (v.getZ() * u.getX() - v.getX() * u.getZ()); - q3 = coeff * (v.getX() * u.getY() - v.getY() * u.getX()); + Vector3D q = v.crossProduct(u); + q1 = coeff * q.getX(); + q2 = coeff * q.getY(); + q3 = coeff * q.getZ(); } } Modified: commons/proper/math/trunk/src/site/xdoc/changes.xml URL: http://svn.apache.org/viewvc/commons/proper/math/trunk/src/site/xdoc/changes.xml?rev=1154257&r1=1154256&r2=1154257&view=diff ============================================================================== --- commons/proper/math/trunk/src/site/xdoc/changes.xml (original) +++ commons/proper/math/trunk/src/site/xdoc/changes.xml Fri Aug 5 15:05:33 2011 @@ -52,6 +52,10 @@ The <action> type attribute can be add,u If the output is not quite correct, check for invisible trailing spaces! --> <release version="3.0" date="TBD" description="TBD"> + <action dev="luc" type="fix" issue="MATH-639" > + Fixed a wrong detection of rotation axis versus vectors plane in Rotation constructor + using two vectors pairs. + </action> <action dev="luc" type="add" > Added a few linearCombination utility methods in MathUtils to compute accurately linear combinations a1.b1 + a2.b2 + ... + an.bn taking great care to compensate Modified: commons/proper/math/trunk/src/test/java/org/apache/commons/math/geometry/euclidean/threed/RotationTest.java URL: http://svn.apache.org/viewvc/commons/proper/math/trunk/src/test/java/org/apache/commons/math/geometry/euclidean/threed/RotationTest.java?rev=1154257&r1=1154256&r2=1154257&view=diff ============================================================================== --- commons/proper/math/trunk/src/test/java/org/apache/commons/math/geometry/euclidean/threed/RotationTest.java (original) +++ commons/proper/math/trunk/src/test/java/org/apache/commons/math/geometry/euclidean/threed/RotationTest.java Fri Aug 5 15:05:33 2011 @@ -17,11 +17,6 @@ package org.apache.commons.math.geometry.euclidean.threed; -import org.apache.commons.math.geometry.euclidean.threed.CardanEulerSingularityException; -import org.apache.commons.math.geometry.euclidean.threed.NotARotationMatrixException; -import org.apache.commons.math.geometry.euclidean.threed.Rotation; -import org.apache.commons.math.geometry.euclidean.threed.RotationOrder; -import org.apache.commons.math.geometry.euclidean.threed.Vector3D; import org.apache.commons.math.util.FastMath; import org.apache.commons.math.util.MathUtils; import org.junit.Assert; @@ -481,6 +476,21 @@ public class RotationTest { } + @Test + public void testIssue639(){ + Vector3D u1 = new Vector3D(-1321008684645961.0 / 268435456.0, + -5774608829631843.0 / 268435456.0, + -3822921525525679.0 / 4294967296.0); + Vector3D u2 =new Vector3D( -5712344449280879.0 / 2097152.0, + -2275058564560979.0 / 1048576.0, + 4423475992255071.0 / 65536.0); + Rotation rot = new Rotation(u1, u2, Vector3D.PLUS_I,Vector3D.PLUS_K); + Assert.assertEquals( 0.6228370359608200639829222, rot.getQ0(), 1.0e-15); + Assert.assertEquals( 0.0257707621456498790029987, rot.getQ1(), 1.0e-15); + Assert.assertEquals(-0.0000000002503012255839931, rot.getQ2(), 1.0e-15); + Assert.assertEquals(-0.7819270390861109450724902, rot.getQ3(), 1.0e-15); + } + private void checkVector(Vector3D v1, Vector3D v2) { Assert.assertTrue(v1.subtract(v2).getNorm() < 1.0e-10); }