Numeric Helpers
Isomorphic data-viz math: clamp, lerp, invLerp, mapRange, round, sqrtScale (softer delta bars), and smoothPath (Catmull-Rom to bezier for smooth line/area charts).
// Isomorphic numeric helpers used across data-viz.
export const clamp = (v: number, lo: number, hi: number): number =>
v < lo ? lo : v > hi ? hi : v;
export const lerp = (a: number, b: number, t: number): number => a + (b - a) * t;
/** Inverse lerp: where does v sit in [a,b] as 0..1 (clamped). */
export const invLerp = (a: number, b: number, v: number): number =>
b === a ? 0 : clamp((v - a) / (b - a), 0, 1);
/** Remap v from [inMin,inMax] to [outMin,outMax]. */
export const mapRange = (
v: number,
inMin: number,
inMax: number,
outMin: number,
outMax: number,
): number => lerp(outMin, outMax, invLerp(inMin, inMax, v));
/** Round to n decimal places. */
export const round = (v: number, n = 0): number => {
const p = 10 ** n;
return Math.round(v * p) / p;
};
/** Perceptually softer compression for delta bars (matches the broadcast look). */
export const sqrtScale = (v: number): number => Math.sign(v) * Math.sqrt(Math.abs(v));
/** Catmull-Rom → cubic-bezier path data through points (smooth area/line charts). */
export function smoothPath(pts: { x: number; y: number }[]): string {
if (pts.length < 2) return "";
let d = "";
for (let i = 0; i < pts.length - 1; i++) {
const p0 = pts[i - 1] || pts[i];
const p1 = pts[i];
const p2 = pts[i + 1];
const p3 = pts[i + 2] || p2;
const c1x = p1.x + (p2.x - p0.x) / 6;
const c1y = p1.y + (p2.y - p0.y) / 6;
const c2x = p2.x - (p3.x - p1.x) / 6;
const c2y = p2.y - (p3.y - p1.y) / 6;
d += `C ${c1x.toFixed(2)} ${c1y.toFixed(2)} ${c2x.toFixed(2)} ${c2y.toFixed(2)} ${p2.x.toFixed(2)} ${p2.y.toFixed(2)} `;
}
return d;
}