Since Bernstein’s or DeCasteljau’s cubic bezier curve would not give us evenly distributed points along with a curve.
For example, when BezierCurvePointAt(float t) is 0.25, it does not give us the position along with the curve at 25%!
Arc-Length Parameterization comes to rescue! to get evenly distributed point.
This lead artist to use accurate object animation along with a curve using different ease e.g. Linear, EaseIn, EaseOut and etc.
C#, Unity
Left is Bernstein's cubic bezier curve distribution and Right is set of evenly distributed points calculated with Arc Parameterization.
public void GetPositionAtDistance(float t, float[] arcLengthSet, float curveLength, ref Vector3 position) { // assume that t is [0~1] float targetLength = t * curveLength; int index = 0; int low = 0; int high = arcLengthSet.Length; while (low < high) { //binary search on our stored lengths to find the largest length that's smaller then targetLength index = low + ((high - low) / 2 | 0); if (arcLengthSet[index] < targetLength) { low = index + 1; } else { high = index; } } if (arcLengthSet[index] > targetLength && index > 0 && index < arcLengthSet.Length) { index--; } float lengthBefore = arcLengthSet[index]; float k = 0f; // just return or do the interpolation and return if (lengthBefore == targetLength || (index + 1) >= arcLengthSet.Length) // TODO: use .AboutEqual instead? { k = index / arcLengthSet.Length; } else { k = (index + (targetLength - lengthBefore) / (arcLengthSet[index + 1] - lengthBefore)) / arcLengthSet.Length; } GetPositionAt(k, ref position); }public float[] GetArcLengthSet(int resolution, int segmentID = 0) { float[] set = new float[resolution + 1]; float lengthSq = 0f; set[0] = 0f; Vector3 prev = CurveSegments[segmentID].P0.transform.position; // BezierCurveMath.GetPoint(0f, CurveSegments[segmentID].P0.transform.position, CurveSegments[segmentID].P0_Tangent.transform.position, CurveSegments[segmentID].P1_Tangent.transform.position, CurveSegments[segmentID].P1.transform.position); for (int i = 1; i <= resolution; i++) { float t = (float)i / resolution; Vector3 curr = BezierCurveMath.GetPoint(t, CurveSegments[segmentID].P0.transform.position, CurveSegments[segmentID].P0_Tangent.transform.position, CurveSegments[segmentID].P1_Tangent.transform.position, CurveSegments[segmentID].P1.transform.position); // TODO: optimize with sq dist? lengthSq += Vector3.Distance(prev, curr); prev = curr; set[i] = lengthSq; } return set; }