Arc Parameterization

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

alt text
alt text

Left is Bernstein's cubic bezier curve distribution and Right is set of evenly distributed points calculated with Arc Parameterization.

Get position at t which is normalized dist

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); 
}

Get arc length set

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;     
}