geometry_Bezier.js

"use strict";
const vec = require("../geometry/vec");

/**
 * A bezier curve 
 */
class Bezier {
	constructor(pt1, cp1, cp2, pt2) { // start, control 1, control 2, end
		// https://javascript.info/bezier-curve
		// P = ((1−t)^3 * P1) + (3(1−t)^2 * t * P2) + (3(1−t) * t^2 * P3) + (t^3 * P4)
		// arc length = ∫a^b √[1 + (dy/dx)^2] dx
		// arc length = ∫a^b

		if (pt1.a && pt1.b) {
			this.a = new vec(pt1.a);
			this.b = new vec(pt1.b);
			this.c = new vec(pt1.c);
			this.d = new vec(pt1.d);
		}
		else {
			this.a = new vec(pt1);
			this.b = new vec(cp1);
			this.c = new vec(cp2);
			this.d = new vec(pt2);
		}

		this.length = this.getLength();
	}
	getAtT(t) {
		let x = (this.a.x * (1 - t)**3) + (3*this.b.x * t * (1 - t)**2) + (3*this.c.x * (1 - t) * t**2) + (this.d.x * t**3);
		let y = (this.a.y * (1 - t)**3) + (3*this.b.y * t * (1 - t)**2) + (3*this.c.y * (1 - t) * t**2) + (this.d.y * t**3);

		return new vec(x, y);
	}
	getLength(dt = 0.01) {
		let lastPt = this.getAtT(0);
		let len = 0;
		for (let t = dt; t <= 1; t += dt) {
			let pt = this.getAtT(t);
			len += pt.sub(lastPt).length;
			lastPt = pt;
		}
		len += this.getAtT(1).sub(lastPt).length;

		return len;
	}
	get(d) {
		return this.getAtT(d / this.length);
	}
	getDxAtT(t) { // 1st derivative
		let x = 3 * ((this.d.x - 3*this.c.x + 3*this.b.x - this.a.x) * t ** 2 + (2*this.c.x - 4*this.b.x + 2*this.a.x) * t + this.b.x - this.a.x);
		let y = 3 * ((this.d.y - 3*this.c.y + 3*this.b.y - this.a.y) * t ** 2 + (2*this.c.y - 4*this.b.y + 2*this.a.y) * t + this.b.y - this.a.y);

		return new vec(x, y);
	}
	getDx(d) {
		return this.getDxAtT(d / this.length);
	}
	getDx2AtT(t) { // 2nd derivative
		let x = 6 * ((this.d.x - 3*this.c.x + 3*this.b.x - this.a.x) * t + this.c.x - 2*this.b.x + this.a.x);
		let y = 6 * ((this.d.y - 3*this.c.y + 3*this.b.y - this.a.y) * t + this.c.y - 2*this.b.y + this.a.y);

		return new vec(x, y);
	}
	getDx2(d) {
		return this.getDx2AtT(d / this.length);
	}

	toObject() {
		return {
			a: this.a.toObject(),
			b: this.b.toObject(),
			c: this.c.toObject(),
			d: this.d.toObject(),
		};
	}
}
module.exports = Bezier;