Глубокое понимание кривых Безье

внешний интерфейс CSS графика
Глубокое понимание кривых Безье

Кривая Безье широко используется в области компьютерной графики, например в CSS-анимации, Canvas и Photoshop, с которыми мы знакомы, можно увидеть кривую Безье.

Каталог статей

1. Что такое кривая Безье?

Кривые Безье были широко опубликованы в 1962 году французским инженером Пьером Безье, который использовал кривые Безье для проектирования кузова автомобиля.

Кривые Безье в основном используются для математических кривых в двумерных графических приложениях.Кривая состоит из начальной точки, конечной точки (также называемой опорной точкой) и контрольной точки.Путем настройки контрольных точек форма кривой Безье нарисован определенным образом. будет меняться. Метод рисования будет подробно описан позже.

Кривые Безье широко используются в компьютерной графике, например, эффект пера в Photoshop и инструмент кривой Безье в Flash 5. При разработке графического пользовательского интерфейса программного обеспечения обычно предоставляются соответствующие методы для реализации кривых Безье. Мы знакомы с функцией времени перехода анимации CSS. также получены с помощью кривых Безье (кривых Безье третьего порядка).

Какие существуют типы кривых Безье?

Кривая Безье согласноконтрольная точкаКоличество разделено на:

  • Кривая Безье 1-го порядка (2 контрольные точки)
  • Кривая Безье 2-го порядка (3 контрольные точки)
  • Кривая Безье 3-го порядка (4 контрольные точки)
  • Кривая Безье порядка n (n+1контрольные точки)

3. Как строятся кривые Безье?

На следующем рисунке показана кривая Безье третьего порядка, включающая четыре контрольные точки соответственно.P_0,P_1,P_2,P_3.

三阶贝塞尔曲线

Итак, как нам провести кривую Безье через контрольные точки?

