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