Skip to content

Instantly share code, notes, and snippets.

@jaburns
Created August 15, 2015 23:19
Show Gist options
  • Save jaburns/1542865728deee8f432e to your computer and use it in GitHub Desktop.
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.
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