import { Domain, YRange, Feature } from './types'; export function m(x: number, y: number): string { return ` M ${x} ${y}`; } export function l(x: number, y: number): string { return ` L ${x} ${y}`; } /** * Creates a closure for mapping values into an X coordinate space. * @param domain object with the start and end coordinates of the original values. * @param width the maximum value of the output space. */ export function xtransform(domain: Domain, width: number): (i: number) => number { return (i: number) => (i - domain.start) * width / (domain.end - domain.start); } /** * Creates a linear scale for mapping values into a Y coordinate space. * @param range object with the max and min coordinates of the original values. * @param height the maximum value of the output space. */ export function ytransform(range: YRange, height: number): (i: number) => number { return (i: number) => ( range.max === range.min ? 0 : (range.max - i) * height / (range.max - range.min) ); } /** * Creates a linear scale for mapping values from one coordinate space to another. * @param indomain the original domain. * @param outdomain the output domain. */ export function linearTransform(inDomain: Domain, outDomain: Domain): (value: number) => number { const iwidth = inDomain.end - inDomain.start; const owidth = outDomain.end - outDomain.start; return i => (i - inDomain.start) * owidth / iwidth + outDomain.start; }; /** * Creates a linear scale for mapping track input coordinates to an output x coordinate range. If the * domain is not explicitly given it will be computed from the minima and maxima of the passed track data. * @param domain object with the start and end coordinates to set, or null. * @param data optional array of objects with start and end coordinates to compute the domain. * @param width the maximum value of the output space. */ export function trackXTransform(domain: Domain, data: Domain[], width: number) { return domain ? xtransform(domain, width) : xtransform( data.reduce( (domain, v) => ({ start: v.start < domain.start ? v.start : domain.start, end: v.end > domain.end ? v.end : domain.end }), { start: Infinity, end: -Infinity }), width ); } /** * Creates a linear scale for mapping track input coordinates to an output x coordinate range. If the * domain is not explicitly given it will be computed from the minima and maxima of the passed track data. * @param domain object with the start and end coordinates to set, or null. * @param data optional array of objects with start and end coordinates to compute the domain. * @param width the maximum value of the output space. */ export function trackYTransform(range: YRange, data: YRange[], height: number) { return range ? ytransform(range, height) : ytransform( data.reduce( (range, v) => ({ min: v.min < range.min ? v.min : range.min, max: v.max > range.max ? v.max : range.max }), { min: Infinity, max: -Infinity }), height ); } export function groupFeatures(features: T[], x: (value: number) => number, fontSize: number, margin: number | undefined = 10): T[][] { fontSize = fontSize || 0; return features.reduce( (cpacked, feature) => { let foundmatch = false; for (let i = 0; i < cpacked.length; ++i) { if (x(cpacked[i][cpacked[i].length - 1].coordinates.end) + margin + fontSize * cpacked[i][cpacked[i].length - 1].name.length <= x(feature.coordinates.start)) { cpacked[i].push(feature); foundmatch = true; break; } } if (!foundmatch) cpacked.push([ feature ]); return cpacked; }, []); } export function formatPoint(point?: number): string { return point !== undefined ? ("" + (point < 100000 ? (point === Math.floor(point) ? point : point.toFixed(2)) : point.toExponential(2))) : "" }