Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
quadratic bezier curve length in javascript
/*
* 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);
}
@robinrodricks
Copy link

robinrodricks commented Apr 5, 2017

More efficient version. Why create those unnecessary points?

function quadraticBezierLength(p0, p1, p2) {
    var ax = p0.x - 2 * p1.x + p2.x;
    var ay = p0.y - 2 * p1.y + p2.y;
    var bx = 2 * p1.x - 2 * p0.x;
    var by = 2 * p1.y - 2 * p0.y;
    var A = 4 * (ax * ax + ay * ay);
    var B = 4 * (ax * bx + ay * by);
    var C = bx * bx + by * by;

    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);
}

@GerkinDev
Copy link

GerkinDev commented Dec 29, 2019

How to adapt this in 3D space ?

@steveruizok
Copy link

steveruizok commented Oct 16, 2020

@robindricks This is the worst type of feedback, but I believe your code has a bug in it—though I can't find it. The point seems to be behind where it should be, compared to other functions.

This works for me:

function quadraticBezierLength(
	x1: number,
	y1: number,
	x2: number,
	y2: number,
	x3: number,
	y3: number
) {
	let a: number, b: number, c: number, u: number

	let v1x = x2 * 2
	let v1y = y2 * 2
	let d = x1 - v1x + x3
	let d1 = y1 - v1y + y3
	let e = v1x - 2 * x1
	let e1 = v1y - 2 * y1
	let c1 = (a = 4 * (d * d + d1 * d1))
	c1 += b = 4 * (d * e + d1 * e1)
	c1 += c = e * e + e1 * e1
	c1 = 2 * Math.sqrt(c1)
	let a1 = 2 * a * (u = Math.sqrt(a))
	let u1 = b / u
	a = 4 * c * a - b * b
	c = 2 * Math.sqrt(c)
	return (
		(a1 * c1 + u * b * (c1 - c) + a * Math.log((2 * u + u1 + c1) / (u1 + c))) /
		(4 * a1)
	)
}

@chknoflach
Copy link

chknoflach commented Mar 15, 2021

Thank you for this code, it works beautifully. I stumbled upon two edge cases that I had to handle:

  1. A "flat" bezier curve,
    as in both control points are on the same point along a straight line. In this case, (BA + C_2), which is used as a divisor, is zero, resulting in a division by zero. To handle this:
    // […]
    let BA = B / A_2;

    let Y = (BA + C_2) > 0 ? Math.log((2 * A_2 + BA + Sabc) / (BA + C_2)) : 0;
    
    return (A_32 * Sabc + A_2 * B * (Sabc - C_2) + (4 * C * A - B * B) * y) / (4 * A_32);
  1. {0,0}{0,0}{0,0}
    If all three points are passed as {0,0}, A_32 ends up as zero, resulting in a division by zero. To handle this:
    return A_32 === 0 ? 0 : (A_32 * Sabc + A_2 * B * (Sabc - C_2) + (4 * C * A - B * B) * Y) / (4 * A_32);

Note that JavaScript does not throw errors or warnings because it evaluates divisions by zero to "Infinity", which then results in NaN when used in further calculations. So what you end up with is the function returning "NaN" if the edge cases are not handled.

@ayadirh
Copy link

ayadirh commented Apr 6, 2021

how do you calculate the length for Bezier Curve in three dimensions? Is this the right approach? @steveruizok @chknoflach

    function quadraticBezierLength(x1, y1, z1, x2, y2, z2, x3, y3, z3) {
          let a, b, c, u
          let v1x = x2 * 2
          let v1y = y2 * 2
          let v1z = z2 * 2
          let d = x1 - v1x + x3
          let d1 = y1 - v1y + y3
          let d2 = z1 - v1z + z3
          let e = v1x - 2 * x1
          let e1 = v1y - 2 * y1
          let e2 = v1z - 2 * z1
          let c1 = (a = 4 * (d * d + d1 * d1 + d2 * d2))
          c1 += b = 4 * (d * e + d1 * e1 + d2 * e2)
          c1 += c = e * e + e1 * e1 + e2 * e2
          c1 = 2 * Math.sqrt(c1)
          let a1 = 2 * a * (u = Math.sqrt(a))
          let u1 = b / u
          a = 4 * c * a - b * b
          c = 2 * Math.sqrt(c)
          return (
            (a1 * c1 + u * b * (c1 - c) + a * Math.log((2 * u + u1 + c1) / (u1 + c))) /
            (4 * a1)
          )
    }

@bczhc
Copy link

bczhc commented May 17, 2021

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment