xref: /aosp_15_r20/external/perfetto/ui/src/frontend/gridline_helper.ts (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1*6dbdd20aSAndroid Build Coastguard Worker// Copyright (C) 2018 The Android Open Source Project
2*6dbdd20aSAndroid Build Coastguard Worker//
3*6dbdd20aSAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License");
4*6dbdd20aSAndroid Build Coastguard Worker// you may not use this file except in compliance with the License.
5*6dbdd20aSAndroid Build Coastguard Worker// You may obtain a copy of the License at
6*6dbdd20aSAndroid Build Coastguard Worker//
7*6dbdd20aSAndroid Build Coastguard Worker//      http://www.apache.org/licenses/LICENSE-2.0
8*6dbdd20aSAndroid Build Coastguard Worker//
9*6dbdd20aSAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software
10*6dbdd20aSAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS,
11*6dbdd20aSAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*6dbdd20aSAndroid Build Coastguard Worker// See the License for the specific language governing permissions and
13*6dbdd20aSAndroid Build Coastguard Worker// limitations under the License.
14*6dbdd20aSAndroid Build Coastguard Worker
15*6dbdd20aSAndroid Build Coastguard Workerimport {assertTrue} from '../base/logging';
16*6dbdd20aSAndroid Build Coastguard Workerimport {duration, time, Time, TimeSpan} from '../base/time';
17*6dbdd20aSAndroid Build Coastguard Worker
18*6dbdd20aSAndroid Build Coastguard Workerconst micros = 1000n;
19*6dbdd20aSAndroid Build Coastguard Workerconst millis = 1000n * micros;
20*6dbdd20aSAndroid Build Coastguard Workerconst seconds = 1000n * millis;
21*6dbdd20aSAndroid Build Coastguard Workerconst minutes = 60n * seconds;
22*6dbdd20aSAndroid Build Coastguard Workerconst hours = 60n * minutes;
23*6dbdd20aSAndroid Build Coastguard Workerconst days = 24n * hours;
24*6dbdd20aSAndroid Build Coastguard Worker
25*6dbdd20aSAndroid Build Coastguard Worker// These patterns cover the entire range of 0 - 2^63-1 nanoseconds
26*6dbdd20aSAndroid Build Coastguard Workerconst patterns: [bigint, string][] = [
27*6dbdd20aSAndroid Build Coastguard Worker  [1n, '|'],
28*6dbdd20aSAndroid Build Coastguard Worker  [2n, '|:'],
29*6dbdd20aSAndroid Build Coastguard Worker  [5n, '|....'],
30*6dbdd20aSAndroid Build Coastguard Worker  [10n, '|....:....'],
31*6dbdd20aSAndroid Build Coastguard Worker  [20n, '|.:.'],
32*6dbdd20aSAndroid Build Coastguard Worker  [50n, '|....'],
33*6dbdd20aSAndroid Build Coastguard Worker  [100n, '|....:....'],
34*6dbdd20aSAndroid Build Coastguard Worker  [200n, '|.:.'],
35*6dbdd20aSAndroid Build Coastguard Worker  [500n, '|....'],
36*6dbdd20aSAndroid Build Coastguard Worker  [1n * micros, '|....:....'],
37*6dbdd20aSAndroid Build Coastguard Worker  [2n * micros, '|.:.'],
38*6dbdd20aSAndroid Build Coastguard Worker  [5n * micros, '|....'],
39*6dbdd20aSAndroid Build Coastguard Worker  [10n * micros, '|....:....'],
40*6dbdd20aSAndroid Build Coastguard Worker  [20n * micros, '|.:.'],
41*6dbdd20aSAndroid Build Coastguard Worker  [50n * micros, '|....'],
42*6dbdd20aSAndroid Build Coastguard Worker  [100n * micros, '|....:....'],
43*6dbdd20aSAndroid Build Coastguard Worker  [200n * micros, '|.:.'],
44*6dbdd20aSAndroid Build Coastguard Worker  [500n * micros, '|....'],
45*6dbdd20aSAndroid Build Coastguard Worker  [1n * millis, '|....:....'],
46*6dbdd20aSAndroid Build Coastguard Worker  [2n * millis, '|.:.'],
47*6dbdd20aSAndroid Build Coastguard Worker  [5n * millis, '|....'],
48*6dbdd20aSAndroid Build Coastguard Worker  [10n * millis, '|....:....'],
49*6dbdd20aSAndroid Build Coastguard Worker  [20n * millis, '|.:.'],
50*6dbdd20aSAndroid Build Coastguard Worker  [50n * millis, '|....'],
51*6dbdd20aSAndroid Build Coastguard Worker  [100n * millis, '|....:....'],
52*6dbdd20aSAndroid Build Coastguard Worker  [200n * millis, '|.:.'],
53*6dbdd20aSAndroid Build Coastguard Worker  [500n * millis, '|....'],
54*6dbdd20aSAndroid Build Coastguard Worker  [1n * seconds, '|....:....'],
55*6dbdd20aSAndroid Build Coastguard Worker  [2n * seconds, '|.:.'],
56*6dbdd20aSAndroid Build Coastguard Worker  [5n * seconds, '|....'],
57*6dbdd20aSAndroid Build Coastguard Worker  [10n * seconds, '|....:....'],
58*6dbdd20aSAndroid Build Coastguard Worker  [30n * seconds, '|.:.:.'],
59*6dbdd20aSAndroid Build Coastguard Worker  [1n * minutes, '|.....'],
60*6dbdd20aSAndroid Build Coastguard Worker  [2n * minutes, '|.:.'],
61*6dbdd20aSAndroid Build Coastguard Worker  [5n * minutes, '|.....'],
62*6dbdd20aSAndroid Build Coastguard Worker  [10n * minutes, '|....:....'],
63*6dbdd20aSAndroid Build Coastguard Worker  [30n * minutes, '|.:.:.'],
64*6dbdd20aSAndroid Build Coastguard Worker  [1n * hours, '|.....'],
65*6dbdd20aSAndroid Build Coastguard Worker  [2n * hours, '|.:.'],
66*6dbdd20aSAndroid Build Coastguard Worker  [6n * hours, '|.....'],
67*6dbdd20aSAndroid Build Coastguard Worker  [12n * hours, '|.....:.....'],
68*6dbdd20aSAndroid Build Coastguard Worker  [1n * days, '|.:.'],
69*6dbdd20aSAndroid Build Coastguard Worker  [2n * days, '|.:.'],
70*6dbdd20aSAndroid Build Coastguard Worker  [5n * days, '|....'],
71*6dbdd20aSAndroid Build Coastguard Worker  [10n * days, '|....:....'],
72*6dbdd20aSAndroid Build Coastguard Worker  [20n * days, '|.:.'],
73*6dbdd20aSAndroid Build Coastguard Worker  [50n * days, '|....'],
74*6dbdd20aSAndroid Build Coastguard Worker  [100n * days, '|....:....'],
75*6dbdd20aSAndroid Build Coastguard Worker  [200n * days, '|.:.'],
76*6dbdd20aSAndroid Build Coastguard Worker  [500n * days, '|....'],
77*6dbdd20aSAndroid Build Coastguard Worker  [1000n * days, '|....:....'],
78*6dbdd20aSAndroid Build Coastguard Worker  [2000n * days, '|.:.'],
79*6dbdd20aSAndroid Build Coastguard Worker  [5000n * days, '|....'],
80*6dbdd20aSAndroid Build Coastguard Worker  [10000n * days, '|....:....'],
81*6dbdd20aSAndroid Build Coastguard Worker  [20000n * days, '|.:.'],
82*6dbdd20aSAndroid Build Coastguard Worker  [50000n * days, '|....'],
83*6dbdd20aSAndroid Build Coastguard Worker  [100000n * days, '|....:....'],
84*6dbdd20aSAndroid Build Coastguard Worker  [200000n * days, '|.:.'],
85*6dbdd20aSAndroid Build Coastguard Worker];
86*6dbdd20aSAndroid Build Coastguard Worker
87*6dbdd20aSAndroid Build Coastguard Worker// Returns the optimal step size and pattern of ticks within the step.
88*6dbdd20aSAndroid Build Coastguard Workerexport function getPattern(minPatternSize: bigint): [duration, string] {
89*6dbdd20aSAndroid Build Coastguard Worker  for (const [size, pattern] of patterns) {
90*6dbdd20aSAndroid Build Coastguard Worker    if (size >= minPatternSize) {
91*6dbdd20aSAndroid Build Coastguard Worker      return [size, pattern];
92*6dbdd20aSAndroid Build Coastguard Worker    }
93*6dbdd20aSAndroid Build Coastguard Worker  }
94*6dbdd20aSAndroid Build Coastguard Worker
95*6dbdd20aSAndroid Build Coastguard Worker  throw new Error('Pattern not defined for this minsize');
96*6dbdd20aSAndroid Build Coastguard Worker}
97*6dbdd20aSAndroid Build Coastguard Worker
98*6dbdd20aSAndroid Build Coastguard Workerfunction tickPatternToArray(pattern: string): TickType[] {
99*6dbdd20aSAndroid Build Coastguard Worker  const array = Array.from(pattern);
100*6dbdd20aSAndroid Build Coastguard Worker  return array.map((char) => {
101*6dbdd20aSAndroid Build Coastguard Worker    switch (char) {
102*6dbdd20aSAndroid Build Coastguard Worker      case '|':
103*6dbdd20aSAndroid Build Coastguard Worker        return TickType.MAJOR;
104*6dbdd20aSAndroid Build Coastguard Worker      case ':':
105*6dbdd20aSAndroid Build Coastguard Worker        return TickType.MEDIUM;
106*6dbdd20aSAndroid Build Coastguard Worker      case '.':
107*6dbdd20aSAndroid Build Coastguard Worker        return TickType.MINOR;
108*6dbdd20aSAndroid Build Coastguard Worker      default:
109*6dbdd20aSAndroid Build Coastguard Worker        // This is almost certainly a developer/fat-finger error
110*6dbdd20aSAndroid Build Coastguard Worker        throw Error(`Invalid char "${char}" in pattern "${pattern}"`);
111*6dbdd20aSAndroid Build Coastguard Worker    }
112*6dbdd20aSAndroid Build Coastguard Worker  });
113*6dbdd20aSAndroid Build Coastguard Worker}
114*6dbdd20aSAndroid Build Coastguard Worker
115*6dbdd20aSAndroid Build Coastguard Workerexport enum TickType {
116*6dbdd20aSAndroid Build Coastguard Worker  MAJOR,
117*6dbdd20aSAndroid Build Coastguard Worker  MEDIUM,
118*6dbdd20aSAndroid Build Coastguard Worker  MINOR,
119*6dbdd20aSAndroid Build Coastguard Worker}
120*6dbdd20aSAndroid Build Coastguard Worker
121*6dbdd20aSAndroid Build Coastguard Workerexport interface Tick {
122*6dbdd20aSAndroid Build Coastguard Worker  type: TickType;
123*6dbdd20aSAndroid Build Coastguard Worker  time: time;
124*6dbdd20aSAndroid Build Coastguard Worker}
125*6dbdd20aSAndroid Build Coastguard Worker
126*6dbdd20aSAndroid Build Coastguard Workerexport const MIN_PX_PER_STEP = 120;
127*6dbdd20aSAndroid Build Coastguard Workerexport function getMaxMajorTicks(width: number) {
128*6dbdd20aSAndroid Build Coastguard Worker  return Math.max(1, Math.floor(width / MIN_PX_PER_STEP));
129*6dbdd20aSAndroid Build Coastguard Worker}
130*6dbdd20aSAndroid Build Coastguard Worker
131*6dbdd20aSAndroid Build Coastguard Workerexport function* generateTicks(
132*6dbdd20aSAndroid Build Coastguard Worker  timeSpan: TimeSpan,
133*6dbdd20aSAndroid Build Coastguard Worker  maxMajorTicks: number,
134*6dbdd20aSAndroid Build Coastguard Worker  offset: time = Time.ZERO,
135*6dbdd20aSAndroid Build Coastguard Worker): Generator<Tick> {
136*6dbdd20aSAndroid Build Coastguard Worker  assertTrue(timeSpan.duration > 0n, 'timeSpan.duration cannot be lte 0');
137*6dbdd20aSAndroid Build Coastguard Worker  assertTrue(maxMajorTicks > 0, 'maxMajorTicks cannot be lte 0');
138*6dbdd20aSAndroid Build Coastguard Worker
139*6dbdd20aSAndroid Build Coastguard Worker  timeSpan = timeSpan.translate(-offset);
140*6dbdd20aSAndroid Build Coastguard Worker  const minStepSize = BigInt(
141*6dbdd20aSAndroid Build Coastguard Worker    Math.floor(Number(timeSpan.duration) / maxMajorTicks),
142*6dbdd20aSAndroid Build Coastguard Worker  );
143*6dbdd20aSAndroid Build Coastguard Worker  const [patternSize, pattern] = getPattern(minStepSize);
144*6dbdd20aSAndroid Build Coastguard Worker  const tickPattern = tickPatternToArray(pattern);
145*6dbdd20aSAndroid Build Coastguard Worker
146*6dbdd20aSAndroid Build Coastguard Worker  const stepSize = patternSize / BigInt(tickPattern.length);
147*6dbdd20aSAndroid Build Coastguard Worker  const start = Time.quantFloor(timeSpan.start, patternSize);
148*6dbdd20aSAndroid Build Coastguard Worker  const end = timeSpan.end;
149*6dbdd20aSAndroid Build Coastguard Worker  let patternIndex = 0;
150*6dbdd20aSAndroid Build Coastguard Worker
151*6dbdd20aSAndroid Build Coastguard Worker  for (
152*6dbdd20aSAndroid Build Coastguard Worker    let time = start;
153*6dbdd20aSAndroid Build Coastguard Worker    time < end;
154*6dbdd20aSAndroid Build Coastguard Worker    time = Time.add(time, stepSize), patternIndex++
155*6dbdd20aSAndroid Build Coastguard Worker  ) {
156*6dbdd20aSAndroid Build Coastguard Worker    if (time >= timeSpan.start) {
157*6dbdd20aSAndroid Build Coastguard Worker      patternIndex = patternIndex % tickPattern.length;
158*6dbdd20aSAndroid Build Coastguard Worker      const type = tickPattern[patternIndex];
159*6dbdd20aSAndroid Build Coastguard Worker      yield {type, time: Time.add(time, offset)};
160*6dbdd20aSAndroid Build Coastguard Worker    }
161*6dbdd20aSAndroid Build Coastguard Worker  }
162*6dbdd20aSAndroid Build Coastguard Worker}
163