Подсчет длины кривой Безье

Как посчитать длину кубической Безье с 4 точками? Нашел подсчет для квадратичной Безье

var v = {};
var w = {};

v.x = 2*(p2.x - p1.x);
v.y = 2*(p2.y - p1.y);
w.x = p3.x - 2*p2.x + p1.x;
w.y = p3.y - 2*p2.y + p1.y;

var uu = 4*(Math.pow(w.x, 2) + Math.pow(w.y, 2));

if(uu < 0.00001)
{
    console.log(Math.sqrt(Math.pow(p3.x - p1.x, 2) + Math.pow(p3.y - p1.y, 2)));
}
else
{
    var vv = 4*(v.x*w.x + v.y*w.y);
    var ww = v.x*v.x + v.y*v.y;

    var t1 = (2*Math.sqrt(uu*(uu + vv + ww)));
    var t2 = 2*uu+vv;
    var t3 = vv*vv - 4*uu*ww;
    var t4 = (2*Math.sqrt(uu*ww));

    console.log(((t1*t2 - t3*Math.log(t2+t1) -(vv*t4 - t3*Math.log(vv+t4))) / (8*Math.pow(uu, 1.5))));
}

Может как-то можно сделать и для кубической?


Ответы (2 шт):

Автор решения: MBo

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

Можно делить кривую на кусочки пополам рекурсивно, пока они не станут достаточно плоскими - это стандартный процесс Bezier subdivision, и на последнем уровне вычислять длины отрезков (можно также оценивать длину дуги по хорде и апофеме). Возможно, у вас есть какой-то готовый метод типа Flatten (представить кривую как набор прямых отрезков), иначе это вполне реально сделать вручную.

Альтернатива - выполнить численное интегрирование. Например, в квадратурах Гаусса - опять же, придётся делить кривую на куски, пока не будет достигнута нужная точность.

→ Ссылка
Автор решения: Leonid

Можно довериться браузуру, передать путь кубической Безье в SVG в качества атрибута d элемента path и взять длину кривой с помощью path.getTotalLength().

SVG скрыт, но для наглядность кривая отрисовывается на холсте (canvas).

Четыре точки кубической Безье в данном случае: [10 100] - начало, [30,40] - первая контрольная точка, [180,20] - вторая контрольная точка, [300,100] - конец кривой.

В виде строки можно сформировать так:

`M ${begin.x} ${begin.y} C ${control1.x} ${control1.y} ${control2.x} ${control2.y} ${end.x} ${end.y}`

const curve = "M 10 100 C 30 40 180 20 300 100";

document.getElementById('canvas').getContext('2d').stroke(new Path2D(curve));

const path = document.querySelector('path');
path.setAttribute('d', curve);
document.querySelector('p').textContent = path.getTotalLength();
<canvas id="canvas"></canvas>
<p id="curve-length" style="position:fixed;top:0;right:0"></p>
<svg id="svg" style="display:none"><path></path></svg>

→ Ссылка