/** * Given a scale ( bp/px ) and minimum distances (px) between major and minor * gridlines, return an object like `{ majorPitch: bp, minorPitch: bp }` giving * the gridline pitches to use. */ export function chooseGridPitch( scale: number, minMajorPitchPx: number, minMinorPitchPx: number, ) { scale = Math.abs(scale) const minMajorPitchBp = minMajorPitchPx * scale const majorMagnitude = Number.parseInt( minMajorPitchBp.toExponential().split(/e/i)[1]!, 10, ) let majorPitch = 10 ** majorMagnitude while (majorPitch < minMajorPitchBp) { majorPitch *= 2 if (majorPitch >= minMajorPitchBp) { break } majorPitch *= 2.5 } majorPitch = Math.max(majorPitch, 5) const majorPitchPx = majorPitch / scale let minorPitch = 0 if (!(majorPitch % 10) && majorPitchPx / 10 >= minMinorPitchPx) { minorPitch = majorPitch / 10 } else if (!(majorPitch % 5) && majorPitchPx / 5 >= minMinorPitchPx) { minorPitch = majorPitch / 5 } else if (!(majorPitch % 2) && majorPitchPx / 2 >= minMinorPitchPx) { minorPitch = majorPitch / 2 } return { majorPitch, minorPitch } } export function makeTicks( start: number, end: number, bpPerPx: number, emitMajor = true, emitMinor = true, ) { const gridPitch = chooseGridPitch(bpPerPx, 60, 15) let minBase = start let maxBase = end if (bpPerPx < 0) { ;[minBase, maxBase] = [maxBase, minBase] } // add 20px additional on the right and left to allow us to draw the ends of // labels that lie a little outside our region minBase -= Math.abs(20 * bpPerPx) maxBase += Math.abs(20 * bpPerPx) + 1 const iterPitch = gridPitch.minorPitch || gridPitch.majorPitch let index = 0 const ticks = [] for ( let base = Math.ceil(minBase / iterPitch) * iterPitch; base < maxBase; base += iterPitch ) { if (emitMinor && base % (gridPitch.majorPitch * 2)) { ticks.push({ type: 'minor', base: base - 1, index }) index += 1 } else if (emitMajor && !(base % (gridPitch.majorPitch * 2))) { ticks.push({ type: 'major', base: base - 1, index }) index += 1 } } return ticks } export function mathPower(num: number): string { if (num < 999) { return String(num) } return `${mathPower(~~(num / 1000))},${`00${~~(num % 1000)}`.slice( -3, -3 + 3, )}` }