@liammartens/svg-path-properties
Version:
Calculate the length for an SVG path, to use it with node or a Canvas element
102 lines (94 loc) • 3.09 kB
text/typescript
import { Properties, Point } from "./types";
import {
cubicPoint,
getCubicArcLength,
cubicDerivative,
getQuadraticArcLength,
quadraticPoint,
quadraticDerivative,
t2length
} from "./bezier-functions";
export class Bezier implements Properties {
private a: Point;
private b: Point;
private c: Point;
private d: Point;
private length: number;
private getArcLength: (xs: number[], ys: number[], t: number) => number;
private getPoint: (xs: number[], ys: number[], t: number) => Point;
private getDerivative: (xs: number[], ys: number[], t: number) => Point;
constructor(
ax: number,
ay: number,
bx: number,
by: number,
cx: number,
cy: number,
dx: number | undefined,
dy: number | undefined
) {
this.a = { x: ax, y: ay };
this.b = { x: bx, y: by };
this.c = { x: cx, y: cy };
if (dx !== undefined && dy !== undefined) {
this.getArcLength = getCubicArcLength;
this.getPoint = cubicPoint;
this.getDerivative = cubicDerivative;
this.d = { x: dx, y: dy };
} else {
this.getArcLength = getQuadraticArcLength;
this.getPoint = quadraticPoint;
this.getDerivative = quadraticDerivative;
this.d = { x: 0, y: 0 };
}
this.length = this.getArcLength(
[this.a.x, this.b.x, this.c.x, this.d.x],
[this.a.y, this.b.y, this.c.y, this.d.y],
1
);
}
public getTotalLength = () => {
return this.length;
};
public getPointAtLength = (length: number) => {
const xs = [this.a.x, this.b.x, this.c.x, this.d.x];
const xy = [this.a.y, this.b.y, this.c.y, this.d.y];
const t = t2length(length, this.length, i => this.getArcLength(xs, xy, i));
return this.getPoint(xs, xy, t);
};
public getTangentAtLength = (length: number) => {
const xs = [this.a.x, this.b.x, this.c.x, this.d.x];
const xy = [this.a.y, this.b.y, this.c.y, this.d.y];
const t = t2length(length, this.length, i => this.getArcLength(xs, xy, i));
const derivative = this.getDerivative(xs, xy, t);
const mdl = Math.sqrt(derivative.x * derivative.x + derivative.y * derivative.y);
let tangent: Point;
if (mdl > 0) {
tangent = { x: derivative.x / mdl, y: derivative.y / mdl };
} else {
tangent = { x: 0, y: 0 };
}
return tangent;
};
public getPropertiesAtLength = (length: number) => {
const xs = [this.a.x, this.b.x, this.c.x, this.d.x];
const xy = [this.a.y, this.b.y, this.c.y, this.d.y];
const t = t2length(length, this.length, i => this.getArcLength(xs, xy, i));
const derivative = this.getDerivative(xs, xy, t);
const mdl = Math.sqrt(derivative.x * derivative.x + derivative.y * derivative.y);
let tangent: Point;
if (mdl > 0) {
tangent = { x: derivative.x / mdl, y: derivative.y / mdl };
} else {
tangent = { x: 0, y: 0 };
}
const point = this.getPoint(xs, xy, t);
return { x: point.x, y: point.y, tangentX: tangent.x, tangentY: tangent.y };
};
public getC = () => {
return this.c;
};
public getD = () => {
return this.d;
};
}