1// Copyright (C) 2018 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15import {assertTrue} from '../base/logging'; 16import { 17 HighPrecisionTime, 18 HighPrecisionTimeSpan, 19} from '../common/high_precision_time'; 20import {Span} from '../common/time'; 21import { 22 TPDuration, 23 TPTime, 24} from '../common/time'; 25 26export class TimeScale { 27 private _start: HighPrecisionTime; 28 private _durationNanos: number; 29 readonly pxSpan: PxSpan; 30 private _nanosPerPx = 0; 31 private _startSec: number; 32 33 constructor(start: HighPrecisionTime, durationNanos: number, pxSpan: PxSpan) { 34 // TODO(stevegolton): Ensure duration & pxSpan > 0. 35 // assertTrue(pxSpan.start < pxSpan.end, 'Px start >= end'); 36 // assertTrue(durationNanos < 0, 'Duration <= 0'); 37 this.pxSpan = pxSpan; 38 this._start = start; 39 this._durationNanos = durationNanos; 40 if (durationNanos <= 0 || pxSpan.delta <= 0) { 41 this._nanosPerPx = 1; 42 } else { 43 this._nanosPerPx = durationNanos / (pxSpan.delta); 44 } 45 this._startSec = this._start.seconds; 46 } 47 48 get timeSpan(): Span<HighPrecisionTime> { 49 const end = this._start.addNanos(this._durationNanos); 50 return new HighPrecisionTimeSpan(this._start, end); 51 } 52 53 tpTimeToPx(ts: TPTime): number { 54 // WARNING: Number(bigint) can be surprisingly slow. Avoid in hotpath. 55 const timeOffsetNanos = Number(ts - this._start.base) - this._start.offset; 56 return this.pxSpan.start + timeOffsetNanos / this._nanosPerPx; 57 } 58 59 secondsToPx(seconds: number): number { 60 const timeOffset = (seconds - this._startSec) * 1e9; 61 return this.pxSpan.start + timeOffset / this._nanosPerPx; 62 } 63 64 hpTimeToPx(time: HighPrecisionTime): number { 65 const timeOffsetNanos = time.subtract(this._start).nanos; 66 return this.pxSpan.start + timeOffsetNanos / this._nanosPerPx; 67 } 68 69 // Convert pixels to a high precision time object, which can be futher 70 // converted to other time formats. 71 pxToHpTime(px: number): HighPrecisionTime { 72 const offsetNanos = (px - this.pxSpan.start) * this._nanosPerPx; 73 return this._start.addNanos(offsetNanos); 74 } 75 76 durationToPx(dur: TPDuration): number { 77 // WARNING: Number(bigint) can be surprisingly slow. Avoid in hotpath. 78 return Number(dur) / this._nanosPerPx; 79 } 80 81 pxDeltaToDuration(pxDelta: number): HighPrecisionTime { 82 const time = pxDelta * this._nanosPerPx; 83 return HighPrecisionTime.fromNanos(time); 84 } 85} 86 87export class PxSpan { 88 constructor(private _start: number, private _end: number) { 89 assertTrue(_start <= _end, 'PxSpan start > end'); 90 } 91 92 get start(): number { 93 return this._start; 94 } 95 96 get end(): number { 97 return this._end; 98 } 99 100 get delta(): number { 101 return this._end - this._start; 102 } 103} 104