Created
August 15, 2015 23:19
-
-
Save jaburns/1542865728deee8f432e to your computer and use it in GitHub Desktop.
Estimates the length of a cubic Bezier spline and provides an interface to walk it at a constant speed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine; | |
using System; | |
using System.Collections.Generic; | |
public class CubicSplineWalker | |
{ | |
readonly Vector2 _anchor0; | |
readonly Vector2 _control0; | |
readonly Vector2 _control1; | |
readonly Vector2 _anchor1; | |
readonly DistanceTable _distTable; | |
public CubicSplineWalker(Vector2 anchor0, Vector2 control0, Vector2 control1, Vector2 anchor1, int segments) | |
{ | |
this._anchor0 = anchor0; | |
this._control0 = control0; | |
this._control1 = control1; | |
this._anchor1 = anchor1; | |
if (segments < 1) segments = 1; | |
_distTable = new DistanceTable(); | |
Vector2 prevPt = _anchor0; | |
for (int i = 1; i <= segments; ++i) { | |
var t = (float)i / segments; | |
var thisPt = Evaluate(t); | |
_distTable.addPoint(t, (thisPt - prevPt).magnitude); | |
prevPt = thisPt; | |
} | |
_distTable.compute(); | |
} | |
public float Length | |
{ | |
get { | |
return _distTable.totalDistance; | |
} | |
} | |
public Vector2 Evaluate(float t) | |
{ | |
var u = 1 - t; | |
return new Vector2 { | |
x = u*u*u*_anchor0.x + 3*u*u*t*_control0.x + 3*u*t*t*_control1.x + t*t*t*_anchor1.x, | |
y = u*u*u*_anchor0.y + 3*u*u*t*_control0.y + 3*u*t*t*_control1.y + t*t*t*_anchor1.y | |
}; | |
} | |
public Vector2 EvaluateByDistance(float s) | |
{ | |
return Evaluate(_distTable.getTimeFromDistance(s)); | |
} | |
class DistanceTable | |
{ | |
readonly List<float> _dists = new List<float>(); | |
readonly List<float> _times = new List<float>(); | |
public float totalDistance { get; private set; } | |
public void addPoint(float t, float distance) | |
{ | |
_times.Add(t); | |
_dists.Add(distance); | |
} | |
public void compute() | |
{ | |
float runningTotal = 0; | |
for (int i = 0; i < _dists.Count; ++i) { | |
var thisDist = _dists[i]; | |
_dists[i] += runningTotal; | |
runningTotal += thisDist; | |
} | |
totalDistance = _dists[_dists.Count-1]; | |
} | |
public float getTimeFromDistance(float s) | |
{ | |
if (s < 0) return 0; | |
if (s > totalDistance) return totalDistance; | |
int index = 0; | |
while (s > _dists[index]) index++; | |
var thisDist = _dists[index]; | |
var prevDist = index > 0 ? _dists[index-1] : 0; | |
var relativeDist = (s - prevDist) / (thisDist - prevDist); | |
var thisTime = _times[index]; | |
var prevTime = index > 0 ? _times[index-1] : 0; | |
return prevTime + relativeDist*(thisTime - prevTime); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment