// Map utility functions /** * Deep clone an object using JSON serialization */ export function clone(obj: T): T { return JSON.parse(JSON.stringify(obj)); } /** * Calculate the value at a given percentile in a sorted array * @param arr Sorted array of numbers * @param p Percentile (0-1) */ export function percentile(arr: number[], p: number): number { if (arr.length === 0) return 0; if (typeof p !== 'number') throw new TypeError('p must be a number'); if (p <= 0) return arr[0]; if (p >= 1) return arr[arr.length - 1]; const index = arr.length * p; const lower = Math.floor(index); const upper = lower + 1; const weight = index % 1; if (upper >= arr.length) return arr[lower]; return arr[lower] * (1 - weight) + arr[upper] * weight; } /** * Convert percentage-based color stops to value-based color stops * @param colorStopsPerc Array of [percentage, color] tuples * @param min Minimum value * @param max Maximum value */ export function calculateColorStops( colorStopsPerc: [number, string][], min: number, max: number ): [number, string][] { return colorStopsPerc.map(([perc, color]) => [ min + (perc * (max - min)) / 100, color, ]); }