/// <summary> /// Calculates the determinant of two vectors. /// </summary> /// <param name="vector1">The first vector to evaluate.</param> /// <param name="vector2">The second vector to evaluate.</param> /// <returns>The determinant of vector1 and vector2.</returns> public static float Determinant(VectorF vector1, VectorF vector2) { // In the case of two 2D vectors, the determinant is the cross-product. return CrossProduct(vector1, vector2); }
/// <summary> /// Retrieves the angle, expressed in radians, between the two specified vectors. /// </summary> /// <param name="vector1">The first vector to evaluate.</param> /// <param name="vector2">The second vector to evaluate.</param> /// <returns>The angle, in radians, between vector1 and vector2.</returns> public static float AngleBetween(VectorF vector1, VectorF vector2) { vector1.Normalize(); vector2.Normalize(); double angle = Math.Atan2(vector2.y, vector2.x) - Math.Atan2(vector1.y, vector1.x); if (angle > Math.PI) { angle -= Math.PI * 2.0; } else if (angle < -Math.PI) { angle += Math.PI * 2.0; } return (float)angle; }
/// <summary> /// Calculates the dot product of the two specified vectors and returns the result. /// </summary> /// <param name="vector1">The first vector to multiply.</param> /// <param name="vector2">The second vector structure to multiply.</param> /// <returns>The scalar dot product of vector1 and vector2, which is calculated /// using the following formula: (vector1.X * vector2.X) + (vector1.Y * vector2.Y)</returns> public static float Multiply(VectorF vector1, VectorF vector2) { return vector1 * vector2; }
/// <summary> /// Calculates the cross product of two vectors. /// </summary> /// <param name="vector1">The first vector to evaluate.</param> /// <param name="vector2">The second vector to evaluate.</param> /// <returns>The cross product of vector1 and vector2. The following formula is used to /// calculate the cross product: (Vector1.X * Vector2.Y) - (Vector1.Y * Vector2.X)</returns> public static float CrossProduct(VectorF vector1, VectorF vector2) { return (vector1.x * vector2.y) - (vector1.y * vector2.x); }
/// <summary> /// Multiplies the specified scalar by the specified vector and returns the resulting vector. /// </summary> /// <param name="vector">The vector to multiply.</param> /// <param name="scalar">The scalar to multiply.</param> /// <returns>The result of multiplying scalar and vector.</returns> public static VectorF Multiply(VectorF vector, float scalar) { return vector * scalar; }
/// <summary> /// Divides the specified vector by the specified scalar and returns the result. /// </summary> /// <param name="vector">The vector structure to divide.</param> /// <param name="scalar">The amount by which vector is divided.</param> /// <returns>The result of dividing vector by scalar.</returns> public static VectorF Divide(VectorF vector, float scalar) { return vector / scalar; }
/// <summary> /// Subtracts the specified vector from another specified vector. /// </summary> /// <param name="vector1">The vector from which vector2 is subtracted.</param> /// <param name="vector2">The vector to subtract from vector1.</param> /// <returns>The difference between vector1 and vector2.</returns> public static VectorF Subtract(VectorF vector1, VectorF vector2) { return vector1 - vector2; }
/// <summary> /// Multiplies the specified scalar by the specified vector and returns the resulting vector. /// </summary> /// <param name="scalar">The scalar to multiply.</param> /// <param name="vector">The vector to multiply.</param> /// <returns>The result of multiplying scalar and vector.</returns> public static VectorF Multiply(float scalar, VectorF vector) { return scalar * vector; }
/// <summary> /// Adds two vectors and returns the result. /// </summary> /// <param name="vector1">The first vector to add.</param> /// <param name="vector2">The second vector to add.</param> /// <returns> The sum of vector1 and vector2.</returns> public static VectorF Add(VectorF vector1, VectorF vector2) { return vector1 + vector2; }
/// <summary> /// Translates the specified point by the specified vector and returns the resulting point. /// </summary> /// <param name="vector">The amount to translate the specified point.</param> /// <param name="point">The point to translate.</param> /// <returns>The result of translating point by vector.</returns> public static PointF Add(VectorF vector, PointF point) { return vector + point; }
/// <summary> /// Compares two vectors for equality. /// </summary> /// <param name="value">The vector to compare with this vector.</param> /// <returns>true if value has the same X and Y values as this vector; /// otherwise, false.</returns> public bool Equals(VectorF value) { return value == this; }
/// <summary> /// Compares the two specified vectors for equality. /// </summary> /// <param name="vector1">The first vector to compare.</param> /// <param name="vector2">The second vector to compare.</param> /// <returns>true if the X and Y components of vector1 and vector2 are equal; /// otherwise, false.</returns> public static bool Equals(VectorF vector1, VectorF vector2) { return vector1 == vector2; }
/// <summary> /// Start the inertia processor in Surface screen space. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void OnAffine2DManipulationCompleted(object sender, Manipulation2DCompletedEventArgs e) { if (inertiaProcessor != null) { inertiaProcessor.Delta -= OnAffine2DInertiaDelta; inertiaProcessor.Completed -= OnAffine2DInertiaCompleted; inertiaProcessor.Complete(stopwatch.Elapsed100Nanoseconds()); } VectorF translation = new VectorF(e.Total.TranslationX, e.Total.TranslationY); // Don't start inertia if the tranlation is less than roughly 1/8" regardless of velocity. if (translation.Length < 96 / 8f) { return; } // Scroll in the opposite direction because a ScrollViewer moves with the touch. VectorF initialVelocity = new VectorF(-e.Velocities.LinearVelocityX, -e.Velocities.LinearVelocityY); // Check velocity. if (initialVelocity.Length < FlickUtilities.MinimumFlickVelocity) { // If velocity is below this threshold ignore it. return; } if (initialVelocity.Length >= FlickUtilities.MaximumFlickVelocity) { // If velocity is too large, reduce it to a reasonable value. initialVelocity.Normalize(); initialVelocity = FlickUtilities.MaximumFlickVelocity * initialVelocity; } processInertia = true; inertiaProcessor = new InertiaProcessor2D(); inertiaProcessor.Delta += OnAffine2DInertiaDelta; inertiaProcessor.Completed += OnAffine2DInertiaCompleted; inertiaProcessor.TranslationBehavior = new InertiaTranslationBehavior2D { DesiredDisplacement = ConvertFromVerticalValueToScreenSpace(VerticalViewportSize), InitialVelocityX = initialVelocity.X * HorizontalViewportSize, InitialVelocityY = initialVelocity.Y * VerticalViewportSize }; inertiaProcessor.InitialOriginX = ConvertFromHorizontalValueToScreenSpace(horizontalScrollBarStateMachine.Value); inertiaProcessor.InitialOriginY = ConvertFromVerticalValueToScreenSpace(verticalScrollBarStateMachine.Value); }
/// <summary> /// Start the flicking behavior. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void OnAffine2DManipulationCompleted(object sender, Affine2DOperationCompletedEventArgs e) { if (inertiaProcessor != null) { inertiaProcessor.Affine2DInertiaDelta -= OnAffine2DInertiaDelta; inertiaProcessor.Affine2DInertiaCompleted -= OnAffine2DInertiaCompleted; inertiaProcessor = null; } // The Manipulations should all run in screen space so don't convert between spaces. VectorF initialVelocity; VectorF maxViewPortSize; if (Orientation == Orientation.Horizontal) { initialVelocity = new VectorF(e.VelocityX, 0); maxViewPortSize = new VectorF(numberOfPixelsInAxis * viewportSize, 0); } else { initialVelocity = new VectorF(0, e.VelocityY); maxViewPortSize = new VectorF(0, numberOfPixelsInAxis * viewportSize); } // Check velocity. if (initialVelocity.Length < FlickUtilities.MinimumFlickVelocity) { return; } if (initialVelocity.Length >= MaximumFlickVelocity) { // If velocity is too large, reduce it to a reasonable value. initialVelocity.Normalize(); initialVelocity = MaximumFlickVelocity * initialVelocity; } VectorF flickDistance; if (FlickUtilities.TryGetFlickDistance(initialVelocity, out flickDistance, maxViewPortSize)) { processInertia = true; inertiaProcessor = new Affine2DInertiaProcessor(); inertiaProcessor.Affine2DInertiaDelta += OnAffine2DInertiaDelta; inertiaProcessor.Affine2DInertiaCompleted += OnAffine2DInertiaCompleted; float displacement = 0; if (flickDistance.X == 0) { displacement = flickDistance.Y; } else if (flickDistance.Y == 0) { displacement = flickDistance.X; } else { displacement = Math.Min(flickDistance.X, flickDistance.Y); } inertiaProcessor.DesiredDisplacement = float.IsInfinity(displacement) ? 0 : Math.Abs(displacement); inertiaProcessor.InitialTimestamp = stopwatch.ElapsedTicks; inertiaProcessor.InitialVelocityX = initialVelocity.X; inertiaProcessor.InitialVelocityY = initialVelocity.Y; inertiaProcessor.InitialOriginX = ToScreenSpace(Value); inertiaProcessor.InitialOriginY = ToScreenSpace(Value); } }
/// <summary> /// Calculates the page size for a flick with a given velocity. /// The method chooses vertical or horizontal direction depending on the /// given initial velocity. /// </summary> /// <param name="velocity">flick velocity in device independent units per msec.</param> /// <param name="flickDistance">flick page size in device independent units.</param> /// <param name="maxViewportSize">The maximum size of the viewport.</param> /// <returns></returns> public static bool TryGetFlickDistance(VectorF velocity, out VectorF flickDistance, VectorF maxViewportSize) { Debug.Assert(!float.IsNaN(velocity.X) && !float.IsInfinity(velocity.X), "Cannot start flick with invalid velocity.X"); Debug.Assert(!float.IsNaN(velocity.Y) && !float.IsInfinity(velocity.Y), "Cannot start flick with invalid velocity.Y"); // Set the flickDistance to the maximum viewport size. flickDistance = maxViewportSize; // choose the main direction of the flick based on duration float durationX = float.PositiveInfinity; float durationY = float.PositiveInfinity; if (Math.Abs(velocity.X) > float.Epsilon && !float.IsInfinity(flickDistance.X)) { durationX = GetDurationGivenDistanceAndVelocity(flickDistance.X, velocity.X); } if (Math.Abs(velocity.Y) > float.Epsilon && !float.IsInfinity(flickDistance.Y)) { durationY = GetDurationGivenDistanceAndVelocity(flickDistance.Y, velocity.Y); } // choose the minimum duration float duration = Math.Min(durationX, durationY); if (float.IsInfinity(duration)) { // cannot calculate flick page size return false; } // make sure that duration doesn't exceed MaximumFlickDuration, if (duration > MaximumFlickDuration) { // recalculate duration for the 'flick' direction if (durationX <= durationY) { // calculate deceleration based on X page size and MaximumFlickDuration Debug.Assert(flickDistance.X > 0); float deceleration = GetDecelerationGivenDistanceAndDuration(flickDistance.X, MaximumFlickDuration); duration = GetDurationGivenVelocityAndDeceleration(velocity.X, deceleration); } else { // calculate deceleration based on Y page size and MaximumFlickDuration Debug.Assert(flickDistance.Y > 0); float deceleration = GetDecelerationGivenDistanceAndDuration(flickDistance.Y, MaximumFlickDuration); duration = GetDurationGivenVelocityAndDeceleration(velocity.Y, deceleration); } Debug.Assert(duration <= MaximumFlickDuration, "Duration is supposed to be less than MaximumFlickDuration."); } // adjust the flick distance if (duration < durationX) { flickDistance.X = Math.Abs(GetDistanceGivenVelocityAndDuration(velocity.X, duration)); } if (duration < durationY) { flickDistance.Y = Math.Abs(GetDistanceGivenVelocityAndDuration(velocity.Y, duration)); } return true; }