UNPKG

@tanstack/charts

Version:

A chart grammar for TypeScript and JavaScript. Marks consume your data directly, channels describe visual encodings, and the engine compiles them into a renderer-neutral keyed scene. TanStack's compact scales cover common numeric and categorical mappings.

76 lines (75 loc) 2.69 kB
function allocateProportionalIntervals(weights, options = {}) { const start = options.start ?? 0; const end = options.end ?? 1; const gap = options.gap ?? 0; assertFinite(start, "start"); assertFinite(end, "end"); assertNonnegativeFinite(gap, "gap"); const span = end - start; if (!Number.isFinite(span)) { throw new TypeError("proportional intervals: extent span must be finite"); } let positiveCount = 0; let unscaledTotal = 0; let maximum = 0; weights.forEach((weight, index) => { if (!Number.isFinite(weight) || weight < 0) { throw new TypeError( `proportional intervals: weight at index ${index} must be nonnegative and finite` ); } if (weight > 0) positiveCount += 1; unscaledTotal += weight; maximum = Math.max(maximum, weight); }); const valueScale = Number.isFinite(unscaledTotal) ? 1 : maximum; const total = valueScale === 1 ? unscaledTotal : weights.reduce((sum, weight) => sum + weight / valueScale, 0); const absoluteSpan = Math.abs(span); const gapCount = positiveCount === 0 ? 0 : Math.max(0, positiveCount - 1) + (options.gapAfterLast === true ? 1 : 0); const totalGap = gapCount * gap; if (!Number.isFinite(totalGap) || totalGap > absoluteSpan) { throw new TypeError( "proportional intervals: gap leaves insufficient extent" ); } const drawableSpan = absoluteSpan - totalGap; if (positiveCount > 0 && drawableSpan <= 0) { throw new TypeError( "proportional intervals: positive weights require drawable extent" ); } const direction = span < 0 ? -1 : 1; const intervals = []; let cursor = start; let remainingPositive = positiveCount; for (const weight of weights) { const fraction = total === 0 ? 0 : weight / valueScale / total; const intervalStart = cursor; let intervalEnd = cursor; if (weight > 0) { remainingPositive -= 1; intervalEnd = remainingPositive === 0 ? end - (options.gapAfterLast === true ? direction * gap : 0) : cursor + direction * drawableSpan * fraction; cursor = intervalEnd; if (remainingPositive > 0 || options.gapAfterLast === true) { cursor += direction * gap; } } intervals.push({ fraction, start: intervalStart, end: intervalEnd }); } return intervals; } function assertFinite(value, name) { if (!Number.isFinite(value)) { throw new TypeError(`proportional intervals: ${name} must be finite`); } } function assertNonnegativeFinite(value, name) { if (!Number.isFinite(value) || value < 0) { throw new TypeError( `proportional intervals: ${name} must be nonnegative and finite` ); } } export { allocateProportionalIntervals };