Last active
December 15, 2023 10:13
-
-
Save tunght13488/6744e77c242cc7a94859 to your computer and use it in GitHub Desktop.
quadratic bezier curve length in javascript
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* http://en.wikipedia.org/wiki/B%C3%A9zier_curve | |
* http://www.malczak.linuxpl.com/blog/quadratic-bezier-curve-length/g | |
*/ | |
function Point(x, y) { | |
this.x = x; | |
this.y = y; | |
} | |
function quadraticBezierLength(p0, p1, p2) { | |
var a = new Point( | |
p0.x - 2 * p1.x + p2.x, | |
p0.y - 2 * p1.y + p2.y | |
); | |
var b = new Point( | |
2 * p1.x - 2 * p0.x, | |
2 * p1.y - 2 * p0.y | |
); | |
var A = 4 * (a.x * a.x + a.y * a.y); | |
var B = 4 * (a.x * b.x + a.y * b.y); | |
var C = b.x * b.x + b.y * b.y; | |
var Sabc = 2 * sqrt(A+B+C); | |
var A_2 = sqrt(A); | |
var A_32 = 2 * A * A_2; | |
var C_2 = 2 * sqrt(C); | |
var BA = B / A_2; | |
return (A_32 * Sabc + A_2 * B * (Sabc - C_2) + (4 * C * A - B * B) * log((2 * A_2 + BA + Sabc) / (BA + C_2))) / (4 * A_32); | |
} |
As commented before, we should also check for actually linear commands:
/**
* p0: previous command's final point
* cp1: quadratic bézier control point
* p: final point
*/
function quadraticBezierLength(p0, cp1, p, t = 1) {
if (t === 0) {
return 0;
}
const interpolate = (p1, p2, t) => {
let pt = { x: (p2.x - p1.x) * t + p1.x, y: (p2.y - p1.y) * t + p1.y };
return pt;
}
const getLineLength = (p1, p2) => {
return Math.sqrt(
(p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y)
);
}
// is flat/linear
let l1 = getLineLength(p0, cp1) + getLineLength(cp1, p);
let l2 = getLineLength(p0, p);
if (l1 === l2) {
let m1 = interpolate(p0, cp1, t);
let m2 = interpolate(cp1, p, t);
p = interpolate(m1, m2, t);
let lengthL;
lengthL = Math.sqrt((p.x - p0.x) * (p.x - p0.x) + (p.y - p0.y) * (p.y - p0.y));
return lengthL;
}
let a, b, c, d, e, e1, d1, v1x, v1y;
v1x = cp1.x * 2;
v1y = cp1.y * 2;
d = p0.x - v1x + p.x;
d1 = p0.y - v1y + p.y;
e = v1x - 2 * p0.x;
e1 = v1y - 2 * p0.y;
a = 4 * (d * d + d1 * d1);
b = 4 * (d * e + d1 * e1);
c = e * e + e1 * e1;
const bt = b / (2 * a),
ct = c / a,
ut = t + bt,
k = ct - bt ** 2;
return (
(Math.sqrt(a) / 2) *
(ut * Math.sqrt(ut ** 2 + k) -
bt * Math.sqrt(bt ** 2 + k) +
k *
Math.log((ut + Math.sqrt(ut ** 2 + k)) / (bt + Math.sqrt(bt ** 2 + k))))
);
}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@chknoflach Yes but in the first case you mentioned, if P0 is (10, 10), P1 is (10.00001, 10.00012), P2 is (20, 20), the divisor will be so small and the division result will be so big, makes a poor precision as a
double
number. It's annoying for me to process these points data from users drawing on a GUI.