ttrpg-tools/src/plotcutter/bezier.ts

49 lines
1.3 KiB
TypeScript

export type Pt = [number, number];
export function cubicBezierCommand(pts: Pt[], p1: Pt, p2: Pt, p3: Pt){
const p0 = pts[pts.length - 1];
if (!p0) return pts;
const [b1p0, b1p1, b1p2] = getMidPoints(p0, p1, p2, p3);
const [b2p0, b2p1] = getMidPoints(b1p0, b1p1, b1p2);
const [b3p0] = getMidPoints(b2p0, b2p1);
const a1 = Math.atan2(b3p0[1] - p0[1], b3p0[0] - p0[0]);
const a2 = Math.atan2(p3[1] - b3p0[1], p3[0] - b3p0[0]);
const d = a2 - a1 - Math.round((a2 - a1) / Math.PI / 2) * Math.PI * 2;
if (isNaN(d)) {
console.error('NaN found', { d, a2, a1, p0, p1, p2, p3 });
return pts;
}
const d03 = sqdist(p0, p3);
if (d * d * d03 < Math.PI * Math.PI / 18 / 18) {
pts.push(p3);
return pts;
}
cubicBezierCommand(pts, b1p0, b2p0, b3p0);
pts.push(b3p0);
cubicBezierCommand(pts, b2p1, b1p2, p3);
return pts;
}
function sqdist(pt: Pt, to: Pt) {
const x = pt[0] - to[0];
const y = pt[1] - to[1];
return x * x + y * y;
}
function getMidPoints(...pts: Pt[]){
const mps = [] as typeof pts;
for(let i = 1; i < pts.length; i ++){
mps[i-1] = [
(pts[i][0] + pts[i-1][0])/2,
(pts[i][1] + pts[i-1][1])/2,
];
}
return mps;
}