Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cubic Hermite Spline behaving strangely

I'm attempting to draw a graph using Cubic Hermite Splines. I grabbed the simple code to do so from this interpolation methods page.

Here is my code:

private float HermiteInterpolate(float y0, float y1, float y2, float y3, float mu)
{
    var mu2 = mu * mu;

    var a0 = -0.5f * y0 + 1.5f * y1 - 1.5f * y2 + 0.5f * y3;
    var a1 = y0 - 2.5f * y1 + 2f * y2 - 0.5f * y3;
    var a2 = -0.5f * y0 + 0.5f * y2;
    var a3 = y1;

    return (a0 * mu * mu2) + (a1 * mu2) + (a2 * mu) + a3;
}

With this data (y-values, from 0-1, x-values are distributed evenly from 0-21):

0, 0.09448819, 0.1102362, 0.1338583, 0.1811024, 0.2283465 ,0.3543307, 0.4645669, 0.480315, 0.480315, 0.527559, 0.527559, 0.527559, 0.527559, 0.527559, 0.527559, 0.6062992, 0.6377953, 0.6377953, 0.6377953, 0.7480315

And here is the result:

Hermite Graph

The problem is, at some areas of the graph, the line goes downward. Looking at the data, it never decreases. I don't know if the algorithm is supposed to do this, but for what I am working on, I want the lines to never go downward (and if I was drawing the graph by hand, I would never make them point downward anyway).

So,

  • Is there something wrong with the graphing?
  • Is the algorithm supposed to do this? If so, is there one where this doesn't happen?
  • I have tried cosine interpolation, but didn't like how it turned out.

Here is the actual graphing function:

public void DrawGraph(IList<float> items)
{
    for (var x = 0; x < Width; x++)
    {
        var percentThrough = (float)x / (float)Width;
        var itemIndexRaw = items.Count * percentThrough;
        var itemIndex = (int)Math.Floor(itemIndexRaw);

        var item = items[itemIndex];
        var previousItem = (itemIndex - 1) < 0 ? item : items[itemIndex - 1];
        var nextItem = (itemIndex + 1) >= items.Count ? item : items[itemIndex + 1];
        var nextNextItem = (itemIndex + 2) >= items.Count ? nextItem : items[itemIndex + 2];

        var itemMu = FractionalPart(itemIndexRaw);

        var pointValue = HermiteInterpolate(previousItem, item, nextItem, nextNextItem, itemMu);

        WritePixel(x, (int)(pointValue * Height) - 1, (1 - FractionalPart(pointValue)), false);
        WritePixel(x, (int)(pointValue * Height), 1.0f, false);
        WritePixel(x, (int)(pointValue * Height) + 1, FractionalPart(pointValue), false);
    }
}
like image 219
Snea Avatar asked Dec 19 '11 04:12

Snea


1 Answers

This behavior is normal.

Interpolation methods impose certain continuity conditions in order to give the appearance of a smooth curve. For Hermite interpolation, there is no condition that the interpolating curve through a sequence of increasing values must also be increasing everywhere, and so sometimes you get the effect you show here.

There is something called monotone cubic interpolation which does what you want: if the data points are increasing, the interpolating curve will be increasing everywhere also.

like image 83
Jeffrey Sax Avatar answered Sep 21 '22 14:09

Jeffrey Sax