xref: /aosp_15_r20/development/tools/motion/motion_test_watcher_app/src/app/visual-timeline.ts (revision 90c8c64db3049935a07c6143d7fd006e26f8ecca)
1/*
2 * Copyright 2024 Google LLC
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17import { Timeline } from './timeline';
18
19/** Translates pixel values to frames / video timestamps, and vice-versa. */
20export class VisualTimeline extends EventTarget {
21  constructor(
22    width: number,
23    public readonly timeline: Timeline,
24  ) {
25    super();
26    this._width = width;
27  }
28
29  private _width: number;
30
31  get width(): number {
32    return this._width;
33  }
34
35  /**
36   * Pixel-width of the interaction area.
37   *
38   * `0`px ≙ `0`s, while `width`px ≙ the duration.
39   */
40  set width(value: number) {
41    if (value == this._width) return;
42    this._width = value;
43    this.dispatchEvent(new Event('timeline-changed'));
44  }
45
46  /** Pixel-value that marks the given time. */
47  timeToPx(timeSeconds: number): number {
48    if (timeSeconds < 0) return Number.NEGATIVE_INFINITY;
49    const duration = this.timeline.duration;
50    if (timeSeconds > duration) return Number.POSITIVE_INFINITY;
51
52    return this.width * (timeSeconds / duration);
53  }
54
55  /** Pixel-value that marks the given time. */
56  timeToPxClamped(timeSeconds: number): number {
57    if (timeSeconds < 0) return Number.NEGATIVE_INFINITY;
58    const duration = this.timeline.duration;
59    if (timeSeconds > duration) return Number.POSITIVE_INFINITY;
60
61    return (
62      this.width * (this.timeline.clampTimeToFrame(timeSeconds) / duration)
63    );
64  }
65
66  /** Pixel-value that marks the beginning of the given frame. */
67  frameToPx(frame: number): number {
68    return this.timeToPx(this.timeline.frameToTime(frame));
69  }
70
71  /** Video-time associated with the given pixel value. */
72  pxToTime(px: number): number {
73    if (px < 0) return Number.NEGATIVE_INFINITY;
74    const width = this.width;
75    if (px > width) return Number.POSITIVE_INFINITY;
76
77    return this.timeline.duration * (px / width);
78  }
79
80  /** Frame number associated with the given pixel value. */
81  pxToFrame(px: number): number {
82    return this.timeline.timeToFrame(this.pxToTime(px));
83  }
84}
85