| // 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 {Store} from '../base/store'; |
| import {time, TimeSpan} from '../base/time'; |
| import {HighPrecisionTimeSpan} from '../base/high_precision_time_span'; |
| import {Area, State} from '../common/state'; |
| import {raf} from '../core/raf_scheduler'; |
| import {ratelimit} from './rate_limiters'; |
| |
| interface Range { |
| start?: number; |
| end?: number; |
| } |
| |
| const MIN_DURATION = 10; |
| |
| /** |
| * State that is shared between several frontend components, but not the |
| * controller. This state is updated at 60fps. |
| */ |
| export class Timeline { |
| private _visibleWindow: HighPrecisionTimeSpan; |
| private readonly traceSpan: TimeSpan; |
| private readonly store: Store<State>; |
| |
| // This is used to calculate the tracks within a Y range for area selection. |
| areaY: Range = {}; |
| private _selectedArea?: Area; |
| |
| constructor(store: Store<State>, traceSpan: TimeSpan) { |
| this.store = store; |
| this.traceSpan = traceSpan; |
| this._visibleWindow = HighPrecisionTimeSpan.fromTime( |
| traceSpan.start, |
| traceSpan.end, |
| ); |
| } |
| |
| // This is a giant hack. Basically, removing visible window from the state |
| // means that we no longer update the state periodically while navigating |
| // the timeline, which means that controllers are not running. This keeps |
| // making null edits to the store which triggers the controller to run. |
| // |
| // TODO(stevegolton): When we remove controllers, we can remove this! |
| private readonly rateLimitedPoker = ratelimit( |
| () => this.store.edit(() => {}), |
| 50, |
| ); |
| |
| // TODO: there is some redundancy in the fact that both |visibleWindowTime| |
| // and a |timeScale| have a notion of time range. That should live in one |
| // place only. |
| |
| zoomVisibleWindow(ratio: number, centerPoint: number) { |
| this._visibleWindow = this._visibleWindow |
| .scale(ratio, centerPoint, MIN_DURATION) |
| .fitWithin(this.traceSpan.start, this.traceSpan.end); |
| |
| this.rateLimitedPoker(); |
| } |
| |
| panVisibleWindow(delta: number) { |
| this._visibleWindow = this._visibleWindow |
| .translate(delta) |
| .fitWithin(this.traceSpan.start, this.traceSpan.end); |
| this.rateLimitedPoker(); |
| } |
| |
| // Set the highlight box to draw |
| selectArea( |
| start: time, |
| end: time, |
| tracks = this._selectedArea ? this._selectedArea.trackUris : [], |
| ) { |
| assertTrue( |
| end >= start, |
| `Impossible select area: start [${start}] >= end [${end}]`, |
| ); |
| this._selectedArea = {start, end, trackUris: tracks}; |
| raf.scheduleFullRedraw(); |
| } |
| |
| deselectArea() { |
| this._selectedArea = undefined; |
| raf.scheduleRedraw(); |
| } |
| |
| get selectedArea(): Area | undefined { |
| return this._selectedArea; |
| } |
| |
| // Set visible window using an integer time span |
| updateVisibleTime(ts: TimeSpan) { |
| this.updateVisibleTimeHP(HighPrecisionTimeSpan.fromTime(ts.start, ts.end)); |
| } |
| |
| // Set visible window using a high precision time span |
| updateVisibleTimeHP(ts: HighPrecisionTimeSpan) { |
| this._visibleWindow = ts |
| .clampDuration(MIN_DURATION) |
| .fitWithin(this.traceSpan.start, this.traceSpan.end); |
| |
| this.rateLimitedPoker(); |
| } |
| |
| // Get the bounds of the visible window as a high-precision time span |
| get visibleWindow(): HighPrecisionTimeSpan { |
| return this._visibleWindow; |
| } |
| } |