Используя кривую Безье третьего порядка в качестве примера на рисунке выше, основные шаги заключаются в следующем:

  1. Четыре контрольные точки соединены последовательно, чтобы сформировать три сегмента линии, которые показаны на рисунке выше.P_0P_1,P_1P_2,P_2P_3, а затем передать параметрt,其中 t\in[0,1], значение этого параметра равно длине точки на отрезке от начальной точки, деленной на длину отрезка. подобноP_0P_1На отрезке есть точкаP_0^{'},В настоящее времяtЗначение\frac{P_0P_0^{'}}{P_0P_1}P_0^{'}Расположение показано ниже.

bezier-01

  1. Затем сделайте то же самое для каждого сегмента линии, чтобы получить три контрольные точки.P_0^{'},P_1^{'},P_2^{'},Как показано ниже.

bezier-02

  1. Затем повторите шаг 1 для этих трех контрольных точек, чтобы получить две контрольные точки.P_0^{''},P_1^{''},Как показано ниже.

bezier-03

  1. Наконец, тот же метод можно использовать для получения конечной точки.P_0^{'''}, как показано на рисунке ниже, эта точка является точкой на кривой Безье.

bezier-04

контролируяtЗначение , увеличенное от 0 до 1, проводит линию от начальной точкиP_0к концуP_1кривая Безье.

Интуитивно ощутить процесс рисования можно благодаря следующей анимации:

三阶贝塞尔曲线绘制过程

4. Как найти координаты точки кривой Безье?

1. Кривая Безье первого порядка

一阶贝塞尔曲线绘制过程

Для кривой Безье первого порядка мы можем с помощью геометрических знаний легко согласноtЗначение извлечения координат этой точки на отрезке линии:

B_{1}(t) = P_0 + (P_1 - P_0)t

Тогда мы можем получить:

B_{1}(t) = (1 - t)P_0 + tP_1,t\in[0,1]

2. Кривая Безье второго порядка

二阶贝塞尔曲线绘制过程

Для кривой Безье второго порядка вы можете понять это как:P_0P_1Используя формулу первого порядка, найти точкуP_0^{'}, затем вP_1P_2Используя формулу первого порядка, найти точкуP_1^{'}, и, наконец, вP_0^{'}P_1^{'}Затем используйте формулу первого порядка, чтобы найти точку на окончательной кривой Безье.P_0{''}. Конкретный процесс получения выглядит следующим образом:

Сначала найдите контрольные точки на отрезке прямой.

P_0^{'} = (1 - t)P_0 + tP_1
P_1^{'} = (1 - t)P_1 + tP_2

Подставьте приведенную выше формулу в следующую формулу:

B_{2}(t) = (1 - t)P_0^{'} + tP_1^{'}
= (1 - t)((1 - t)P_0 + tP_1) + t((1 - t)P_1 + tP_2)
= (1 - t)^2P_0 + 2t(1 - t)P_1 + t^2P_2

В результате получается следующая формула:

B_{2}(t) = (1 - t)^2P_0 + 2t(1 - t)P_1 + t^2P_2 , t\in[0, 1]

3. Кривая Безье третьего порядка

三阶贝塞尔曲线绘制过程

Подобно кривой Безье второго порядка, следующие формулы координат могут быть получены тем же методом:

B_{3}(t) = (1 - t)^3P_0 + 3t(1 - t)^2P_1 + 3t^2(1 - t)P_2 + t^3P_3 , t\in[0, 1]

4. Многопорядковая кривая Безье

Вот я просто поставилnДана формула порядковой кривой Безье, и заинтересованные студенты могут изучить ее самостоятельно.

B(t) = \sum_{i=0}^{n}C_n^{i}P_i(1-t)^{n-i}t^i,t\in[0,1]

который:

B(t) = \sum_{i=0}^{n}P_ib_{i,n}(t),t\in[0,1]

формулаC_n^iценность\frac{n!}{(n - i)!\cdot i!}, связанные со статистикой, заинтересованные студенты могут взглянуть на мойэта статья.

вb_{i,n}(t)Значение:

b_{i,n}(t)=C_n^{i}(1-t)^{n-i}t^i,其中i=0,1,...,n

5. Как реализовать конструктор кривой Безье третьего порядка, аналогичный свойству easing в CSS?

Если мы хотим реализовать такую ​​кривую Безье третьего порядка, нам нужно не только получить некоторые точки на кривой, но и получить координаты по оси Y через ось X.

Ослабляющая кривая Безье в CSS имеет характеристику, то есть начальная точка и конечная точка фиксированы, то есть они соответственно[0, 0],\ [1,1]. Итак, есть только две неизвестные точки, то есть нужно передать четыре значения, и диапазон этих четырех значений должен быть в[0,1]Внутри.

Итак, нам нужно создать класс CubicBezier со свойствамиcontrolPoints:

class CubicBezier {
  constructor(x1, y1, x2, y2) {
    this.controlPoints = [x1, y1, x2, y2];
  }
}

После инициализации вышеуказанного кода мы также должны быть основаны на T (диапазон стоимости)[0, 1]) значение для получения координат и массив набора координат на кривой. Кроме того, нужно использовать формулу Бесселя третьего порядка:

B_{2}(t) = (1 - t)^3P_0 + 3t(1 - t)^2P_1 + 3t^2(1 - t)P_2 + t^3P_3 , t\in[0, 1]

потому чтоP_0Координаты точки [0, 0],P_1Координаты точки[1, 1]Тогда формулу можно записать так:

B_{3, x}(t) = 3t(1 - t)^2x_1 + 3t^2(1 - t)x_2 + t^3 , t\in[0, 1]
B_{3, y}(t) = 3t(1 - t)^2y_1 + 3t^2(1 - t)y_2 + t^3 , t\in[0, 1]
class CubicBezier {
  constructor(x1, y1, x2, y2) {
    this.controlPoints = [x1, y1, x2, y2];
  }

  getCoord(t) {
    // 如果t取值不在0到1之间,则终止操作
    if (t > 1 || t < 0) return;
    const _t = 1 - t;
    const [ x1, y1, x2, y2 ] = this.controlPoints;
    const coefficient1 = 3 * t * Math.pow(_t, 2);
    const coefficient2 = 3 * _t * Math.pow(t, 2);
    const coefficient3 = Math.pow(t, 3);
    const px = coefficient1 * x1 + coefficient2 * x2 + coefficient3;
    const py = coefficient1 * y1 + coefficient2 * y2 + coefficient3;
    // 结果只保留三位有效数字
    return [parseFloat(px.toFixed(3)), parseFloat(py.toFixed(3))];
  }
}

Используя приведенный выше класс Безье, мы можем построить экземпляр Безье на основе двух контрольных точек.Через этот экземпляр мы можем получить приблизительное значение точки на основе значения t.

Итак, если мы хотим получить координату по оси Y на основе значения координаты по оси X, что нам делать?

Здесь я использую приблизительный метод, а именно:

  1. Сначала получите две точки, ближайшие к точке, которую нужно оценить.
  2. Тогда через эти две точки можно получить уравнение прямой линии.
  3. Наконец, путем передачи координаты по оси x в уравнение прямой можно приблизительно получить значение координаты по оси y.

Итак, нам нужно дополнительно преобразовать конструктор Безье, нужно кэшировать свойства фиксированного количества массивов координатcoords, и получитьcoordsМетодыgetCoordsArray, и, наконец, есть способ получить координаты по оси YgetY, конкретный метод реализации выглядит следующим образом:

class CubicBezier {
  constructor(x1, y1, x2, y2) {
    const precision = 100;
    this.controlPoints = [x1, y1, x2, y2];
    this.coords = this.getCoordsArray(precision);
  }
  
  getCoord(t) {
    // 如果t取值不在0到1之间,则终止操作
    if (t > 1 || t < 0) return;
    const _t = 1 - t;
    const [ x1, y1, x2, y2 ] = this.controlPoints;
    const coefficient1 = 3 * t * Math.pow(_t, 2);
    const coefficient2 = 3 * _t * Math.pow(t, 2);
    const coefficient3 = Math.pow(t, 3);
    const px = coefficient1 * x1 + coefficient2 * x2 + coefficient3;
    const py = coefficient1 * y1 + coefficient2 * y2 + coefficient3;
    // 结果只保留三位有效数字
    return [parseFloat(px.toFixed(3)), parseFloat(py.toFixed(3))];
  }
  
  getCoordsArray(precision) {
    const step = 1 / (precision + 1);
    const result = [];
    for (let t = 0; t <= precision + 1; t++) {
      result.push(this.getCoord(t * step));
    }
    this.coords = result;
    return result;
  }
  
  getY(x) {
    if (x >= 1) return 1;
    if (x <= 0) return 0;
    let startX = 0;
    for (let i = 0; i < this.coords.length; i++) {
      if (this.coords[i][0] >= x) {
        startX = i;
        break;
      }
    }
    const axis1 = this.coords[startX];
    const axis2 = this.coords[startX - 1];
    const k = (axis2[1] - axis1[1]) / (axis2[0] - axis1[0]);
    const b = axis1[1] - k * axis1[0];
    // 结果也只保留三位有效数字
    return parseFloat((k * x + b).toFixed(3));
  }
}

Тогда мы можем использовать нашCubicBezierсейчас:

const cubicBezier = new CubicBezier(0.3, 0.1, 0.3, 1);
cubicBezier.getY(0.1); // 0.072
cubicBezier.getY(0.7); // 0.931

Я написал приложение для этогоCubicBezierБиблиотека конструкторовAnimate-Scroll, Если вам интересно, вы можете взглянуть на исходный код.

6. Как использовать кривые Безье высокого порядка для представления кривых Безье низкого порядка?

ОдинnЗаказать Кривые Безье можно провести через точно такую ​​же формуn+1Заказать Представление кривой Безье. Итак, что мы делаем, чтобы получить этоn+1Как насчет кривых Безье порядка?

Процесс представления кривой более низкого порядка кривой Безье более высокого порядка, мы называем этоВосходящий порядок.

нам нужно использоватьB(t)=(1-t)B(t)+tB(t)Это уравнение используется для масштабирования.

  1. Взяв в качестве примера рост второго порядка до третьего порядка, формула координат кривой Безье второго порядка будет следующей:
B(t) = (1 - t)^2P_0 + 2t(1 - t)P_1 + t^2P_2

Подставьте следующее уравнение в приведенную выше формулу:

P_0=(1-t)P_0 + tP_0
P_1=(1-t)P_1 + tP_1
P_2=(1-t)P_2 + tP_2

Тогда выводится следующая формула:

B(t) = (1-t)^3P_0 + (1-t)^2tP_0 + 2t(1-t)^2P_1
+ 2t^2(1-t)P_1 + t^2(1-t)P_2 + t^3P_2
=(1-t)^3P_0 + 3(1-t)^2t\frac{P_0+2P_1}{3} + 3(1-t)t^2\frac{2P_1+P_2}{3} + t^3P_2

По вышеприведенным результатам можно сделать вывод, что контрольная точка определяется предыдущимP_0,P_1,P_2сталP_0,\frac{P_0+2P_1}{3},\frac{2P_1+P_2}{3}а такжеP_2Есть четыре контрольных точки, тем самым завершая апгрейд.

  1. Если для любого значения n, как нам повысить порядок? (Ниже приведен процесс вывода, учащиеся, которые не заинтересованы, могут сразу перейти к формуле ниже 👇)

Здесь требуется некоторый вывод (здесь вывод требует использованияC_n^{i}Формулу, заинтересованные студенты могут вывести самостоятельно), потому что:

(1-t)b_{i,n}=\frac{n+1-i}{n+1}b_{i,n+1}
tb_{i,n}=\frac{i+1}{n+1}b_{i+1,n+1}

Формула Бесселя может быть выражена как:

B(t) = (1-t)\sum_{i=0}^{n}b_{i,n}(t)P_i+t\sum_{i=0}^{n}b_{i,n}(t)P_{i}

Подставляя два приведенных выше уравнения, мы получаем:

B(t) = \sum_{i=0}^{n}\frac{n+1-i}{n+1}b_{i,n+1}(t)P_i+\sum_{i=0}^{n}\frac{i+1}{n+1}b_{i+1,n+1}P_i \quad--\ (0)

потому что когдаi=n+1Время:

\frac{i}{n+1}P_{i-1}=0

Таким образом, формулу можно записать так:

\sum_{i=0}^{n}\frac{n+1-i}{n+1}P_i = \sum_{i=0}^{n+1}\frac{n+1-i}{n+1}P_i \quad--\ (1)

и потому, что:

\sum_{i=0}^{n}\frac{i+1}{n+1}P_{i} = \sum_{i=1}^{n+1}\frac{i}{n+1}P_{i-1}

когдаi=0Время:

\frac{i}{n+1}P_{i-1} = 0

так:

\sum_{i=0}^{n}\frac{i+1}{n+1}P_i=\sum_{i=0}^{n+1}\frac{i}{n+1}P_{i-1} \quad--\ (2)

Подставив два приведенных выше уравнения (1) и (2) в формулу (0), окончательно можно получить следующую возрастающую формулу:

B(t) = \sum_{i=0}^{n+1}(\frac{i}{n+1}P_{i-1} + \frac{n+1-i}{n+1}P_i)b_{i,n+1}(t)
B(t) = \sum_{i=0}^{n+1}(P_{i}^{'})b_{i,n+1}(t)
式中\ P_{i}^{'} = \frac{i}{n+1}P_{i-1} + \frac{n+1-i}{n+1}P_i,其中i=0,1,...n+1

Базовое содержание кривой Безье почти готово, если вы нашли что-то не так или есть что добавить, пожалуйста, укажите это в комментариях 😊.

использованная литература