public void SamplingKeyFrames() { var keyFrame0 = new KeyFrame <QuaternionF>(TimeSpan.FromSeconds(1.0), _random.NextQuaternionF()); var keyFrame1 = new KeyFrame <QuaternionF>(TimeSpan.FromSeconds(2.0), _random.NextQuaternionF()); var keyFrame2 = new KeyFrame <QuaternionF>(TimeSpan.FromSeconds(3.0), _random.NextQuaternionF()); var animation = new QuaternionFKeyFrameAnimation(); animation.KeyFrames.Add(keyFrame0); animation.KeyFrames.Add(keyFrame1); animation.KeyFrames.Add(keyFrame2); var defaultSource = _random.NextQuaternionF(); var defaultTarget = _random.NextQuaternionF(); // Without interpolation animation.EnableInterpolation = false; Assert.AreEqual(keyFrame0.Value, animation.GetValue(TimeSpan.FromSeconds(1.0), defaultSource, defaultTarget)); Assert.AreEqual(keyFrame0.Value, animation.GetValue(TimeSpan.FromSeconds(1.75), defaultSource, defaultTarget)); Assert.AreEqual(keyFrame1.Value, animation.GetValue(TimeSpan.FromSeconds(2.0), defaultSource, defaultTarget)); Assert.AreEqual(keyFrame1.Value, animation.GetValue(TimeSpan.FromSeconds(2.75), defaultSource, defaultTarget)); Assert.AreEqual(keyFrame2.Value, animation.GetValue(TimeSpan.FromSeconds(3.0), defaultSource, defaultTarget)); // With interpolation animation.EnableInterpolation = true; Assert.IsTrue(QuaternionF.AreNumericallyEqual(keyFrame0.Value, animation.GetValue(TimeSpan.FromSeconds(1.0), defaultSource, defaultTarget))); var expected = InterpolationHelper.Lerp(keyFrame0.Value, keyFrame1.Value, 0.75f); Assert.AreEqual(expected, animation.GetValue(TimeSpan.FromSeconds(1.75), defaultSource, defaultTarget)); Assert.AreEqual(keyFrame1.Value, animation.GetValue(TimeSpan.FromSeconds(2.0), defaultSource, defaultTarget)); expected = InterpolationHelper.Lerp(keyFrame1.Value, keyFrame2.Value, 0.75f); Assert.AreEqual(expected, animation.GetValue(TimeSpan.FromSeconds(2.75), defaultSource, defaultTarget)); Assert.AreEqual(keyFrame2.Value, animation.GetValue(TimeSpan.FromSeconds(3.0), defaultSource, defaultTarget)); }
public void CyclicOffsetLoopBehavior() { var keyFrame0 = new KeyFrame <QuaternionF>(TimeSpan.FromSeconds(0.0), _random.NextQuaternionF()); var keyFrame1 = new KeyFrame <QuaternionF>(TimeSpan.FromSeconds(1.0), _random.NextQuaternionF()); var keyFrame2 = new KeyFrame <QuaternionF>(TimeSpan.FromSeconds(2.0), _random.NextQuaternionF()); var animation = new QuaternionFKeyFrameAnimation(); animation.KeyFrames.Add(keyFrame0); animation.KeyFrames.Add(keyFrame1); animation.KeyFrames.Add(keyFrame2); var animationClip = new AnimationClip <QuaternionF> { Animation = animation }; animationClip.LoopBehavior = LoopBehavior.CycleOffset; animationClip.Duration = TimeSpan.MaxValue; animationClip.ClipOffset = TimeSpan.FromSeconds(-1); var defaultSource = _random.NextQuaternionF(); var defaultTarget = _random.NextQuaternionF(); // Pre loop var cycleOffset = keyFrame2.Value * keyFrame0.Value.Inverse; var expected = InterpolationHelper.Lerp(keyFrame1.Value, keyFrame2.Value, 0.25f); Assert.IsTrue(QuaternionF.AreNumericallyEqual(expected, cycleOffset * animationClip.GetValue(TimeSpan.FromSeconds(0.25), defaultSource, defaultTarget))); // Post loop expected = cycleOffset * cycleOffset * InterpolationHelper.Lerp(keyFrame1.Value, keyFrame2.Value, 0.75f); Assert.IsTrue(QuaternionF.AreNumericallyEqual(expected, animationClip.GetValue(TimeSpan.FromSeconds(6.75), defaultSource, defaultTarget))); }
public void Power3() { const float θ = 0.4f; Vector3F v = new Vector3F(2.3f, 1.0f, -2.0f); v.Normalize(); QuaternionF q = new QuaternionF((float)Math.Cos(θ), (float)Math.Sin(θ) * v); QuaternionF q2 = q; q2.Power(2); Assert.IsTrue(QuaternionF.AreNumericallyEqual(q * q, q2)); QuaternionF q3 = q; q3.Power(3); Assert.IsTrue(QuaternionF.AreNumericallyEqual(q * q * q, q3)); q2 = q; q2.Power(-2); Assert.IsTrue(QuaternionF.AreNumericallyEqual(q.Inverse * q.Inverse, q2)); q3 = q; q3.Power(-3); Assert.IsTrue(QuaternionF.AreNumericallyEqual(q.Inverse * q.Inverse * q.Inverse, q3)); }
public void InterpolateTest() { SrtTransform a = new SrtTransform(new Vector3F(1, 2, 3), new QuaternionF(1, 2, 3, 4).Normalized, new Vector3F(4, 5, 6)); SrtTransform b = a; var c = SrtTransform.Interpolate(a, b, 0.5f); Assert.AreEqual(a, c); b = new SrtTransform(new Vector3F(7, 9, 8), new QuaternionF(6, 6, 4, 2).Normalized, new Vector3F(-2, 4, -9)); c = SrtTransform.Interpolate(a, b, 0); Assert.AreEqual(a, c); c = SrtTransform.Interpolate(a, b, 1); Assert.AreEqual(b, c); c = SrtTransform.Interpolate(a, b, 0.3f); Assert.AreEqual(a.Translation * 0.7f + b.Translation * 0.3f, c.Translation); Assert.AreEqual(a.Scale * 0.7f + b.Scale * 0.3f, c.Scale); Assert.IsTrue(QuaternionF.AreNumericallyEqual( new QuaternionF( a.Rotation.W * 0.7f + b.Rotation.W * 0.3f, a.Rotation.V * 0.7f + b.Rotation.V * 0.3f).Normalized, c.Rotation)); }
public void CycleOffsetTest() { // IAnimationValueTraits<T> is used in a cyclic animation to a add the cycle offset in // each iteration. var traits = QuaternionTraits.Instance; var first = (Quaternion)_random.NextQuaternionF(); // Animation value of first key frame. var last = (Quaternion)_random.NextQuaternionF(); // Animation value of last key frame. var cycleOffset = traits.Add(traits.Inverse(first), last); var cycleOffsetInverse = cycleOffset; cycleOffsetInverse.Conjugate(); // Cycle offset should be the difference between last and first key frame. Assert.IsTrue(QuaternionF.AreNumericallyEqual((QuaternionF)last, (QuaternionF)traits.Add(first, cycleOffset))); Assert.IsTrue(QuaternionF.AreNumericallyEqual((QuaternionF)last, (QuaternionF)(cycleOffset * first))); // Check multiple cycles (post-loop). Assert.IsTrue(QuaternionF.AreNumericallyEqual((QuaternionF)last, (QuaternionF)traits.Add(first, traits.Multiply(cycleOffset, 1)))); Assert.IsTrue(QuaternionF.AreNumericallyEqual((QuaternionF)cycleOffset * (QuaternionF)cycleOffset * (QuaternionF)last, (QuaternionF)traits.Add(first, traits.Multiply(cycleOffset, 3)))); // Check multiple cycles (pre-loop). Assert.IsTrue(QuaternionF.AreNumericallyEqual((QuaternionF)first, (QuaternionF)traits.Add(last, traits.Multiply(cycleOffset, -1)))); Assert.IsTrue(QuaternionF.AreNumericallyEqual((QuaternionF)cycleOffsetInverse * (QuaternionF)cycleOffsetInverse * (QuaternionF)first, (QuaternionF)traits.Add(last, traits.Multiply(cycleOffset, -3)))); }
public void DivisionScalar() { float s = 123.456f; QuaternionF q = new QuaternionF(1.0f, 2.0f, 3.0f, 4.0f); QuaternionF expectedResult = new QuaternionF(1.0f / s, 2.0f / s, 3.0f / s, 4.0f / s); QuaternionF result = QuaternionF.Divide(q, s); Assert.IsTrue(QuaternionF.AreNumericallyEqual(expectedResult, result)); }
public void InterpolationTest() { var traits = QuaternionTraits.Instance; var value0 = (Quaternion)_random.NextQuaternionF(); var value1 = (Quaternion)_random.NextQuaternionF(); Assert.IsTrue(QuaternionF.AreNumericallyEqual((QuaternionF)value0, (QuaternionF)traits.Interpolate(value0, value1, 0.0f))); Assert.IsTrue(QuaternionF.AreNumericallyEqual((QuaternionF)value1, (QuaternionF)traits.Interpolate(value0, value1, 1.0f))); Assert.IsTrue(QuaternionF.AreNumericallyEqual(InterpolationHelper.Lerp((QuaternionF)value0, (QuaternionF)value1, 0.75f), (QuaternionF)traits.Interpolate(value0, value1, 0.75f))); }
public void FromMatrixWithZeroTrace() { QuaternionF q; Matrix33F m = new Matrix33F(0, 1, 0, 0, 0, 1, 1, 0, 0); q = QuaternionF.CreateRotation(m); Assert.IsTrue(QuaternionF.AreNumericallyEqual(new QuaternionF(-0.5f, 0.5f, 0.5f, 0.5f), q)); }
public void XnaQuaternionMultiplication() { QuaternionF q1 = _random.NextQuaternionF(); QuaternionF q2 = _random.NextQuaternionF(); var q1Xna = (Quaternion)q1; var q2Xna = (Quaternion)q2; Assert.IsTrue(QuaternionF.AreNumericallyEqual(q1 * q2, (QuaternionF)(q1Xna * q2Xna))); Assert.IsTrue(QuaternionF.AreNumericallyEqual(q2 * q1, (QuaternionF)(q2Xna * q1Xna))); }
public void AreEqualWithEpsilon() { float epsilon = 0.001f; QuaternionF q1 = new QuaternionF(1.0f, 2.0f, 3.0f, 4.0f); QuaternionF q2 = new QuaternionF(1.002f, 2.002f, 3.002f, 4.002f); QuaternionF q3 = new QuaternionF(1.0001f, 2.0001f, 3.0001f, 4.0001f); Assert.IsTrue(QuaternionF.AreNumericallyEqual(q1, q1, epsilon)); Assert.IsFalse(QuaternionF.AreNumericallyEqual(q1, q2, epsilon)); Assert.IsTrue(QuaternionF.AreNumericallyEqual(q1, q3, epsilon)); }
public void Angle() { Vector3F axis = new Vector3F(1.0f, 2.0f, 3.0f); QuaternionF q = QuaternionF.CreateRotation(axis, 0.4f); Assert.IsTrue(Numeric.AreEqual(0.4f, q.Angle)); q.Angle = 0.9f; Assert.IsTrue(QuaternionF.AreNumericallyEqual(q, QuaternionF.CreateRotation(axis, 0.9f))); Assert.AreEqual(0, new QuaternionF(1.000001f, 0, 0, 0).Angle); }
public void MultiplyTest() { var traits = QuaternionFTraits.Instance; var value = _random.NextQuaternionF(); Assert.IsTrue(QuaternionF.AreNumericallyEqual(QuaternionF.Identity, traits.Multiply(value, 0))); Assert.IsTrue(QuaternionF.AreNumericallyEqual(value, traits.Multiply(value, 1))); Assert.IsTrue(QuaternionF.AreNumericallyEqual(value * value, traits.Multiply(value, 2))); Assert.IsTrue(QuaternionF.AreNumericallyEqual(value * value * value, traits.Multiply(value, 3))); Assert.IsTrue(QuaternionF.AreNumericallyEqual(value.Inverse, traits.Multiply(value, -1))); Assert.IsTrue(QuaternionF.AreNumericallyEqual(value.Inverse * value.Inverse, traits.Multiply(value, -2))); Assert.IsTrue(QuaternionF.AreNumericallyEqual(value.Inverse * value.Inverse * value.Inverse, traits.Multiply(value, -3))); }
public void Power() { const float θ = 0.4f; const float t = -1.2f; Vector3F v = new Vector3F(2.3f, 1.0f, -2.0f); v.Normalize(); QuaternionF q = new QuaternionF((float)Math.Cos(θ), (float)Math.Sin(θ) * v); QuaternionF power = QuaternionF.Power(q, t); QuaternionF expected = new QuaternionF((float)Math.Cos(t * θ), (float)Math.Sin(t * θ) * v); Assert.IsTrue(QuaternionF.AreNumericallyEqual(expected, power)); }
public void AnimateUsingDefaults() { var defaultSource = _random.NextQuaternionF(); var defaultTarget = _random.NextQuaternionF(); var animation = new QuaternionFFromToByAnimation(); animation.From = null; animation.To = null; animation.By = null; Assert.AreEqual(defaultSource, animation.GetValue(TimeSpan.FromSeconds(0.0), defaultSource, defaultTarget)); Assert.IsTrue(QuaternionF.AreNumericallyEqual(InterpolationHelper.Lerp(defaultSource, defaultTarget, 0.75f), animation.GetValue(TimeSpan.FromSeconds(0.75), defaultSource, defaultTarget))); Assert.AreEqual(defaultTarget, animation.GetValue(TimeSpan.FromSeconds(1.0), defaultSource, defaultTarget)); }
public void MultiplyTest() { var traits = QuaternionTraits.Instance; var value = (Quaternion)_random.NextQuaternionF(); Quaternion valueInverse = value; valueInverse.Conjugate(); Assert.IsTrue(QuaternionF.AreNumericallyEqual((QuaternionF)Quaternion.Identity, (QuaternionF)traits.Multiply(value, 0))); Assert.IsTrue(QuaternionF.AreNumericallyEqual((QuaternionF)value, (QuaternionF)traits.Multiply(value, 1))); Assert.IsTrue(QuaternionF.AreNumericallyEqual((QuaternionF)(value * value), (QuaternionF)traits.Multiply(value, 2))); Assert.IsTrue(QuaternionF.AreNumericallyEqual((QuaternionF)(value * value * value), (QuaternionF)traits.Multiply(value, 3))); Assert.IsTrue(QuaternionF.AreNumericallyEqual((QuaternionF)valueInverse, (QuaternionF)traits.Multiply(value, -1))); Assert.IsTrue(QuaternionF.AreNumericallyEqual((QuaternionF)valueInverse * (QuaternionF)valueInverse, (QuaternionF)traits.Multiply(value, -2))); Assert.IsTrue(QuaternionF.AreNumericallyEqual((QuaternionF)valueInverse * (QuaternionF)valueInverse * (QuaternionF)valueInverse, (QuaternionF)traits.Multiply(value, -3))); }
public void AreEqual() { float originalEpsilon = Numeric.EpsilonF; Numeric.EpsilonF = 1e-8f; QuaternionF q1 = new QuaternionF(1.0f, 2.0f, 3.0f, 4.0f); QuaternionF q2 = new QuaternionF(1.000001f, 2.000001f, 3.000001f, 4.000001f); QuaternionF q3 = new QuaternionF(1.00000001f, 2.00000001f, 3.00000001f, 4.00000001f); Assert.IsTrue(QuaternionF.AreNumericallyEqual(q1, q1)); Assert.IsFalse(QuaternionF.AreNumericallyEqual(q1, q2)); Assert.IsTrue(QuaternionF.AreNumericallyEqual(q1, q3)); Numeric.EpsilonF = originalEpsilon; }
public void ShouldIgnoreByIfToIsSet() { var defaultSource = _random.NextQuaternionF(); var defaultTarget = _random.NextQuaternionF(); var to = _random.NextQuaternionF(); var by = _random.NextQuaternionF(); var animation = new QuaternionFFromToByAnimation(); animation.From = null; animation.To = to; animation.By = by; Assert.AreEqual(defaultSource, animation.GetValue(TimeSpan.FromSeconds(0.0), defaultSource, defaultTarget)); Assert.IsTrue(QuaternionF.AreNumericallyEqual(InterpolationHelper.Lerp(defaultSource, to, 0.75f), animation.GetValue(TimeSpan.FromSeconds(0.75), defaultSource, defaultTarget))); Assert.AreEqual(to, animation.GetValue(TimeSpan.FromSeconds(1.0), defaultSource, defaultTarget)); }
public void FromByTest() { // IAnimationValueTraits<T> is used in a from-by animation to a add a relative offset to // the start value. var traits = QuaternionTraits.Instance; var from = (Quaternion)_random.NextQuaternionF(); var by = (Quaternion)_random.NextQuaternionF(); var to = traits.Add(from, by); Assert.IsTrue(QuaternionF.AreNumericallyEqual((QuaternionF)by * (QuaternionF)from, (QuaternionF)to)); Assert.IsTrue(QuaternionF.AreNumericallyEqual((QuaternionF)from, (QuaternionF)traits.Add(to, traits.Inverse(by)))); Assert.IsTrue(QuaternionF.AreNumericallyEqual((QuaternionF)by, (QuaternionF)traits.Add(traits.Inverse(from), to))); }
public void SlerpSinglePrecision() { // Warning: The not all results are not verified QuaternionF q1 = new QuaternionF(1.0f, 2.0f, 3.0f, 4.0f).Normalized; QuaternionF q2 = new QuaternionF(2.0f, 4.0f, 6.0f, 8.0f).Normalized; QuaternionF slerp = InterpolationHelper.Slerp(q1, q2, 0.75f); Assert.IsTrue(slerp.IsNumericallyNormalized); slerp = InterpolationHelper.Slerp(q1, q2, 0); Assert.IsTrue(slerp.IsNumericallyNormalized); Assert.IsTrue(QuaternionF.AreNumericallyEqual(q1, slerp)); slerp = InterpolationHelper.Slerp(q1, q2, 1); Assert.IsTrue(slerp.IsNumericallyNormalized); Assert.IsTrue(QuaternionF.AreNumericallyEqual(q2, slerp)); }
public void Interpolate() { Pose p1 = new Pose(new Vector3F(1, 2, 3), QuaternionF.CreateRotationY(0.3f)); Pose p2 = new Pose(new Vector3F(-4, 5, -6), QuaternionF.CreateRotationZ(-0.1f)); Assert.IsTrue(Vector3F.AreNumericallyEqual(p1.Position, Pose.Interpolate(p1, p2, 0).Position)); Assert.IsTrue(Matrix33F.AreNumericallyEqual(p1.Orientation, Pose.Interpolate(p1, p2, 0).Orientation)); Assert.IsTrue(Vector3F.AreNumericallyEqual(p2.Position, Pose.Interpolate(p1, p2, 1).Position)); Assert.IsTrue(Matrix33F.AreNumericallyEqual(p2.Orientation, Pose.Interpolate(p1, p2, 1).Orientation)); Assert.IsTrue(Vector3F.AreNumericallyEqual(InterpolationHelper.Lerp(p1.Position, p2.Position, 0.3f), Pose.Interpolate(p1, p2, 0.3f).Position)); Assert.IsTrue( QuaternionF.AreNumericallyEqual( InterpolationHelper.Lerp(QuaternionF.CreateRotation(p1.Orientation), QuaternionF.CreateRotation(p2.Orientation), 0.3f), QuaternionF.CreateRotation(Pose.Interpolate(p1, p2, 0.3f).Orientation))); }
public void LerpQuaternionF() { // Warning: The not all results are not verified QuaternionF q1 = new QuaternionF(1.0f, 2.0f, 3.0f, 4.0f).Normalized; QuaternionF q2 = new QuaternionF(2.0f, 4.0f, 6.0f, 8.0f).Normalized; QuaternionF lerp = InterpolationHelper.Lerp(q1, q2, 0.75f); Assert.IsTrue(lerp.IsNumericallyNormalized); lerp = InterpolationHelper.Lerp(q1, q2, 0); Assert.IsTrue(QuaternionF.AreNumericallyEqual(q1, lerp)); lerp = InterpolationHelper.Lerp(q1, q2, 1); Assert.IsTrue(QuaternionF.AreNumericallyEqual(q2, lerp)); q1 = QuaternionF.Identity; q2 = QuaternionF.CreateRotation(Vector3F.UnitZ, (float)Math.PI / 2); lerp = InterpolationHelper.Lerp(q1, q2, 0.5f); Vector3F v = lerp.Rotate(Vector3F.UnitX); Vector3F result = new Vector3F(1.0f, 1.0f, 0.0f).Normalized; Assert.IsTrue(Vector3F.AreNumericallyEqual(result, v)); q1 = QuaternionF.Identity; q2 = QuaternionF.CreateRotation(Vector3F.UnitY, (float)Math.PI / 2); lerp = InterpolationHelper.Lerp(q1, q2, 0.5f); v = lerp.Rotate(Vector3F.UnitZ); result = new Vector3F(1.0f, 0.0f, 1.0f).Normalized; Assert.IsTrue(Vector3F.AreNumericallyEqual(result, v)); q1 = QuaternionF.Identity; q2 = QuaternionF.CreateRotation(Vector3F.UnitX, (float)Math.PI / 2); lerp = InterpolationHelper.Lerp(q1, q2, 0.5f); v = lerp.Rotate(Vector3F.UnitY); result = new Vector3F(0.0f, 1.0f, 1.0f).Normalized; Assert.IsTrue(Vector3F.AreNumericallyEqual(result, v)); q1 = new QuaternionF(-1.0f, 0.0f, 0.0f, 0.0f); q2 = QuaternionF.CreateRotation(-Vector3F.UnitZ, (float)-Math.PI / 2); lerp = InterpolationHelper.Lerp(q1, q2, 0.5f); v = lerp.Rotate(Vector3F.UnitX); result = new Vector3F(1.0f, 1.0f, 0.0f).Normalized; Assert.IsTrue(Vector3F.AreNumericallyEqual(result, v)); }
public void FromToMatrixTest() { var t = new Vector3F(1, 2, 3); var r = new QuaternionF(1, 2, 3, 4).Normalized; var s = new Vector3F(2, 7, 9); var m = Matrix44F.CreateTranslation(t) * Matrix44F.CreateRotation(r) * Matrix44F.CreateScale(s); var srt = SrtTransform.FromMatrix(m); Assert.IsTrue(Vector3F.AreNumericallyEqual(t, srt.Translation)); Assert.IsTrue(QuaternionF.AreNumericallyEqual(r, srt.Rotation)); Assert.IsTrue(Vector3F.AreNumericallyEqual(s, srt.Scale)); // XNA: srt = SrtTransform.FromMatrix((Matrix)m); Assert.IsTrue(Vector3F.AreNumericallyEqual(t, srt.Translation)); Assert.IsTrue(QuaternionF.AreNumericallyEqual(r, srt.Rotation)); Assert.IsTrue(Vector3F.AreNumericallyEqual(s, srt.Scale)); // With negative scale, the decomposition is not unique (many possible combinations of // axis mirroring + rotation). t = new Vector3F(1, 2, 3); r = new QuaternionF(1, 2, 3, 4).Normalized; s = new Vector3F(2, -7, 9); m = Matrix44F.CreateTranslation(t) * Matrix44F.CreateRotation(r) * Matrix44F.CreateScale(s); srt = SrtTransform.FromMatrix(m); var m2 = (Matrix44F)srt; Assert.IsTrue(Matrix44F.AreNumericallyEqual(m, m2)); m2 = srt.ToMatrix44F(); Assert.IsTrue(Matrix44F.AreNumericallyEqual(m, m2)); m2 = srt; Assert.IsTrue(Matrix44F.AreNumericallyEqual(m, m2)); Matrix mXna = srt.ToXna(); Assert.IsTrue(Matrix44F.AreNumericallyEqual(m, (Matrix44F)mXna)); mXna = srt; Assert.IsTrue(Matrix44F.AreNumericallyEqual(m, (Matrix44F)mXna)); }
public void Inverse() { QuaternionF identity = QuaternionF.Identity; QuaternionF inverseIdentity = identity.Inverse; Assert.AreEqual(inverseIdentity, identity); float angle = 0.4f; Vector3F axis = new Vector3F(1.0f, 1.0f, 1.0f); axis.Normalize(); QuaternionF q = QuaternionF.CreateRotation(axis, angle); QuaternionF inverse = q.Inverse; Assert.IsTrue(Vector3F.AreNumericallyEqual(-axis, inverse.Axis)); q = new QuaternionF(1, 2, 3, 4); inverse = q.Inverse; Assert.IsTrue(QuaternionF.AreNumericallyEqual(QuaternionF.Identity, inverse * q)); }
public void ShouldReturnTheFirstOrLastKeyFrame() { var keyFrame0 = new KeyFrame <QuaternionF>(TimeSpan.FromSeconds(1.0), _random.NextQuaternionF()); var keyFrame1 = new KeyFrame <QuaternionF>(TimeSpan.FromSeconds(2.0), _random.NextQuaternionF()); var keyFrame2 = new KeyFrame <QuaternionF>(TimeSpan.FromSeconds(3.0), _random.NextQuaternionF()); var animation = new QuaternionFKeyFrameAnimation(); animation.KeyFrames.Add(keyFrame0); animation.KeyFrames.Add(keyFrame1); animation.KeyFrames.Add(keyFrame2); var defaultSource = _random.NextQuaternionF(); var defaultTarget = _random.NextQuaternionF(); // Pre loop Assert.IsTrue(QuaternionF.AreNumericallyEqual(keyFrame0.Value, animation.GetValue(TimeSpan.FromSeconds(0.0), defaultSource, defaultTarget))); // Post loop Assert.IsTrue(QuaternionF.AreNumericallyEqual(keyFrame2.Value, animation.GetValue(TimeSpan.FromSeconds(3.75), defaultSource, defaultTarget))); }
public void AnimateFromBy() { var defaultSource = _random.NextQuaternionF(); var defaultTarget = _random.NextQuaternionF(); var from = _random.NextQuaternionF(); var by = _random.NextQuaternionF(); var animation = new QuaternionFFromToByAnimation(); animation.From = from; animation.To = null; animation.By = by; Assert.AreEqual(from, animation.GetValue(TimeSpan.FromSeconds(0.0), defaultSource, defaultTarget)); Assert.IsTrue(QuaternionF.AreNumericallyEqual(InterpolationHelper.Lerp(from, by * from, 0.75f), animation.GetValue(TimeSpan.FromSeconds(0.75), defaultSource, defaultTarget))); Assert.AreEqual(by * from, animation.GetValue(TimeSpan.FromSeconds(1.0), defaultSource, defaultTarget)); animation.By = by.Inverse; Assert.AreEqual(from, animation.GetValue(TimeSpan.FromSeconds(0.0), defaultSource, defaultTarget)); Assert.IsTrue(QuaternionF.AreNumericallyEqual(InterpolationHelper.Lerp(from, by.Inverse * from, 0.75f), animation.GetValue(TimeSpan.FromSeconds(0.75), defaultSource, defaultTarget))); Assert.AreEqual(by.Inverse * from, animation.GetValue(TimeSpan.FromSeconds(1.0), defaultSource, defaultTarget)); }
public void ConstructorTest() { var rotationQ = new QuaternionF(1, 2, 3, 4).Normalized; var srt = new SrtTransform(rotationQ.ToRotationMatrix33()); Assert.AreEqual(Vector3F.One, srt.Scale); Assert.IsTrue(QuaternionF.AreNumericallyEqual(rotationQ, srt.Rotation)); Assert.AreEqual(Vector3F.Zero, srt.Translation); srt = new SrtTransform(rotationQ); Assert.AreEqual(Vector3F.One, srt.Scale); Assert.IsTrue(QuaternionF.AreNumericallyEqual(rotationQ, srt.Rotation)); Assert.AreEqual(Vector3F.Zero, srt.Translation); srt = new SrtTransform(new Vector3F(-1, 2, -3), rotationQ.ToRotationMatrix33(), new Vector3F(10, 9, -8)); Assert.AreEqual(new Vector3F(-1, 2, -3), srt.Scale); Assert.IsTrue(QuaternionF.AreNumericallyEqual(rotationQ, srt.Rotation)); Assert.AreEqual(new Vector3F(10, 9, -8), srt.Translation); }
public void SquadSinglePrecision() { QuaternionF q0 = QuaternionF.CreateRotation(new Vector3F(1, 1, 1), 0.3f); QuaternionF q1 = QuaternionF.CreateRotation(new Vector3F(1, 0, 1), 0.4f); QuaternionF q2 = QuaternionF.CreateRotation(new Vector3F(1, 0, -1), -0.6f); QuaternionF q3 = QuaternionF.CreateRotation(new Vector3F(0, 1, 1), 0.2f); QuaternionF q, a, b, p; QuaternionF expected; InterpolationHelper.SquadSetup(q0, q1, q2, q3, out q, out a, out b, out p); // t = 0 QuaternionF result = InterpolationHelper.Squad(q, a, b, p, 0.0f); Assert.IsTrue(QuaternionF.AreNumericallyEqual(q1, result)); // t = 1.0f result = InterpolationHelper.Squad(q, a, b, p, 1.0f); Assert.IsTrue(QuaternionF.AreNumericallyEqual(q2, result)); // Check series (just for debugging) QuaternionF r1, r2, r3, r4, r5, r6, r7, r8, r9; r1 = InterpolationHelper.Squad(q, a, b, p, 0.1f); r2 = InterpolationHelper.Squad(q, a, b, p, 0.2f); r3 = InterpolationHelper.Squad(q, a, b, p, 0.3f); r4 = InterpolationHelper.Squad(q, a, b, p, 0.4f); r5 = InterpolationHelper.Squad(q, a, b, p, 0.5f); r6 = InterpolationHelper.Squad(q, a, b, p, 0.6f); r7 = InterpolationHelper.Squad(q, a, b, p, 0.7f); r8 = InterpolationHelper.Squad(q, a, b, p, 0.8f); r9 = InterpolationHelper.Squad(q, a, b, p, 0.9f); // q0 = q1, q2 = q3 InterpolationHelper.SquadSetup(q1, q1, q2, q2, out q, out a, out b, out p); result = InterpolationHelper.Squad(q, a, b, p, 0.5f); expected = InterpolationHelper.Slerp(q1, q2, 0.5f); Assert.IsTrue(QuaternionF.AreNumericallyEqual(expected, result)); }
public void BlendTest() { var traits = SrtTransformTraits.Instance; var value0 = NextRandomValue(); var value1 = NextRandomValue(); var value2 = NextRandomValue(); var w0 = 0.3f; var w1 = 0.4f; var w2 = 1 - w0 - w1; SrtTransform result = new SrtTransform(); traits.BeginBlend(ref result); traits.BlendNext(ref result, ref value0, w0); traits.BlendNext(ref result, ref value1, w1); traits.BlendNext(ref result, ref value2, w2); traits.EndBlend(ref result); Assert.IsTrue(Vector3F.AreNumericallyEqual(value0.Scale * w0 + value1.Scale * w1 + value2.Scale * w2, result.Scale)); Assert.IsTrue(Vector3F.AreNumericallyEqual(value0.Translation * w0 + value1.Translation * w1 + value2.Translation * w2, result.Translation)); QuaternionF expected; expected = value0.Rotation * w0; // Consider "selective negation" when blending quaternions! if (QuaternionF.Dot(expected, value1.Rotation) < 0) { value1.Rotation = -value1.Rotation; } expected += value1.Rotation * w1; if (QuaternionF.Dot(expected, value2.Rotation) < 0) { value2.Rotation = -value2.Rotation; } expected += value2.Rotation * w2; expected.Normalize(); Assert.IsTrue(QuaternionF.AreNumericallyEqual(expected, result.Rotation)); }
public void CreateRotation() { QuaternionF q; // From matrix vs. from angle/axis Matrix33F m = Matrix33F.CreateRotation(Vector3F.UnitX, (float)Math.PI / 4); q = QuaternionF.CreateRotation(m); QuaternionF q2 = QuaternionF.CreateRotation(Vector3F.UnitX, (float)Math.PI / 4); Assert.IsTrue(QuaternionF.AreNumericallyEqual(q2, q)); m = Matrix33F.CreateRotation(Vector3F.UnitY, (float)Math.PI / 4); q = QuaternionF.CreateRotation(m); q2 = QuaternionF.CreateRotation(Vector3F.UnitY, (float)Math.PI / 4); Assert.IsTrue(QuaternionF.AreNumericallyEqual(q2, q)); m = Matrix33F.CreateRotation(Vector3F.UnitZ, (float)Math.PI / 4); q = QuaternionF.CreateRotation(m); q2 = QuaternionF.CreateRotation(Vector3F.UnitZ, (float)Math.PI / 4); Assert.IsTrue(QuaternionF.AreNumericallyEqual(q2, q)); // From vector-vector Vector3F start, end; start = Vector3F.UnitX; end = Vector3F.UnitY; q = QuaternionF.CreateRotation(start, end); Assert.IsTrue(Vector3F.AreNumericallyEqual(end, q.ToRotationMatrix33() * start)); start = Vector3F.UnitY; end = Vector3F.UnitZ; q = QuaternionF.CreateRotation(start, end); Assert.IsTrue(Vector3F.AreNumericallyEqual(end, q.ToRotationMatrix33() * start)); start = Vector3F.UnitZ; end = Vector3F.UnitX; q = QuaternionF.CreateRotation(start, end); Assert.IsTrue(Vector3F.AreNumericallyEqual(end, q.ToRotationMatrix33() * start)); start = new Vector3F(1, 1, 1); end = new Vector3F(1, 1, 1); q = QuaternionF.CreateRotation(start, end); Assert.IsTrue(Vector3F.AreNumericallyEqual(end, q.ToRotationMatrix33() * start)); start = new Vector3F(1.0f, 1.0f, 1.0f); end = new Vector3F(-1.0f, -1.0f, -1.0f); q = QuaternionF.CreateRotation(start, end); Assert.IsTrue(Vector3F.AreNumericallyEqual(end, q.ToRotationMatrix33() * start)); start = new Vector3F(-1.0f, 2.0f, 1.0f); end = new Vector3F(-2.0f, -1.0f, -1.0f); q = QuaternionF.CreateRotation(start, end); Assert.IsTrue(Vector3F.AreNumericallyEqual(end, q.ToRotationMatrix33() * start)); float degree45 = MathHelper.ToRadians(45); q = QuaternionF.CreateRotation(degree45, Vector3F.UnitZ, degree45, Vector3F.UnitY, degree45, Vector3F.UnitX, false); QuaternionF expected = QuaternionF.CreateRotation(Vector3F.UnitZ, degree45) * QuaternionF.CreateRotation(Vector3F.UnitY, degree45) * QuaternionF.CreateRotation(Vector3F.UnitX, degree45); Assert.IsTrue(QuaternionF.AreNumericallyEqual(expected, q)); q = QuaternionF.CreateRotation(degree45, Vector3F.UnitZ, degree45, Vector3F.UnitY, degree45, Vector3F.UnitX, true); expected = QuaternionF.CreateRotation(Vector3F.UnitX, degree45) * QuaternionF.CreateRotation(Vector3F.UnitY, degree45) * QuaternionF.CreateRotation(Vector3F.UnitZ, degree45); Assert.IsTrue(QuaternionF.AreNumericallyEqual(expected, q)); }
/// <summary> /// Compresses the specified animation using simple lossy compression algorithm. /// </summary> /// <param name="animation">The animation.</param> /// <param name="scaleThreshold">The scale threshold.</param> /// <param name="rotationThreshold">The rotation threshold in degrees.</param> /// <param name="translationThreshold">The translation threshold.</param> /// <returns> /// The compressed animation. Or <see langword="null"/> if the animation does contain any /// key frames. /// </returns> /// <remarks> /// <para> /// This method takes an <see cref="SrtKeyFrameAnimation"/> and removes not needed scale, /// rotation or translation channels. It further removes key frames that can be interpolated /// from the neighbor key frames. This a lossy compression and the threshold parameters define /// the allowed errors. If the thresholds are 0 or negative, this compression is lossless. If /// the thresholds are greater than 0 (recommended), the compression is lossy. The best way to /// determine optimal thresholds is to compare the compressed animation with the uncompressed /// animation visually. /// </para> /// </remarks> /// <exception cref="ArgumentNullException"> /// <paramref name="animation" /> is <see langword="null"/>. /// </exception> public static SrtAnimation Compress(SrtKeyFrameAnimation animation, float scaleThreshold, float rotationThreshold, float translationThreshold) { if (animation == null) { throw new ArgumentNullException("animation"); } var keyFrames = animation.KeyFrames; if (keyFrames.Count == 0) { // Empty animation. return(null); } var animationEx = new SrtAnimation { FillBehavior = animation.FillBehavior, IsAdditive = animation.IsAdditive, TargetObject = animation.TargetObject, TargetProperty = animation.TargetProperty, }; Vector3FKeyFrameAnimation scaleAnimation = null; QuaternionFKeyFrameAnimation rotationAnimation = null; Vector3FKeyFrameAnimation translationAnimation = null; // Create Scale channel if required. foreach (var keyFrame in keyFrames) { if (!Vector3F.AreNumericallyEqual(keyFrame.Value.Scale, Vector3F.One)) { scaleAnimation = new Vector3FKeyFrameAnimation(); break; } } // Create Rotation channel if required. foreach (var keyFrame in keyFrames) { if (!QuaternionF.AreNumericallyEqual(keyFrame.Value.Rotation, QuaternionF.Identity)) { rotationAnimation = new QuaternionFKeyFrameAnimation(); break; } } // Create Translation channel if required. foreach (var keyFrame in keyFrames) { if (!keyFrame.Value.Translation.IsNumericallyZero) { translationAnimation = new Vector3FKeyFrameAnimation(); break; } } if (scaleAnimation == null && rotationAnimation == null && translationAnimation == null) { // The animation does not contain any transformations. However, the keyframe times (start // and end) may be relevant. translationAnimation = new Vector3FKeyFrameAnimation(); } if (keyFrames.Count <= 2) { // Add first keyframe. { Debug.Assert(keyFrames.Count > 0); var keyFrame = keyFrames[0]; if (scaleAnimation != null) { scaleAnimation.KeyFrames.Add(new KeyFrame <Vector3F>(keyFrame.Time, keyFrame.Value.Scale)); } if (rotationAnimation != null) { rotationAnimation.KeyFrames.Add(new KeyFrame <QuaternionF>(keyFrame.Time, keyFrame.Value.Rotation)); } if (translationAnimation != null) { translationAnimation.KeyFrames.Add(new KeyFrame <Vector3F>(keyFrame.Time, keyFrame.Value.Translation)); } } // Add second (last) keyframe. if (keyFrames.Count > 1) { Debug.Assert(keyFrames.Count == 2); var keyFrame = keyFrames[1]; if (scaleAnimation != null) { scaleAnimation.KeyFrames.Add(new KeyFrame <Vector3F>(keyFrame.Time, keyFrame.Value.Scale)); } if (rotationAnimation != null) { rotationAnimation.KeyFrames.Add(new KeyFrame <QuaternionF>(keyFrame.Time, keyFrame.Value.Rotation)); } if (translationAnimation != null) { translationAnimation.KeyFrames.Add(new KeyFrame <Vector3F>(keyFrame.Time, keyFrame.Value.Translation)); } } } else { // Animation has more than 2 keyframes. // --> Compress animation. if (scaleAnimation != null) { Compress(animation, scaleAnimation, scaleThreshold, keyFrame => keyFrame.Value.Scale, ComputeError); } if (rotationAnimation != null) { Compress(animation, rotationAnimation, MathHelper.ToRadians(rotationThreshold), keyFrame => keyFrame.Value.Rotation, ComputeError); } if (translationAnimation != null) { Compress(animation, translationAnimation, translationThreshold, keyFrame => keyFrame.Value.Translation, ComputeError); } } animationEx.Scale = scaleAnimation; animationEx.Rotation = rotationAnimation; animationEx.Translation = translationAnimation; return(animationEx); }