blob: 0364a10791188638f5328e893f42994d270ca8a3 [file] [log] [blame]
// Copyright (C) 2018 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import {assertTrue} from '../base/logging';
import {duration, time, Time, TimeSpan} from '../base/time';
const micros = 1000n;
const millis = 1000n * micros;
const seconds = 1000n * millis;
const minutes = 60n * seconds;
const hours = 60n * minutes;
const days = 24n * hours;
// These patterns cover the entire range of 0 - 2^63-1 nanoseconds
const patterns: [bigint, string][] = [
[1n, '|'],
[2n, '|:'],
[5n, '|....'],
[10n, '|....:....'],
[20n, '|.:.'],
[50n, '|....'],
[100n, '|....:....'],
[200n, '|.:.'],
[500n, '|....'],
[1n * micros, '|....:....'],
[2n * micros, '|.:.'],
[5n * micros, '|....'],
[10n * micros, '|....:....'],
[20n * micros, '|.:.'],
[50n * micros, '|....'],
[100n * micros, '|....:....'],
[200n * micros, '|.:.'],
[500n * micros, '|....'],
[1n * millis, '|....:....'],
[2n * millis, '|.:.'],
[5n * millis, '|....'],
[10n * millis, '|....:....'],
[20n * millis, '|.:.'],
[50n * millis, '|....'],
[100n * millis, '|....:....'],
[200n * millis, '|.:.'],
[500n * millis, '|....'],
[1n * seconds, '|....:....'],
[2n * seconds, '|.:.'],
[5n * seconds, '|....'],
[10n * seconds, '|....:....'],
[30n * seconds, '|.:.:.'],
[1n * minutes, '|.....'],
[2n * minutes, '|.:.'],
[5n * minutes, '|.....'],
[10n * minutes, '|....:....'],
[30n * minutes, '|.:.:.'],
[1n * hours, '|.....'],
[2n * hours, '|.:.'],
[6n * hours, '|.....'],
[12n * hours, '|.....:.....'],
[1n * days, '|.:.'],
[2n * days, '|.:.'],
[5n * days, '|....'],
[10n * days, '|....:....'],
[20n * days, '|.:.'],
[50n * days, '|....'],
[100n * days, '|....:....'],
[200n * days, '|.:.'],
[500n * days, '|....'],
[1000n * days, '|....:....'],
[2000n * days, '|.:.'],
[5000n * days, '|....'],
[10000n * days, '|....:....'],
[20000n * days, '|.:.'],
[50000n * days, '|....'],
[100000n * days, '|....:....'],
[200000n * days, '|.:.'],
];
// Returns the optimal step size and pattern of ticks within the step.
export function getPattern(minPatternSize: bigint): [duration, string] {
for (const [size, pattern] of patterns) {
if (size >= minPatternSize) {
return [size, pattern];
}
}
throw new Error('Pattern not defined for this minsize');
}
function tickPatternToArray(pattern: string): TickType[] {
const array = Array.from(pattern);
return array.map((char) => {
switch (char) {
case '|':
return TickType.MAJOR;
case ':':
return TickType.MEDIUM;
case '.':
return TickType.MINOR;
default:
// This is almost certainly a developer/fat-finger error
throw Error(`Invalid char "${char}" in pattern "${pattern}"`);
}
});
}
export enum TickType {
MAJOR,
MEDIUM,
MINOR,
}
export interface Tick {
type: TickType;
time: time;
}
export const MIN_PX_PER_STEP = 120;
export function getMaxMajorTicks(width: number) {
return Math.max(1, Math.floor(width / MIN_PX_PER_STEP));
}
export function* generateTicks(
timeSpan: TimeSpan,
maxMajorTicks: number,
offset: time = Time.ZERO,
): Generator<Tick> {
assertTrue(timeSpan.duration > 0n, 'timeSpan.duration cannot be lte 0');
assertTrue(maxMajorTicks > 0, 'maxMajorTicks cannot be lte 0');
timeSpan = timeSpan.translate(-offset);
const minStepSize = BigInt(
Math.floor(Number(timeSpan.duration) / maxMajorTicks),
);
const [patternSize, pattern] = getPattern(minStepSize);
const tickPattern = tickPatternToArray(pattern);
const stepSize = patternSize / BigInt(tickPattern.length);
const start = Time.quantFloor(timeSpan.start, patternSize);
const end = timeSpan.end;
let patternIndex = 0;
for (
let time = start;
time < end;
time = Time.add(time, stepSize), patternIndex++
) {
if (time >= timeSpan.start) {
patternIndex = patternIndex % tickPattern.length;
const type = tickPattern[patternIndex];
yield {type, time: Time.add(time, offset)};
}
}
}