Curves и кривые Безье

Кривые в библиотеке Paper.js представлены сегментами (Segment), соединёнными в пути (Path). Каждый сегмент может содержать управляющие точки, определяющие форму кривой Безье. В основе лежит кубическая кривая Безье, задаваемая четырьмя точками:

  • начальная точка (anchor point)
  • конечная точка (anchor point)
  • две управляющие точки (handles)

Каждый сегмент хранит:

  • point — координаты опорной точки
  • handleIn — входящая управляющая точка (от предыдущего сегмента)
  • handleOut — исходящая управляющая точка (к следующему сегменту)
var segment = new paper.Segment({
    point: [100, 100],
    handleIn: [-50, 0],
    handleOut: [50, 0]
});

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

Кубическая кривая Безье описывается параметрическим уравнением:

[ B(t) = (1 - t)^3 P_0 + 3(1 - t)^2 t P_1 + 3(1 - t)t^2 P_2 + t^3 P_3,t ]

где:

  • (P_0) — начальная точка
  • (P_1), (P_2) — управляющие точки
  • (P_3) — конечная точка

В Paper.js эта математика скрыта за API, но понимание структуры позволяет точно управлять формой кривой.

Создание кривых

Добавление сегментов с управляющими точками

var path = new paper.Path();
path.strokeColor = 'black';

path.add(new paper.Segment([50, 250], null, [100, 0]));
path.add(new paper.Segment([250, 250], [-100, 0], null));

Здесь:

  • первая точка имеет только handleOut
  • вторая — только handleIn

Это создаёт плавную кривую между двумя точками.

Упрощённый способ через Path.curveTo

var path = new paper.Path();
path.moveTo([50, 250]);
path.curveTo([150, 150], [200, 350], [300, 250]);

Параметры:

  1. первая управляющая точка
  2. вторая управляющая точка
  3. конечная точка

Типы кривых в Paper.js

1. Прямые сегменты

Если handleIn и handleOut равны [0, 0], сегмент является прямой линией.

path.add([100, 100]);
path.add([200, 200]);

2. Плавные кривые

При симметричных управляющих точках получается гладкая кривая.

segment.handleIn = [-50, 0];
segment.handleOut = [50, 0];

3. Угловые точки

Если handleIn и handleOut не симметричны — появляется излом.

Доступ к кривым (Curve API)

Каждый путь (Path) состоит из массива кривых (Curve), каждая из которых соединяет два сегмента.

var curve = path.curves[0];

Свойства кривой:

  • curve.point1 — начальная точка
  • curve.point2 — конечная точка
  • curve.handle1 — первая управляющая точка
  • curve.handle2 — вторая управляющая точка

Получение точки на кривой

var point = curve.getPointAt(0.5);

Значение t = 0.5 соответствует середине кривой.

Касательная (tangent)

var tangent = curve.getTangentAt(0.5);

Используется для:

  • ориентации объектов вдоль пути
  • вычисления нормалей

Нормаль (normal)

var normal = curve.getNormalAt(0.5);

Нормаль перпендикулярна касательной.

Длина кривой

var length = curve.length;

Paper.js вычисляет длину численно, используя аппроксимацию.

Для точки на расстоянии:

var point = curve.getPointAt(length / 2);

Деление кривой

Метод divideAt разбивает кривую на две части:

var newCurve = curve.divideAt(0.5);

После вызова:

  • исходная кривая делится
  • создаётся новый сегмент

Сглаживание (smoothing)

Paper.js предоставляет встроенные алгоритмы сглаживания:

path.smooth();

Режимы:

path.smooth({
    type: 'continuous'
});

Варианты:

  • asymmetric
  • continuous
  • catmull-rom
  • geometric

Сглаживание автоматически корректирует handleIn и handleOut.

Редактирование управляющих точек

Прямое изменение

path.segments[0].handleOut.x += 20;

Перетаскивание мышью

tool.onMouseD rag = function(event) {
    path.segments[0].handleOut = event.point.subtract(path.segments[0].point);
};

Интерполяция между кривыми

var path1 = new paper.Path.Circle([100, 100], 50);
var path2 = new paper.Path.Rectangle([50, 50], [200, 200]);

var result = path1.interpolate(path2, 0.5);

Создаётся промежуточная форма.

Аппроксимация кривой

Преобразование в линейные сегменты

var flattened = path.flatten(10);

Параметр — максимальное отклонение.

Используется для:

  • экспорта в SVG
  • физики
  • коллизий

Проверка пересечений

var intersections = path1.getIntersections(path2);

Каждое пересечение содержит:

  • точку
  • параметр кривой

Кривые и производительность

Ключевые моменты:

  • большое количество сегментов замедляет отрисовку
  • сглаживание увеличивает вычисления
  • частое изменение handle требует перерасчёта

Оптимизация:

  • использовать simplify()
  • избегать лишних сегментов
  • применять flatten() при необходимости

Практические паттерны

Волнообразная линия

var path = new paper.Path();
path.strokeColor = 'black';

for (var i = 0; i < 10; i++) {
    var point = new paper.Point(i * 50, 200);
    var segment = new paper.Segment(point);
    segment.handleIn = new paper.Point(-25, i % 2 ? -50 : 50);
    segment.handleOut = new paper.Point(25, i % 2 ? 50 : -50);
    path.add(segment);
}

Анимация вдоль кривой

var position = 0;

view.onFr ame = function(event) {
    position += 1;
    var point = path.getPointAt(position);
    circle.position = point;
};

Ориентация объекта по касательной

var tangent = path.getTangentAt(position);
circle.rotation = tangent.angle;

Преобразования кривых

Кривые наследуют трансформации пути:

path.scale(1.5);
path.rotate(45);
path.translate([100, 0]);

Все управляющие точки трансформируются автоматически.

Булевы операции с кривыми

Paper.js позволяет выполнять операции:

var result = path1.unite(path2);
var result = path1.subtract(path2);
var result = path1.intersect(path2);

Результат — новый путь, состоящий из кривых.

Работа с замкнутыми кривыми

path.closed = true;

Замыкание:

  • соединяет последний сегмент с первым
  • создаёт дополнительную кривую

Итерация по кривым

path.curves.forEach(function(curve) {
    console.log(curve.length);
});

Вычисление ближайшей точки

var location = path.getNearestLocation(point);

Возвращает:

  • точку на кривой
  • индекс сегмента
  • параметр t

Глубокое понимание структуры сегментов, управляющих точек и API Curve позволяет точно контролировать геометрию, создавать сложные формы, анимации и интерактивные графические системы на основе Paper.js.