using UnityEngine;
namespace ARLocation
{
///
/// A catmull-rom curve.
///
public class CatmullRomCurve : Curve
{
// The control points
private Vector3 p0;
private Vector3 p1;
private Vector3 p2;
private Vector3 p3;
// The alpha/tension factor
private float alpha;
// The knots
private float t0;
private float t1;
private float t2;
private float t3;
// Getters for the knots
public float T0 { get { return t0; } }
public float T1 { get { return t1; } }
public float T2 { get { return t2; } }
public float T3 { get { return t3; } }
///
/// The curve's cached length
///
private float length;
///
/// An array with the sample curve parameters.
///
private float[] sampleParameters;
///
/// An array with the curve's lenght at each of each sampled points.
///
private float[] sampleLengths;
///
/// Flag dirty sample.
///
private bool isSampleDirty;
///
/// The size of the last sample; used for cache validation.
///
private int lastSampleSize = 100;
///
/// Gets or sets the alpha.
///
/// The alpha.
public float Alpha
{
get
{
return alpha;
}
set
{
alpha = value;
CalculateKnots();
isSampleDirty = true;
}
}
public Vector3 P0
{
get
{
return p0;
}
set
{
p0 = value;
CalculateKnots();
isSampleDirty = true;
}
}
public Vector3 P1
{
get
{
return p1;
}
set
{
p1 = value;
CalculateKnots();
isSampleDirty = true;
}
}
public Vector3 P2
{
get
{
return p2;
}
set
{
p2 = value;
CalculateKnots();
isSampleDirty = true;
}
}
public Vector3 P3
{
get
{
return p3;
}
set
{
p3 = value;
CalculateKnots();
isSampleDirty = true;
}
}
///
/// Creates a catmull-rom curve with control points p0, p1, p2 and p3, and with
/// a given alpha/tension parameter.
///
///
///
///
///
///
public CatmullRomCurve(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float alpha)
{
this.p0 = p0;
this.p1 = p1;
this.p2 = p2;
this.p3 = p3;
this.alpha = alpha;
isSampleDirty = true;
CalculateKnots();
}
private void CalculateKnots()
{
var x = alpha * 0.5f;
t0 = 0.0f;
t1 = t0 + Mathf.Pow((p1 - p0).sqrMagnitude, x);
t2 = t1 + Mathf.Pow((p2 - p1).sqrMagnitude, x);
t3 = t2 + Mathf.Pow((p3 - p2).sqrMagnitude, x);
}
///
/// Calculates the curve at a point u, where u is between 0 and 1.
///
/// The curve parameter in the [0, 1] interval.
///
public override Vector3 GetPoint(float u)
{
u = Mathf.Clamp(u, 0, 1);
float t = t1 * (1 - u) + t2 * u;
Vector3 a1 = (t1 - t) / (t1 - t0) * p0 + ((t - t0) / (t1 - t0)) * p1;
Vector3 a2 = (t2 - t) / (t2 - t1) * p1 + ((t - t1) / (t2 - t1)) * p2;
Vector3 a3 = (t3 - t) / (t3 - t2) * p2 + ((t - t2) / (t3 - t2)) * p3;
Vector3 b1 = (t2 - t) / (t2 - t0) * a1 + ((t - t0) / (t2 - t0)) * a2;
Vector3 b2 = (t3 - t) / (t3 - t1) * a2 + ((t - t1) / (t3 - t1)) * a3;
Vector3 c = (t2 - t) / (t2 - t1) * b1 + ((t - t1) / (t2 - t1)) * b2;
return c;
}
///
/// Calculates the point and the tangent of the curve.
///
/// The curve parameter in the [0, 1] interval.
///
public override CurvePointData GetPointAndTangent(float u)
{
u = Mathf.Clamp(u, 0, 1);
float t = t1 * (1 - u) + t2 * u;
Vector3 a1 = (t1 - t) / (t1 - t0) * p0 + ((t - t0) / (t1 - t0)) * p1;
Vector3 a2 = (t2 - t) / (t2 - t1) * p1 + ((t - t1) / (t2 - t1)) * p2;
Vector3 a3 = (t3 - t) / (t3 - t2) * p2 + ((t - t2) / (t3 - t2)) * p3;
Vector3 b1 = (t2 - t) / (t2 - t0) * a1 + ((t - t0) / (t2 - t0)) * a2;
Vector3 b2 = (t3 - t) / (t3 - t1) * a2 + ((t - t1) / (t3 - t1)) * a3;
Vector3 c = (t2 - t) / (t2 - t1) * b1 + ((t - t1) / (t2 - t1)) * b2;
Vector3 da1 = (1.0f / (t1 - t0)) * (p1 - p0);
Vector3 da2 = (1.0f / (t2 - t1)) * (p2 - p1);
Vector3 da3 = (1.0f / (t3 - t2)) * (p3 - p2);
Vector3 db1 = (t2 - t) / (t2 - t0) * da1 + ((t - t0) / (t2 - t0)) * da2 +
(1.0f / (t2 - t0)) * (a2 - a1);
Vector3 db2 = (t3 - t) / (t3 - t1) * da2 + ((t - t1) / (t3 - t1)) * da3 +
(1.0f / (t3 - t1)) * (a3 - a2);
Vector3 dc = (t2 - t) / (t2 - t1) * db1 + ((t - t1) / (t2 - t1)) * db2 +
(1.0f / (t2 - t1)) * (b2 - b1);
return new CurvePointData { point = c, tangent = dc };
}
///
/// Creates a sample of (N+2) points (i.e., N + start and end points) of
/// the current curve. Also calculates the length estimate.
///
/// The sample.
/// N.
public override Vector3[] Sample(int n)
{
lastSampleSize = n;
if (n == 0)
{
return new[] { GetPoint(0), GetPoint(1) };
}
var sample = new Vector3[n + 2];
var delta = 1.0f / (n + 1.0f);
length = 0.0f;
sampleParameters = new float[n + 2];
sampleLengths = new float[n + 2];
for (var i = 0; i < (n + 2); i++)
{
sample[i] = GetPoint(i * delta);
sampleParameters[i] = i * delta;
if (i > 0)
{
length += (sample[i] - sample[i - 1]).magnitude;
}
sampleLengths[i] = length;
}
isSampleDirty = false;
return (Vector3[])sample.Clone();
}
///
/// Returns the estimated length.
///
/// The length.
/// N.
public override float EstimateLength(int n = 100)
{
if (isSampleDirty || lastSampleSize != n)
{
Sample(n);
}
return length;
}
///
/// Gets the curve parameter for a given length.
///
/// The parameter for length.
/// S.
public override float GetParameterForLength(float s)
{
if (isSampleDirty)
{
Sample(lastSampleSize);
}
for (var i = 0; i < (sampleParameters.Length - 1); i++)
{
if (s >= sampleLengths[i] && s <= sampleLengths[i + 1])
{
var a = (s - sampleLengths[i]) / (sampleLengths[i + 1] - sampleLengths[i]);
return (1 - a) * sampleParameters[i] + a * sampleParameters[i + 1];
}
}
return -1.0f;
}
///
/// Gets the curve point at a given length.
///
/// The point at length.
/// S.
public override Vector3 GetPointAtLength(float s)
{
return GetPoint(GetParameterForLength(s));
}
///
/// Gets the CurvePointData which stores the point and tangent
/// at a given arc-length.
///
///
///
public override CurvePointData GetPointAndTangentAtLength(float s)
{
return GetPointAndTangent(GetParameterForLength(s));
}
}
}