mixedreality/com.microsoft.mixedreality..../Core/Utilities/Lines/LineUtility.cs

221 lines
10 KiB
C#

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit.Utilities
{
/// <summary>
/// Mixed Reality line utility class with helpful math functions for calculation, and other convenience methods.
/// </summary>
public static class LineUtility
{
/// <summary>
/// Inverts the color
/// </summary>
public static Color Invert(this Color color)
{
color.r = 1.0f - color.r;
color.g = 1.0f - color.g;
color.b = 1.0f - color.b;
return color;
}
/// <summary>
/// Returns a blended value from a collection of vectors
/// </summary>
/// <param name="vectorCollection">The collection to use to calculate the blend.</param>
/// <param name="normalizedLength">the normalized length along the line to calculate the point.</param>
/// <returns>The calculated point found along the normalized length.</returns>
public static Vector3 GetVectorCollectionBlend(Vector3[] vectorCollection, float normalizedLength, bool repeat)
{
if (vectorCollection.Length == 0)
{
return Vector3.zero;
}
if (vectorCollection.Length == 1)
{
return vectorCollection[0];
}
float arrayValueLength = 1f / vectorCollection.Length;
int indexA = Mathf.FloorToInt(normalizedLength * vectorCollection.Length);
if (indexA >= vectorCollection.Length)
{
indexA = repeat ? 0 : vectorCollection.Length - 1;
}
int indexB = indexA + 1;
if (indexB >= vectorCollection.Length)
{
indexB = repeat ? 0 : vectorCollection.Length - 1;
}
float blendAmount = (normalizedLength - (arrayValueLength * indexA)) / arrayValueLength;
return Vector3.Lerp(vectorCollection[indexA], vectorCollection[indexB], blendAmount);
}
/// <summary>
/// Gets the point along a physics based parabola.
/// </summary>
/// <param name="origin">The point in space where the parabola starts</param>
/// <param name="direction">The direction the line is intended to go</param>
/// <returns>The calculated point.</returns>
public static Vector3 GetPointAlongPhysicalParabola(Vector3 origin, Vector3 direction, float velocity, Vector3 gravity, float time)
{
direction = Vector3.Normalize(direction);
origin.x += ((direction.x * velocity * time) + (0.5f * gravity.x * (time * time)));
origin.y += ((direction.y * velocity * time) + (0.5f * gravity.y * (time * time)));
origin.z += ((direction.z * velocity * time) + (0.5f * gravity.z * (time * time)));
return origin;
}
/// <summary>
/// Gets the point along a constrained parabola.
/// </summary>
/// <param name="origin">The point in space where the parabola starts.</param>
/// <param name="end">The point in space where the parabola ends.</param>
/// <param name="upDirection">The up direction of the arc.</param>
/// <param name="height">The height of the arc.</param>
/// <param name="normalizedLength">the normalized length along the line to calculate the point.</param>
/// <returns>The calculated point found along the normalized length.</returns>
public static Vector3 GetPointAlongConstrainedParabola(Vector3 origin, Vector3 end, Vector3 upDirection, float height, float normalizedLength)
{
float parabolaTime = normalizedLength * 2 - 1;
Vector3 direction = end - origin;
Vector3 pos = origin + normalizedLength * direction;
pos += ((-parabolaTime * parabolaTime + 1) * height) * upDirection.normalized;
return pos;
}
/// <summary>
/// Gets the point along the spline.
/// </summary>
/// <param name="points">the points of the whole spline.</param>
/// <param name="normalizedLength">the normalized length along the line to calculate the point.</param>
/// <param name="interpolation">Optional Interpolation type to use when calculating the point.</param>
/// <returns>The calculated point found along the normalized length.</returns>
public static Vector3 GetPointAlongSpline(MixedRealityPose[] points, float normalizedLength, InterpolationType interpolation = InterpolationType.Bezier)
{
int pointIndex = (Mathf.RoundToInt(normalizedLength * points.Length));
float length = normalizedLength - ((float)pointIndex / points.Length);
if (pointIndex + 3 >= points.Length)
{
return points[points.Length - 1].Position;
}
if (pointIndex < 0)
{
return points[0].Position;
}
Vector3 point1 = points[pointIndex].Position;
Vector3 point2 = points[pointIndex + 1].Position;
Vector3 point3 = points[pointIndex + 2].Position;
Vector3 point4 = points[pointIndex + 3].Position;
switch (interpolation)
{
case InterpolationType.CatmullRom:
return InterpolateCatmullRomPoints(point1, point2, point3, point4, length);
default:
return InterpolateBezierPoints(point1, point2, point3, point4, length);
}
}
/// <summary>
/// Interpolate a position between the provided points.
/// </summary>
/// <param name="points">The points to use in the calculation.</param>
/// <param name="normalizedLength">the normalized length along the line to calculate the point.</param>
/// <returns>The calculated point found along the normalized length.</returns>
public static Vector3 InterpolateVectorArray(Vector3[] points, float normalizedLength)
{
float arrayValueLength = 1f / points.Length;
int indexA = Mathf.FloorToInt(normalizedLength * points.Length);
if (indexA >= points.Length)
{
indexA = 0;
}
int indexB = indexA + 1;
if (indexB >= points.Length)
{
indexB = 0;
}
float blendAmount = (normalizedLength - (arrayValueLength * indexA)) / arrayValueLength;
return Vector3.Lerp(points[indexA], points[indexB], blendAmount);
}
/// <summary>
/// Interpolate the provided points using Catmull Rom algorithm.
/// </summary>
/// <param name="normalizedLength">the normalized length along the line to calculate the point.</param>
/// <returns>The calculated point found along the normalized length.</returns>
public static Vector3 InterpolateCatmullRomPoints(Vector3 point1, Vector3 point2, Vector3 point3, Vector3 point4, float normalizedLength)
{
Vector3 p1 = 2f * point2;
Vector3 p2 = point3 - point1;
Vector3 p3 = 2f * point1 - 5f * point2 + 4f * point3 - point4;
Vector3 p4 = -point1 + 3f * point2 - 3f * point3 + point4;
return 0.5f * (p1 + (p2 * normalizedLength) + (p3 * normalizedLength * normalizedLength) + (p4 * normalizedLength * normalizedLength * normalizedLength));
}
/// <summary>
/// Interpolate the provided points using the standard Bezier algorithm.
/// </summary>
/// <param name="normalizedLength">the normalized length along the line to calculate the point.</param>
/// <returns>The calculated point found along the normalized length.</returns>
public static Vector3 InterpolateBezierPoints(Vector3 point1, Vector3 point2, Vector3 point3, Vector3 point4, float normalizedLength)
{
float invertedDistance = 1f - normalizedLength;
return invertedDistance * invertedDistance * invertedDistance * point1 +
3f * invertedDistance * invertedDistance * normalizedLength * point2 +
3f * invertedDistance * normalizedLength * normalizedLength * point3 +
normalizedLength * normalizedLength * normalizedLength * point4;
}
/// <summary>
/// Interpolate the provided points using the Hermite algorithm.
/// </summary>
/// <param name="normalizedLength">the normalized length along the line to calculate the point.</param>
/// <returns>The calculated point found along the normalized length.</returns>
public static Vector3 InterpolateHermitePoints(Vector3 point1, Vector3 point2, Vector3 point3, Vector3 point4, float normalizedLength)
{
float invertedDistance = 1f - normalizedLength;
return invertedDistance * invertedDistance * invertedDistance * point1 +
3f * invertedDistance * invertedDistance * normalizedLength * point2 +
3f * invertedDistance * normalizedLength * normalizedLength * point3 +
normalizedLength * normalizedLength * normalizedLength * point4;
}
/// <summary>
/// Calculate the ellipse point at the angle provided.
/// </summary>
/// <param name="radius">The radius of the ellipse.</param>
/// <param name="angle">Angle along the ellipse to find the point.</param>
/// <returns>The calculated point at the specified angle.</returns>
public static Vector3 GetEllipsePoint(Vector2 radius, float angle)
{
cachedEllipsePoint.x = radius.x * Mathf.Cos(angle);
cachedEllipsePoint.y = radius.y * Mathf.Sin(angle);
cachedEllipsePoint.z = 0.0f;
return cachedEllipsePoint;
}
/// <summary>
/// Used to calculate the ellipse point in <see cref="GetEllipsePoint"/>
/// </summary>
private static Vector3 cachedEllipsePoint = Vector3.zero;
}
}