aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim
diff options
context:
space:
mode:
authorJohn Cochran2012-01-03 11:38:38 -0600
committerMelanie2012-01-06 21:30:51 +0000
commite9b9eb6a4c6eaf161532994414f524e823d15120 (patch)
tree28b27e978e2c9ddf45c6aa583050fe468c7f960e /OpenSim
parentMerge branch 'master' into careminster (diff)
downloadopensim-SC-e9b9eb6a4c6eaf161532994414f524e823d15120.zip
opensim-SC-e9b9eb6a4c6eaf161532994414f524e823d15120.tar.gz
opensim-SC-e9b9eb6a4c6eaf161532994414f524e823d15120.tar.bz2
opensim-SC-e9b9eb6a4c6eaf161532994414f524e823d15120.tar.xz
Replaced llRot2Euler function.
The original function suffered from unexpected results due to rounding errors. An error of only 1 or 2 ulps would cause the code to not detect a singularity at Y rotation +/- PI/2 and take the non-singularity code path. The replacement code does not suffer from wildly inaccurate results at the +/- PI/2 singularity. The check in the code for the singularity isn't strictly needed, but gives more consistent results At the singularity, the X and Z rotations add. The if check simply forces the X rotation to be zero so the entirety of the X+Z rotation is carried by Z. Additionally, the test code has been updated to include test cases that caused the old code to fail. The test algorithm is also updated to perform a more meaningful test. The original code checked if the values against expected values. This could fail at +/- PI rotations since a rotation around an axis by PI causes the identical effect as a rotation by -PI. The new test code checks that the returned angles can be used to recreate a quaternion that causes the same rotation.
Diffstat (limited to 'OpenSim')
-rw-r--r--OpenSim/Region/ScriptEngine/Shared/Tests/LSL_ApiTest.cs97
1 files changed, 79 insertions, 18 deletions
diff --git a/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_ApiTest.cs b/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_ApiTest.cs
index 0cbad41..7594691 100644
--- a/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_ApiTest.cs
+++ b/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_ApiTest.cs
@@ -142,30 +142,91 @@ namespace OpenSim.Region.ScriptEngine.Shared.Tests
142 public void TestllRot2Euler() 142 public void TestllRot2Euler()
143 { 143 {
144 // 180, 90 and zero degree rotations. 144 // 180, 90 and zero degree rotations.
145 CheckllRot2Euler(new LSL_Types.Quaternion(1.0f, 0.0f, 0.0f, 0.0f), new LSL_Types.Vector3(Math.PI, 0.0f, 0.0f)); 145 CheckllRot2Euler(new LSL_Types.Quaternion(0.0f, 0.0f, 0.0f, 1.0f));
146 CheckllRot2Euler(new LSL_Types.Quaternion(0.0f, 1.0f, 0.0f, 0.0f), new LSL_Types.Vector3(Math.PI, 0.0f, Math.PI)); 146 CheckllRot2Euler(new LSL_Types.Quaternion(0.0f, 0.0f, 0.707107f, 0.707107f));
147 CheckllRot2Euler(new LSL_Types.Quaternion(0.0f, 0.0f, 1.0f, 0.0f), new LSL_Types.Vector3(0.0f, 0.0f, Math.PI)); 147 CheckllRot2Euler(new LSL_Types.Quaternion(0.0f, 0.0f, 1.0f, 0.0f));
148 CheckllRot2Euler(new LSL_Types.Quaternion(0.0f, 0.0f, 0.0f, 1.0f), new LSL_Types.Vector3(0.0f, 0.0f, 0.0f)); 148 CheckllRot2Euler(new LSL_Types.Quaternion(0.0f, 0.0f, 0.707107f, -0.707107f));
149 CheckllRot2Euler(new LSL_Types.Quaternion(-0.5f, -0.5f, 0.5f, 0.5f), new LSL_Types.Vector3(0, -Math.PI / 2.0f, Math.PI / 2.0f)); 149 CheckllRot2Euler(new LSL_Types.Quaternion(0.707107f, 0.0f, 0.0f, 0.707107f));
150 CheckllRot2Euler(new LSL_Types.Quaternion(-0.707107f, 0.0f, 0.0f, -0.707107f), new LSL_Types.Vector3(Math.PI / 2.0f, 0.0f, 0.0f)); 150 CheckllRot2Euler(new LSL_Types.Quaternion(0.5f, -0.5f, 0.5f, 0.5f));
151 CheckllRot2Euler(new LSL_Types.Quaternion(0.0f, -0.707107f, 0.707107f, 0.0f));
152 CheckllRot2Euler(new LSL_Types.Quaternion(-0.5f, -0.5f, 0.5f, -0.5f));
153 CheckllRot2Euler(new LSL_Types.Quaternion(1.0f, 0.0f, 0.0f, 0.0f));
154 CheckllRot2Euler(new LSL_Types.Quaternion(0.707107f, -0.707107f, 0.0f, 0.0f));
155 CheckllRot2Euler(new LSL_Types.Quaternion(0.0f, -1.0f, 0.0f, 0.0f));
156 CheckllRot2Euler(new LSL_Types.Quaternion(-0.707107f, -0.707107f, 0.0f, 0.0f));
157 CheckllRot2Euler(new LSL_Types.Quaternion(0.707107f, 0.0f, 0.0f, -0.707107f));
158 CheckllRot2Euler(new LSL_Types.Quaternion(0.5f, -0.5f, -0.5f, -0.5f));
159 CheckllRot2Euler(new LSL_Types.Quaternion(0.0f, -0.707107f, -0.707107f, 0.0f));
160 CheckllRot2Euler(new LSL_Types.Quaternion(-0.5f, -0.5f, -0.5f, 0.5f));
161 CheckllRot2Euler(new LSL_Types.Quaternion(0.0f, -0.707107f, 0.0f, 0.707107f));
162 CheckllRot2Euler(new LSL_Types.Quaternion(-0.5f, -0.5f, 0.5f, 0.5f));
163 CheckllRot2Euler(new LSL_Types.Quaternion(-0.707107f, 0.0f, 0.707107f, 0.0f));
164 CheckllRot2Euler(new LSL_Types.Quaternion(-0.5f, 0.5f, 0.5f, -0.5f));
165 CheckllRot2Euler(new LSL_Types.Quaternion(0.0f, -0.707107f, 0.0f, -0.707107f));
166 CheckllRot2Euler(new LSL_Types.Quaternion(-0.5f, -0.5f, -0.5f, -0.5f));
167 CheckllRot2Euler(new LSL_Types.Quaternion(-0.707107f, 0.0f, -0.707107f, 0.0f));
168 CheckllRot2Euler(new LSL_Types.Quaternion(-0.5f, 0.5f, -0.5f, 0.5f));
169
151 // A couple of messy rotations. 170 // A couple of messy rotations.
152 CheckllRot2Euler(new LSL_Types.Quaternion(1.0f, 5.651f, -3.1f, 67.023f), new LSL_Types.Vector3(0.037818f, 0.166447f, -0.095595f)); 171 CheckllRot2Euler(new LSL_Types.Quaternion(1.0f, 5.651f, -3.1f, 67.023f));
153 CheckllRot2Euler(new LSL_Types.Quaternion(0.719188f, -0.408934f, -0.363998f, -0.427841f), new LSL_Types.Vector3(-1.954769f, -0.174533f, 1.151917f)); 172 CheckllRot2Euler(new LSL_Types.Quaternion(0.719188f, -0.408934f, -0.363998f, -0.427841f));
173
174 // Some deliberately malicious rotations (intended on provoking singularity errors)
175 // The "f" suffexes are deliberately omitted.
176 CheckllRot2Euler(new LSL_Types.Quaternion(0.50001f, 0.50001f, 0.50001f, 0.50001f));
177 // More malice. The "f" suffixes are deliberately omitted.
178 CheckllRot2Euler(new LSL_Types.Quaternion(-0.701055, 0.092296, 0.701055, -0.092296));
179 CheckllRot2Euler(new LSL_Types.Quaternion(-0.183005, -0.683010, 0.183005, 0.683010));
180 CheckllRot2Euler(new LSL_Types.Quaternion(-0.430460, -0.560982, 0.430460, 0.560982));
181 CheckllRot2Euler(new LSL_Types.Quaternion(-0.701066, 0.092301, -0.701066, 0.092301));
182 CheckllRot2Euler(new LSL_Types.Quaternion(-0.183013, -0.683010, 0.183013, 0.683010));
183 CheckllRot2Euler(new LSL_Types.Quaternion(-0.183005, -0.683014, -0.183005, -0.683014));
184 CheckllRot2Euler(new LSL_Types.Quaternion(-0.353556, 0.612375, 0.353556, -0.612375));
185 CheckllRot2Euler(new LSL_Types.Quaternion(0.353554, -0.612385, -0.353554, 0.612385));
186 CheckllRot2Euler(new LSL_Types.Quaternion(-0.560989, 0.430450, 0.560989, -0.430450));
187 CheckllRot2Euler(new LSL_Types.Quaternion(-0.183013, 0.683009, -0.183013, 0.683009));
188 CheckllRot2Euler(new LSL_Types.Quaternion(0.430457, -0.560985, -0.430457, 0.560985));
189 CheckllRot2Euler(new LSL_Types.Quaternion(0.353552, 0.612360, -0.353552, -0.612360));
190 CheckllRot2Euler(new LSL_Types.Quaternion(-0.499991, 0.500003, 0.499991, -0.500003));
191 CheckllRot2Euler(new LSL_Types.Quaternion(-0.353555, -0.612385, -0.353555, -0.612385));
192 CheckllRot2Euler(new LSL_Types.Quaternion(0.701066, -0.092301, -0.701066, 0.092301));
193 CheckllRot2Euler(new LSL_Types.Quaternion(-0.499991, 0.500007, 0.499991, -0.500007));
194 CheckllRot2Euler(new LSL_Types.Quaternion(-0.683002, 0.183016, -0.683002, 0.183016));
195 CheckllRot2Euler(new LSL_Types.Quaternion(0.430458, 0.560982, 0.430458, 0.560982));
196 CheckllRot2Euler(new LSL_Types.Quaternion(0.499991, -0.500003, -0.499991, 0.500003));
197 CheckllRot2Euler(new LSL_Types.Quaternion(-0.183009, 0.683011, -0.183009, 0.683011));
198 CheckllRot2Euler(new LSL_Types.Quaternion(0.560975, -0.430457, 0.560975, -0.430457));
199 CheckllRot2Euler(new LSL_Types.Quaternion(0.701055, 0.092300, 0.701055, 0.092300));
200 CheckllRot2Euler(new LSL_Types.Quaternion(-0.560990, 0.430459, -0.560990, 0.430459));
201 CheckllRot2Euler(new LSL_Types.Quaternion(-0.092302, -0.701059, -0.092302, -0.701059));
154 } 202 }
155 203
156 private void CheckllRot2Euler(LSL_Types.Quaternion rot, LSL_Types.Vector3 eulerCheck) 204 // Testing Rot2Euler this way instead of comparing against expected angles because
205 // 1. There are several ways to get to the original Quaternion. For example a rotation
206 // of PI and -PI will give the same result. But PI and -PI aren't equal.
207 // 2. This method checks to see if the calculated angles from a quaternion can be used
208 // to create a new quaternion to produce the same rotation.
209 // However, can't compare the newly calculated quaternion against the original because
210 // once again, there are multiple quaternions that give the same result. For instance
211 // <X, Y, Z, S> == <-X, -Y, -Z, -S>. Additionally, the magnitude of S can be changed
212 // and will still result in the same rotation if the values for X, Y, Z are also changed
213 // to compensate.
214 // However, if two quaternions represent the same rotation, then multiplying the first
215 // quaternion by the conjugate of the second, will give a third quaternion representing
216 // a zero rotation. This can be tested for by looking at the X, Y, Z values which should
217 // be zero.
218 private void CheckllRot2Euler(LSL_Types.Quaternion rot)
157 { 219 {
158 // Call LSL function to convert quaternion rotaion to euler radians. 220 // Call LSL function to convert quaternion rotaion to euler radians.
159 LSL_Types.Vector3 eulerCalc = m_lslApi.llRot2Euler(rot); 221 LSL_Types.Vector3 eulerCalc = m_lslApi.llRot2Euler(rot);
160 // Check upper and lower bounds of x, y and z. 222 // Now use the euler radians to recalculate a new quaternion rotation
161 // This type of check is performed as opposed to comparing for equal numbers, in order to allow slight 223 LSL_Types.Quaternion newRot = m_lslApi.llEuler2Rot(eulerCalc);
162 // differences in accuracy. 224 // Multiple original quaternion by conjugate of quaternion calculated with angles.
163 Assert.Greater(eulerCalc.x, eulerCheck.x - ANGLE_ACCURACY_IN_RADIANS, "TestllRot2Euler X lower bounds check fail"); 225 LSL_Types.Quaternion check = rot * new LSL_Types.Quaternion(-newRot.x, -newRot.y, -newRot.z, newRot.s);
164 Assert.Less(eulerCalc.x, eulerCheck.x + ANGLE_ACCURACY_IN_RADIANS, "TestllRot2Euler X upper bounds check fail"); 226
165 Assert.Greater(eulerCalc.y, eulerCheck.y - ANGLE_ACCURACY_IN_RADIANS, "TestllRot2Euler Y lower bounds check fail"); 227 Assert.AreEqual(0.0, check.x, VECTOR_COMPONENT_ACCURACY, "TestllRot2Euler X bounds check fail");
166 Assert.Less(eulerCalc.y, eulerCheck.y + ANGLE_ACCURACY_IN_RADIANS, "TestllRot2Euler Y upper bounds check fail"); 228 Assert.AreEqual(0.0, check.y, VECTOR_COMPONENT_ACCURACY, "TestllRot2Euler Y bounds check fail");
167 Assert.Greater(eulerCalc.z, eulerCheck.z - ANGLE_ACCURACY_IN_RADIANS, "TestllRot2Euler Z lower bounds check fail"); 229 Assert.AreEqual(0.0, check.z, VECTOR_COMPONENT_ACCURACY, "TestllRot2Euler Z bounds check fail");
168 Assert.Less(eulerCalc.z, eulerCheck.z + ANGLE_ACCURACY_IN_RADIANS, "TestllRot2Euler Z upper bounds check fail");
169 } 230 }
170 231
171 [Test] 232 [Test]