Путь анимации по времени Безье

Я пытаюсь определить путь точек. Каждая точка имеет x, y и время. Затем я хочу запросить этот путь и получить текущую позицию в этот момент времени. Позвольте мне поделиться некоторым псевдокодом.

point {x, y, time}


function initialisePath(point[] path) {
    ... // Create Bezier Path
}

function getXYAtTime(time) {
    return ... // Get interpolated point along the bezier path at the specified time
}

Я пытаюсь реализовать это в javascript, используя тег canvas. Однако подойдет образец на любом языке. Кто-нибудь знает какую-нибудь библиотеку с открытым исходным кодом (на любом языке), которая создает такой запрашиваемый путь??

Примечание. Я пытался разобраться в этом образце и коде из проект DynApi, но переход от этого примера к пути с учетом времени — это натяжка для моих плохих навыков анимации.

Спасибо

Гвидо


person gatapia    schedule 20.11.2009    source источник


Ответы (2)


кривая Безье имеет не только начальную и конечную точки, но и управление точки, определяющие форму кривой. В демонстрационной версии DynApi, которую вы связали, конечные точки отмечены желтым, а контрольные точки отмечены красным.

Ваш путь будет представлять собой последовательность кривых Безье, соединенных встык.

Итак, давайте возьмем ваш псевдокод, но мы будем рассматривать все точки, которые не имеют свойство .time, как контрольные точки.

function Path(points) {
    this.points = points;

    // Sanity check.
    if (points[0].time == undefined || points[points.length - 1].time == undefined)
        throw new Error("all control points must be between two real points");
}

Path.prototype.getXYAtTime = function (t) {
    var points = this.points;

    // First, see if t is out of range.
    if (t < points[0].time)
        return points[0];
    if (t > points[points.length - 1].time)
        return points[points.length - 1];

    // OK, t is in range. Find out which Bezier curve we're in.
    //
    // Specifically we want 'start' and 'stop' to be the indexes of two points
    // that each have a .time property, bracketing the current time t; and
    // all the points in between 'start' and 'stop' should be control points.
    //
    var start = 0, stop = points.length - 1;
    for (var i = 1; i < points.length; i++) {
        var p = points[i];
        if (t < p.time) {
            stop = i;
            break;
        }
        if (p.time != undefined)
            start = i;
    }
    var n = stop - start;

    // Adjust t to be in the range [0, 1).
    var t0 = points[start].time, t1 = points[stop].time;
    t = (t - t0) / (t1 - t0);
    var tInv = 1 - t;

    // Now calculate the current position in the curve.
    // Wikipedia says this is:
    //   sum for i = 0 to n of (n C i * (1 - t) ^ (n - i) * t ^ i * P[i])
    // 
    var x = 0, y = 0;
    for (var i = 0; i <= n; i++) {
        var p = points[start + i];
        var c = nCr(n, i) * Math.pow(1 - t, n - i) * Math.pow(t, i);
        x += c * p.x;
        y += c * p.y;
    }
    return {x: x, y: y};
}

// The number of k-combinations of a set of size n.
function nCr(n, k) {
    var z = 1;
    for (var i = 1; i <= k; i++)
        z *= (n + 1 - i) / i;
    return z;
}

Итак, математическая часть сделана. Это зависит от вас, чтобы подключить его к холсту и заставить его работать.

Вот как вы вызываете этот метод:

// Here's a Path consisting of a single Bezier curve.
var path = new Path([
    {x: 200, y: 150, time: 0},  // start point
    {x: 200, y: 500},           // 2 control points
    {x: 250, y: 100},
    {x: 500, y: 300, time: 50}  // end point
  ]);

var p = path.getXYAtTime(2.718);
alert(p.x + ", " + p.y);
person Jason Orendorff    schedule 20.11.2009
comment
Вау, спасибо за очень описательный ответ. Теперь я чувствую себя глупо, добавляя, что путь (набор точек) у меня не содержит никаких контрольных точек. Я действительно должен был упомянуть, что я просто хочу сгладить этот путь. Поэтому вместо неровного соединения точек анимации я хочу создать плавную анимацию, проходящую через эти точки. Я играл с концепцией «угадывания» этих контрольных точек, учитывая предыдущую точку и следующую точку (сегмента пути из 2 точек), однако это не завело меня слишком далеко. Tnx еще раз за подробно описанный ответ. - person gatapia; 20.11.2009
comment
Ой! Я чувствую себя глупо, потому что не понимаю, чего ты хотел. Это звучит как разумный подход, но может быть трудно угадать эти контрольные точки. Вам нужно выяснить, что такое ограничения. Например, заданная точка и контрольные точки непосредственно перед и сразу после должны выстроиться в линию. В противном случае вы получите внезапное изменение направления в точке. - person Jason Orendorff; 21.11.2009
comment
Что-то, что я сделал однажды в этой ситуации: (а) выбрал скорость, которую я хочу в каждой точке, на основе окружающих точек; (b) учитывая две точки и желаемую скорость в каждой точке, вычислите уникальную кубическую функцию, которая отвечает всем требованиям. Однако в вашем случае у вас есть дополнительное ограничение: фиксированное количество времени. - person Jason Orendorff; 21.11.2009
comment
Привет, Джейсон, я не совсем понимаю твое предложение по скорости. С моими текущими настройками я могу точно рассчитать среднюю скорость между двумя точками (поскольку каждая точка имеет x, y и время). Поэтому я могу предположить, что скорость в каждой точке - это средняя скорость. Однако я не понимаю, как из этой скорости вычислить квадратичную функцию для удовлетворения этих точек. Подожди 1 сек, под скоростью ты имеешь в виду скорость вектора (включая направление)? Звучит интересно, но опять же, это выходит за рамки моих возможностей (хотя я хорош в йо-йо), знаете ли вы какой-нибудь проект с открытым исходным кодом, делающий такие вещи? - person gatapia; 22.11.2009
comment
Ну да, я имел в виду векторную скорость, но это не большая проблема. Вам вообще не нужно заниматься векторной математикой. Вы можете вычислить координату x совершенно независимо от координаты y. - person Jason Orendorff; 22.11.2009

Контрольные точки кривой Безье на самом деле именно то, что вы получаете, добавляя желаемый вектор скорости в каждой конечной точке к конечной точке. Например, если вам нужна скорость vx0,vy0 в точке x0,y0, а затем перейти к точке x1,y1, прибывающей туда со скоростью vx1,vy1, используйте следующие четыре точки для определения кривой Безье: (x0,y0); (x0+vx0,y0+vy0); (x1-vx1,y1-vy1); (x1,y1). (две средние — ваши контрольные точки.)

person C. Scott Ananian    schedule 05.05.2012