svg-path-properties
Version:
Calculate the length for an SVG path, to use it with node or a Canvas element
107 lines (94 loc) • 3.06 kB
text/typescript
import { Properties, Point } from './types.ts'
import {
cubicPoint,
getCubicArcLength,
cubicDerivative,
getQuadraticArcLength,
quadraticPoint,
quadraticDerivative,
t2length
} from './bezier-functions.ts'
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
}
}