Вычисление длины пути

Для работы с Two.js необходимо создать экземпляр библиотеки и привязать его к контейнеру на странице. Основной объект Two управляет сценой, элементами и рендерингом:

const elem = document.getElementById('draw-area');
const two = new Two({
  width: 800,
  height: 600,
  type: Two.Types.svg // поддержка SVG или Canvas
}).appendTo(elem);

Two.Types позволяет выбрать между svg и canvas, что влияет на производительность и возможности анимации. После инициализации создается пространство для отрисовки фигур и линий.


Создание пути (Path)

В Two.js путь (Two.Path) — это последовательность векторов, соединенных линиями или кривыми. Основная структура пути:

const path = new Two.Path([
  new Two.Anchor(100, 100),
  new Two.Anchor(200, 150),
  new Two.Anchor(150, 250)
], false, false);
two.add(path);

Аргументы конструктора:

  1. Массив Anchor — точки пути. Каждая точка может иметь контрольные точки для кривых Безье.
  2. closed — булевое значение, замкнут путь или нет.
  3. curved — определяет, будут ли линии между точками кривыми.

Доступ к точкам и сегментам

Каждый путь содержит массив vertices, который хранит объекты Vector. Длина пути вычисляется через последовательное суммирование длин сегментов между точками:

path.vertices.forEach((vertex, i, vertices) => {
  if (i > 0) {
    const prev = vertices[i - 1];
    const dx = vertex.x - prev.x;
    const dy = vertex.y - prev.y;
    const segmentLength = Math.sqrt(dx * dx + dy * dy);
    console.log(`Длина сегмента ${i}: ${segmentLength}`);
  }
});
  • dx и dy — смещения по осям X и Y.
  • Math.sqrt(dx*dx + dy*dy) — стандартная формула Евклидовой длины линии.

Вычисление полной длины пути

Чтобы получить суммарную длину всего пути, необходимо пройти по всем сегментам и сложить их длины:

function getPathLength(path) {
  let totalLength = 0;
  const vertices = path.vertices;
  for (let i = 1; i < vertices.length; i++) {
    const dx = vertices[i].x - vertices[i - 1].x;
    const dy = vertices[i].y - vertices[i - 1].y;
    totalLength += Math.sqrt(dx * dx + dy * dy);
  }
  if (path.closed) {
    const dx = vertices[0].x - vertices[vertices.length - 1].x;
    const dy = vertices[0].y - vertices[vertices.length - 1].y;
    totalLength += Math.sqrt(dx * dx + dy * dy);
  }
  return totalLength;
}

console.log("Общая длина пути:", getPathLength(path));

Особенности:

  • Если путь замкнут (closed = true), нужно добавить длину сегмента между последней и первой точкой.
  • Для кривых Безье простое суммирование сегментов недостаточно точное; используется аппроксимация через деление кривой на множество маленьких отрезков.

Аппроксимация кривых для точной длины

Кривые Безье в Two.js хранятся как anchor.controls.left и anchor.controls.right. Для вычисления длины кривой применяется разбиение на N отрезков:

function bezierLength(anchor1, anchor2, steps = 100) {
  let length = 0;
  let prev = anchor1;
  for (let i = 1; i <= steps; i++) {
    const t = i / steps;
    const x = Math.pow(1 - t, 3) * anchor1.x +
              3 * Math.pow(1 - t, 2) * t * (anchor1.x + anchor1.controls.right.x) +
              3 * (1 - t) * t * t * (anchor2.x + anchor2.controls.left.x) +
              t * t * t * anchor2.x;
    const y = Math.pow(1 - t, 3) * anchor1.y +
              3 * Math.pow(1 - t, 2) * t * (anchor1.y + anchor1.controls.right.y) +
              3 * (1 - t) * t * t * (anchor2.y + anchor2.controls.left.y) +
              t * t * t * anchor2.y;
    const dx = x - prev.x;
    const dy = y - prev.y;
    length += Math.sqrt(dx * dx + dy * dy);
    prev = { x, y };
  }
  return length;
}
  • steps — количество разбиений кривой, большее число дает точнее длину.
  • Используется формула кубической кривой Безье для расчета каждой точки.

Интеграция аппроксимации в путь

Для суммарной длины пути с кривыми:

function getFullPathLength(path) {
  let total = 0;
  const vertices = path.vertices;
  for (let i = 1; i < vertices.length; i++) {
    if (path.curved) {
      total += bezierLength(vertices[i - 1], vertices[i]);
    } else {
      const dx = vertices[i].x - vertices[i - 1].x;
      const dy = vertices[i].y - vertices[i - 1].y;
      total += Math.sqrt(dx * dx + dy * dy);
    }
  }
  if (path.closed) {
    if (path.curved) {
      total += bezierLength(vertices[vertices.length - 1], vertices[0]);
    } else {
      const dx = vertices[0].x - vertices[vertices.length - 1].x;
      const dy = vertices[0].y - vertices[vertices.length - 1].y;
      total += Math.sqrt(dx * dx + dy * dy);
    }
  }
  return total;
}

console.log("Длина пути с кривыми:", getFullPathLength(path));

Практические советы

  • Для динамически изменяющихся фигур длину пути можно пересчитывать каждый кадр анимации.
  • Аппроксимация кривой позволяет использовать длину для анимации вдоль пути или расчета прогресса.
  • Оптимизация возможна уменьшением числа шагов для кривых при визуально приемлемой точности.