mixedreality/com.microsoft.mixedreality..../Core/Utilities/Lines/DataProviders/SplineDataProvider.cs

279 lines
9.8 KiB
C#

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit.Utilities
{
/// <summary>
/// Creates a spline based on control points.
/// </summary>
[AddComponentMenu("Scripts/MRTK/Core/SplineDataProvider")]
public class SplineDataProvider : BaseMixedRealityLineDataProvider
{
[Tooltip("List of positions and orientations that define control points to generate the spline")]
[SerializeField]
private MixedRealityPose[] controlPoints =
{
MixedRealityPose.ZeroIdentity,
new MixedRealityPose(new Vector3(0.5f, 0.5f, 0f), Quaternion.identity),
new MixedRealityPose(new Vector3(0.5f, -0.5f, 0f), Quaternion.identity),
new MixedRealityPose(Vector3.right, Quaternion.identity),
};
/// <summary>
/// List of positions and orientations that define control points to generate the spline
/// </summary>
public MixedRealityPose[] ControlPoints => controlPoints;
[SerializeField]
private bool alignAllControlPoints = true;
public bool AlignAllControlPoints
{
get => alignAllControlPoints;
set
{
if (alignAllControlPoints != value)
{
alignAllControlPoints = value;
ForceUpdateAlignment();
}
}
}
/// <summary>
/// Forces all the control points into alignment.
/// </summary>
public void ForceUpdateAlignment()
{
if (AlignAllControlPoints)
{
for (int i = 0; i < PointCount; i++)
{
UpdatePointAlignment(i);
}
}
}
private void UpdatePointAlignment(int pointIndex)
{
if (AlignAllControlPoints)
{
int prevControlPoint;
int changedControlPoint;
int midPointIndex = ((pointIndex + 1) / 3) * 3;
if (pointIndex <= midPointIndex)
{
prevControlPoint = midPointIndex - 1;
changedControlPoint = midPointIndex + 1;
}
else
{
prevControlPoint = midPointIndex + 1;
changedControlPoint = midPointIndex - 1;
}
if (Loops)
{
if (changedControlPoint < 0)
{
changedControlPoint = (PointCount - 1) + changedControlPoint;
}
else if (changedControlPoint >= PointCount)
{
changedControlPoint %= (PointCount - 1);
}
if (prevControlPoint < 0)
{
prevControlPoint = (PointCount - 1) + prevControlPoint;
}
else if (prevControlPoint >= PointCount)
{
prevControlPoint %= (PointCount - 1);
}
Vector3 midPoint = controlPoints[midPointIndex].Position;
Vector3 tangent = midPoint - controlPoints[prevControlPoint].Position;
tangent = tangent.normalized * Vector3.Distance(midPoint, controlPoints[changedControlPoint].Position);
controlPoints[changedControlPoint].Position = midPoint + tangent;
}
else if (changedControlPoint >= 0 && changedControlPoint < PointCount && prevControlPoint >= 0 && prevControlPoint < PointCount)
{
Vector3 midPoint = controlPoints[midPointIndex].Position;
Vector3 tangent = midPoint - controlPoints[prevControlPoint].Position;
tangent = tangent.normalized * Vector3.Distance(midPoint, controlPoints[changedControlPoint].Position);
controlPoints[changedControlPoint].Position = midPoint + tangent;
}
}
}
#region BaseMixedRealityLineDataProvider Implementation
/// <inheritdoc />
public override int PointCount => controlPoints.Length;
/// <inheritdoc />
protected override Vector3 GetPointInternal(float normalizedDistance)
{
var totalDistance = normalizedDistance * (PointCount - 1);
int point1Index = Mathf.FloorToInt(totalDistance);
point1Index -= point1Index % 3;
float subDistance = (totalDistance - point1Index) / 3;
int point2Index;
int point3Index;
int point4Index;
if (!Loops)
{
if (point1Index + 3 >= PointCount)
{
return controlPoints[PointCount - 1].Position;
}
if (point1Index < 0)
{
return controlPoints[0].Position;
}
point2Index = point1Index + 1;
point3Index = point1Index + 2;
point4Index = point1Index + 3;
}
else
{
point2Index = (point1Index + 1) % (PointCount - 1);
point3Index = (point1Index + 2) % (PointCount - 1);
point4Index = (point1Index + 3) % (PointCount - 1);
}
Vector3 point1 = controlPoints[point1Index].Position;
Vector3 point2 = controlPoints[point2Index].Position;
Vector3 point3 = controlPoints[point3Index].Position;
Vector3 point4 = controlPoints[point4Index].Position;
return LineUtility.InterpolateBezierPoints(point1, point2, point3, point4, subDistance);
}
/// <inheritdoc />
protected override Vector3 GetPointInternal(int pointIndex)
{
if (pointIndex < 0 || pointIndex >= controlPoints.Length)
{
Debug.LogError("Invalid point index!");
return Vector3.zero;
}
if (Loops && pointIndex == PointCount - 1)
{
controlPoints[pointIndex] = controlPoints[0];
pointIndex = 0;
}
return controlPoints[pointIndex].Position;
}
/// <inheritdoc />
protected override void SetPointInternal(int pointIndex, Vector3 point)
{
if (pointIndex < 0 || pointIndex >= controlPoints.Length)
{
Debug.LogError("Invalid point index!");
return;
}
if (Loops && pointIndex == PointCount - 1)
{
controlPoints[pointIndex] = controlPoints[0];
pointIndex = 0;
}
if (AlignAllControlPoints)
{
if (pointIndex % 3 == 0)
{
Vector3 delta = point - controlPoints[pointIndex].Position;
if (Loops)
{
if (pointIndex == 0)
{
controlPoints[1].Position += delta;
controlPoints[PointCount - 2].Position += delta;
controlPoints[PointCount - 1].Position = point;
}
else if (pointIndex == PointCount)
{
controlPoints[0].Position = point;
controlPoints[1].Position += delta;
controlPoints[pointIndex - 1].Position += delta;
}
else
{
controlPoints[pointIndex - 1].Position += delta;
controlPoints[pointIndex + 1].Position += delta;
}
}
else
{
if (pointIndex > 0)
{
controlPoints[pointIndex - 1].Position += delta;
}
if (pointIndex + 1 < controlPoints.Length)
{
controlPoints[pointIndex + 1].Position += delta;
}
}
}
}
controlPoints[pointIndex].Position = point;
UpdatePointAlignment(pointIndex);
}
/// <inheritdoc />
protected override Vector3 GetUpVectorInternal(float normalizedLength)
{
float arrayValueLength = 1f / controlPoints.Length;
int indexA = Mathf.FloorToInt(normalizedLength * controlPoints.Length);
if (indexA >= controlPoints.Length)
{
indexA = 0;
}
int indexB = indexA + 1;
if (indexB >= controlPoints.Length)
{
indexB = 0;
}
float blendAmount = (normalizedLength - (arrayValueLength * indexA)) / arrayValueLength;
Quaternion rotation = Quaternion.Lerp(controlPoints[indexA].Rotation, controlPoints[indexB].Rotation, blendAmount);
return rotation * transform.up;
}
/// <inheritdoc />
protected override float GetUnClampedWorldLengthInternal()
{
float distance = 0f;
Vector3 last = GetPoint(0f);
for (int i = 1; i < BaseMixedRealityLineDataProvider.UnclampedWorldLengthSearchSteps; i++)
{
Vector3 current = GetPoint((float)i / BaseMixedRealityLineDataProvider.UnclampedWorldLengthSearchSteps);
distance += Vector3.Distance(last, current);
}
return distance;
}
#endregion BaseMixedRealityLineDataProvider Implementation
}
}