Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Interpolating cubic splines

Discussion in 'Scripting' started by BogeyGames, Jul 29, 2022.

  1. BogeyGames

    BogeyGames

    Joined:
    Jul 28, 2022
    Posts:
    3
    Hi,

    How can we leverage Unity's new Spline API to interpolate a cubic spline?

    According to the article below [1], for Catmull-Rom splines, the tangent of a point should simple be the average of its prior and next elements; T = 0.5 * (P[i+1] - P[i-1]).

    So naively, I tried the following:

    Code (CSharp):
    1.  var points = new float2[] {
    2.             new(-1.5f, -1.2f), new(-0.2f, 0f), new(1f, 0.5f), new(5f, 1f),
    3.             new(10f, 1.2f), new(15f, 2f), new(20f, 1f), new(15f, 9f), new(5f, 3f)
    4.         };
    5.  
    6. var points3d = points.Select(x => new float3(x.x, 0f, x.y)).ToArray();
    7.  
    8. var knots = new List<BezierKnot>();
    9. for (int i = 0; i < points.Length; i++)
    10. {
    11.     var iPrev = Math.Max(0, i - 1);
    12.     var iNext = Math.Min(points.Length - 1, i + 1);
    13.  
    14.     var c = 0.5f;
    15.     var tangent= (1 - c) * (points3d[iNext] - points3d[iPrev]);
    16.    
    17.     var knot = new BezierKnot(points3d[i], tangent, tangent, quaternion.identity);
    18.     knots.Add(knot);
    19. }
    20.  
    21. var spline = new Spline(knots, false);
    22.  
    23. var samplePoints = Enumerable.Range(0,1000).Select(x => x/1000f).Select(x => SplineUtility.EvaluatePosition(spline, x));
    Now here is roughly what I would have liked (this is NOT the output from above; its just as excel chart with the points given above)



    Now when I run my beautiful code sample, this is the reality I'm facing:



    Ouch.

    Where did I go wrong? How can I interpolate a spline based on a few points, without the end result looking like the spline is trying to murder me?

    [1] https://www.cubic.org/docs/hermite.htm
     
  2. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,735
    From your picture it kind of looks to my naive eyes like you've got it almost right but the "left side" tangents are inverted. And looking at your code you're using the same tangent twice! Shouldn't it be
    -tangent, tangent
    , rather than
    tangent, tangent
    ? (not sure in which order)?
     
    BogeyGames and Kurt-Dekker like this.
  3. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,949
    Looks like you just have the control points or data out of order. Strip it down to the ultra-simplest thing, two points with two control handles. Run the math, debug your computations, figure out where it is going wrong.
     
    BogeyGames likes this.
  4. BogeyGames

    BogeyGames

    Joined:
    Jul 28, 2022
    Posts:
    3
    Thanks, appreciate the input! (I'd rather not tell how much time I've spent on this already..)

    -tangent, +tangent
    seems a little better, but overall its still a bit of an oddball. See e.g. the little loop on the left side, or the rapid jump at x=~12 (no point is in that area).

    I've dodged the rotation parameter so far and just passed in
    quaternion.identity
    . I wonder if that could be my downfall. Admittedly, I don't quite understand how rotation would even factor into any of this, I would have thought a knot should be well defined by its coordinates and tangent alone





    Edit: Partial success! Changing to
    -tangent, +tangent
    , and scaling the tangent down to 1/3 of its original size, is giving me good results.

    I'm not sure I trust the situation much though. Why do I need to scale it down in the first place... I'm slightly wary it fixed my example case, but might blow up in other cases again...

     
  5. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,949
    The numbers are all there... plug in known good numbers, run your program, break out a calculator or spreadsheet and compute what they SHOULD be, and compare!

    It's just data.
     
  6. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,571
    Your c value is too large. Common values are 2/3 but what values are appropriate depends on several factors. You could also try to normalize the tangents and acale them by the distance to the neighboring points. Using the same tangents for the left and right side would only make sense when the points are roughly equally spaced. However if you have a small distance followed by a larger distance, the two sides would need different tangent length. Using half the distance between the two neighbors (as you currently do) or using 2/3 would not really work as the tangent would be longer than the actual spline section. That's why you get those loops.

    So when your points are roughly equally spaced, just use a greater "c" value (== a smaller factor due to 1-c). If there are vastly different distances involved or quite sharp turns, you probably want to scale the tangents in relation to some metric. One could use the linear distance to the neighbor point + a scaling factor. Though the projected distance onto the tangent is probably better.
     
    Last edited: Jul 30, 2022
  7. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,571
    Last edited: Jul 30, 2022