Кубический Эрмит Сплайн ведет себя странно - PullRequest
6 голосов
/ 19 декабря 2011

Я пытаюсь нарисовать график, используя Кубические сплайны Эрмита . Я взял простой код, чтобы сделать это из этой методы интерполяции страница.

Вот мой код:

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

С этими данными (значения y от 0 до 1, значения x равномерно распределены от 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

А вот и результат:

Hermite Graph

Проблема в том, что в некоторых областях графика линия идет вниз . Глядя на данные, это никогда не уменьшается. Я не знаю, должен ли алгоритм делать это, но для того, над чем я работаю, я хочу, чтобы линии никогда не опускались вниз (и если бы я рисовал график вручную, я бы никогда не сделал так, чтобы они указывали вниз) .

Итак,

  • Что-то не так с графикой?
  • Должен ли алгоритм делать это? Если так, есть ли такой, где это не происходит?
  • Я пробовал косинусную интерполяцию, но мне не понравилось, как это получилось.

Вот фактическая графическая функция:

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

1 Ответ

10 голосов
/ 19 декабря 2011

Это нормальное поведение.

Методы интерполяции накладывают определенные условия непрерывности, чтобы создать гладкую кривую.Для интерполяции Эрмита нет условия, чтобы интерполяционная кривая через последовательность возрастающих значений также увеличивалась повсюду, и поэтому иногда вы получаете эффект, который вы здесь показываете.интерполяция , которая делает то, что вы хотите: если точки данных увеличиваются, интерполяционная кривая будет также расти везде.

...