218 lines
5.3 KiB
C#
218 lines
5.3 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.Events;
|
|
|
|
namespace SplineMesh
|
|
{
|
|
[Serializable]
|
|
public class CubicBezierCurve
|
|
{
|
|
private const int STEP_COUNT = 30;
|
|
|
|
private const float T_STEP = 1f / 30f;
|
|
|
|
private readonly List<CurveSample> samples = new List<CurveSample>(30);
|
|
|
|
public SplineNode n1;
|
|
|
|
public SplineNode n2;
|
|
|
|
public UnityEvent Changed = new UnityEvent();
|
|
|
|
public float Length { get; private set; }
|
|
|
|
public CubicBezierCurve(SplineNode n1, SplineNode n2)
|
|
{
|
|
this.n1 = n1;
|
|
this.n2 = n2;
|
|
n1.Changed += ComputeSamples;
|
|
n2.Changed += ComputeSamples;
|
|
ComputeSamples(null, null);
|
|
}
|
|
|
|
public void ConnectStart(SplineNode n1)
|
|
{
|
|
this.n1.Changed -= ComputeSamples;
|
|
this.n1 = n1;
|
|
n1.Changed += ComputeSamples;
|
|
ComputeSamples(null, null);
|
|
}
|
|
|
|
public void ConnectEnd(SplineNode n2)
|
|
{
|
|
this.n2.Changed -= ComputeSamples;
|
|
this.n2 = n2;
|
|
n2.Changed += ComputeSamples;
|
|
ComputeSamples(null, null);
|
|
}
|
|
|
|
public Vector3 GetInverseDirection()
|
|
{
|
|
return 2f * n2.Position - n2.Direction;
|
|
}
|
|
|
|
private Vector3 GetLocation(float t)
|
|
{
|
|
float num = 1f - t;
|
|
float num2 = num * num;
|
|
float num3 = t * t;
|
|
return n1.Position * (num2 * num) + n1.Direction * (3f * num2 * t) + GetInverseDirection() * (3f * num * num3) + n2.Position * (num3 * t);
|
|
}
|
|
|
|
private Vector3 GetTangent(float t)
|
|
{
|
|
float num = 1f - t;
|
|
float num2 = num * num;
|
|
float num3 = t * t;
|
|
return (n1.Position * (0f - num2) + n1.Direction * (3f * num2 - 2f * num) + GetInverseDirection() * (-3f * num3 + 2f * t) + n2.Position * num3).normalized;
|
|
}
|
|
|
|
private Vector3 GetUp(float t)
|
|
{
|
|
return Vector3.Lerp(n1.Up, n2.Up, t);
|
|
}
|
|
|
|
private Vector2 GetScale(float t)
|
|
{
|
|
return Vector2.Lerp(n1.Scale, n2.Scale, t);
|
|
}
|
|
|
|
private float GetRoll(float t)
|
|
{
|
|
return Mathf.Lerp(n1.Roll, n2.Roll, t);
|
|
}
|
|
|
|
private void ComputeSamples(object sender, EventArgs e)
|
|
{
|
|
samples.Clear();
|
|
Length = 0f;
|
|
Vector3 a = GetLocation(0f);
|
|
for (float num = 0f; num < 1f; num += 1f / 30f)
|
|
{
|
|
Vector3 location = GetLocation(num);
|
|
Length += Vector3.Distance(a, location);
|
|
a = location;
|
|
samples.Add(CreateSample(Length, num));
|
|
}
|
|
Length += Vector3.Distance(a, GetLocation(1f));
|
|
samples.Add(CreateSample(Length, 1f));
|
|
if (Changed != null)
|
|
{
|
|
Changed.Invoke();
|
|
}
|
|
}
|
|
|
|
private CurveSample CreateSample(float distance, float time)
|
|
{
|
|
return new CurveSample(GetLocation(time), GetTangent(time), GetUp(time), GetScale(time), GetRoll(time), distance, time, this);
|
|
}
|
|
|
|
public CurveSample GetSample(float time)
|
|
{
|
|
AssertTimeInBounds(time);
|
|
CurveSample curveSample = samples[0];
|
|
CurveSample curveSample2 = default(CurveSample);
|
|
bool flag = false;
|
|
foreach (CurveSample sample in samples)
|
|
{
|
|
if (sample.timeInCurve >= time)
|
|
{
|
|
curveSample2 = sample;
|
|
flag = true;
|
|
break;
|
|
}
|
|
curveSample = sample;
|
|
}
|
|
if (!flag)
|
|
{
|
|
throw new Exception("Can't find curve samples.");
|
|
}
|
|
float t = ((curveSample2 == curveSample) ? 0f : ((time - curveSample.timeInCurve) / (curveSample2.timeInCurve - curveSample.timeInCurve)));
|
|
return CurveSample.Lerp(curveSample, curveSample2, t);
|
|
}
|
|
|
|
public CurveSample GetSampleAtDistance(float d)
|
|
{
|
|
if (d < 0f || d > Length)
|
|
{
|
|
throw new ArgumentException("Distance must be positive and less than curve length. Length = " + Length + ", given distance was " + d);
|
|
}
|
|
CurveSample curveSample = samples[0];
|
|
CurveSample curveSample2 = default(CurveSample);
|
|
bool flag = false;
|
|
foreach (CurveSample sample in samples)
|
|
{
|
|
if (sample.distanceInCurve >= d)
|
|
{
|
|
curveSample2 = sample;
|
|
flag = true;
|
|
break;
|
|
}
|
|
curveSample = sample;
|
|
}
|
|
if (!flag)
|
|
{
|
|
throw new Exception("Can't find curve samples.");
|
|
}
|
|
float t = ((curveSample2 == curveSample) ? 0f : ((d - curveSample.distanceInCurve) / (curveSample2.distanceInCurve - curveSample.distanceInCurve)));
|
|
return CurveSample.Lerp(curveSample, curveSample2, t);
|
|
}
|
|
|
|
private static void AssertTimeInBounds(float time)
|
|
{
|
|
if (time < 0f || time > 1f)
|
|
{
|
|
throw new ArgumentException("Time must be between 0 and 1 (was " + time + ").");
|
|
}
|
|
}
|
|
|
|
public CurveSample GetProjectionSample(Vector3 pointToProject)
|
|
{
|
|
float num = float.PositiveInfinity;
|
|
int num2 = -1;
|
|
int num3 = 0;
|
|
foreach (CurveSample sample in samples)
|
|
{
|
|
float sqrMagnitude = (sample.location - pointToProject).sqrMagnitude;
|
|
if (sqrMagnitude < num)
|
|
{
|
|
num = sqrMagnitude;
|
|
num2 = num3;
|
|
}
|
|
num3++;
|
|
}
|
|
CurveSample a;
|
|
CurveSample b;
|
|
if (num2 == 0)
|
|
{
|
|
a = samples[num2];
|
|
b = samples[num2 + 1];
|
|
}
|
|
else if (num2 == samples.Count - 1)
|
|
{
|
|
a = samples[num2 - 1];
|
|
b = samples[num2];
|
|
}
|
|
else
|
|
{
|
|
float sqrMagnitude2 = (pointToProject - samples[num2 - 1].location).sqrMagnitude;
|
|
float sqrMagnitude3 = (pointToProject - samples[num2 + 1].location).sqrMagnitude;
|
|
if (sqrMagnitude2 < sqrMagnitude3)
|
|
{
|
|
a = samples[num2 - 1];
|
|
b = samples[num2];
|
|
}
|
|
else
|
|
{
|
|
a = samples[num2];
|
|
b = samples[num2 + 1];
|
|
}
|
|
}
|
|
float value = (Vector3.Project(pointToProject - a.location, b.location - a.location) + a.location - a.location).sqrMagnitude / (b.location - a.location).sqrMagnitude;
|
|
value = Mathf.Clamp(value, 0f, 1f);
|
|
return CurveSample.Lerp(a, b, value);
|
|
}
|
|
}
|
|
}
|