export type ChartPoint = { x: number; y: number }; function distance(a: ChartPoint, b: ChartPoint) { return Math.hypot(b.x - a.x, b.y - a.y); } function pointTowards(from: ChartPoint, to: ChartPoint, amount: number): ChartPoint { const length = distance(from, to); if (length === 0) return from; return { x: from.x + ((to.x - from.x) / length) * amount, y: from.y + ((to.y - from.y) / length) * amount, }; } function coordinate(value: number) { return Number(value.toFixed(2)); } /** * Turns the factual scan points into one continuous SVG path while rounding * only the visual corners. Source values and timestamps stay untouched; the * path merely eases into and out of each factual turning point. */ export function roundedChartPath(points: ChartPoint[], cornerRadius: number): string { if (points.length === 0) return ''; if (points.length === 1) return `M ${coordinate(points[0].x)} ${coordinate(points[0].y)}`; let path = `M ${coordinate(points[0].x)} ${coordinate(points[0].y)}`; for (let index = 1; index < points.length - 1; index += 1) { const previous = points[index - 1]; const current = points[index]; const next = points[index + 1]; const radius = Math.min( cornerRadius, distance(previous, current) / 2, distance(current, next) / 2, ); const before = pointTowards(current, previous, radius); const after = pointTowards(current, next, radius); path += ` L ${coordinate(before.x)} ${coordinate(before.y)}`; path += ` Q ${coordinate(current.x)} ${coordinate(current.y)} ${coordinate(after.x)} ${coordinate(after.y)}`; } const last = points[points.length - 1]; return `${path} L ${coordinate(last.x)} ${coordinate(last.y)}`; }