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 {TimeSpan} from '../common/time'; 16 17const MAX_ZOOM_SPAN_SEC = 1e-4; // 0.1 ms. 18 19/** 20 * Defines a mapping between number and seconds for the entire application. 21 * Linearly scales time values from boundsMs to pixel values in boundsPx and 22 * back. 23 */ 24export class TimeScale { 25 private timeBounds: TimeSpan; 26 private _startPx: number; 27 private _endPx: number; 28 private secPerPx = 0; 29 30 constructor(timeBounds: TimeSpan, boundsPx: [number, number]) { 31 this.timeBounds = timeBounds; 32 this._startPx = boundsPx[0]; 33 this._endPx = boundsPx[1]; 34 this.updateSlope(); 35 } 36 37 private updateSlope() { 38 this.secPerPx = this.timeBounds.duration / (this._endPx - this._startPx); 39 } 40 41 deltaTimeToPx(time: number): number { 42 return Math.round(time / this.secPerPx); 43 } 44 45 timeToPx(time: number): number { 46 return this._startPx + (time - this.timeBounds.start) / this.secPerPx; 47 } 48 49 pxToTime(px: number): number { 50 return this.timeBounds.start + (px - this._startPx) * this.secPerPx; 51 } 52 53 deltaPxToDuration(px: number): number { 54 return px * this.secPerPx; 55 } 56 57 setTimeBounds(timeBounds: TimeSpan) { 58 this.timeBounds = timeBounds; 59 this.updateSlope(); 60 } 61 62 setLimitsPx(pxStart: number, pxEnd: number) { 63 this._startPx = pxStart; 64 this._endPx = pxEnd; 65 this.updateSlope(); 66 } 67 68 timeInBounds(time: number): boolean { 69 return this.timeBounds.isInBounds(time); 70 } 71 72 get startPx(): number { 73 return this._startPx; 74 } 75 76 get endPx(): number { 77 return this._endPx; 78 } 79} 80 81export function computeZoom( 82 scale: TimeScale, span: TimeSpan, zoomFactor: number, zoomPx: number): 83 TimeSpan { 84 const startPx = scale.startPx; 85 const endPx = scale.endPx; 86 const deltaPx = endPx - startPx; 87 const deltaTime = span.end - span.start; 88 const newDeltaTime = Math.max(deltaTime * zoomFactor, MAX_ZOOM_SPAN_SEC); 89 const clampedZoomPx = Math.max(startPx, Math.min(endPx, zoomPx)); 90 const zoomTime = scale.pxToTime(clampedZoomPx); 91 const r = (clampedZoomPx - startPx) / deltaPx; 92 const newStartTime = zoomTime - newDeltaTime * r; 93 const newEndTime = newStartTime + newDeltaTime; 94 return new TimeSpan(newStartTime, newEndTime); 95} 96