/** * Copyright (c) 2018-2019 Mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author David Sehnal */ import { Camera } from '../camera'; import { Quat, Vec3 } from '../../mol-math/linear-algebra'; import { lerp } from '../../mol-math/interpolate'; export { CameraTransitionManager }; class CameraTransitionManager { private t = 0; private func: CameraTransitionManager.TransitionFunc = CameraTransitionManager.defaultTransition; private start = 0; inTransition = false; private durationMs = 0; private _source: Camera.Snapshot = Camera.createDefaultSnapshot(); private _target: Camera.Snapshot = Camera.createDefaultSnapshot(); private _current = Camera.createDefaultSnapshot(); get source(): Readonly { return this._source; } get target(): Readonly { return this._target; } apply(to: Partial, durationMs: number = 0, transition?: CameraTransitionManager.TransitionFunc) { if (!this.inTransition || durationMs > 0) { Camera.copySnapshot(this._source, this.camera.state); } if (!this.inTransition) { Camera.copySnapshot(this._target, this.camera.state); } Camera.copySnapshot(this._target, to); if (this._target.radius > this._target.radiusMax) { this._target.radius = this._target.radiusMax; } if (!this.inTransition && durationMs <= 0 || (typeof to.mode !== 'undefined' && to.mode !== this.camera.state.mode)) { this.finish(this._target); return; } this.inTransition = true; this.func = transition || CameraTransitionManager.defaultTransition; if (!this.inTransition || durationMs > 0) { this.start = this.t; this.durationMs = durationMs; } } tick(t: number) { this.t = t; this.update(); } private finish(to: Partial) { Camera.copySnapshot(this.camera.state, to); this.inTransition = false; } private update() { if (!this.inTransition) return; const normalized = Math.min((this.t - this.start) / this.durationMs, 1); if (normalized === 1) { this.finish(this._target!); return; } this.func(this._current, normalized, this._source, this._target); Camera.copySnapshot(this.camera.state, this._current); } constructor(private camera: Camera) { } } namespace CameraTransitionManager { export type TransitionFunc = (out: Camera.Snapshot, t: number, source: Camera.Snapshot, target: Camera.Snapshot) => void const _rot = Quat.identity(); export function defaultTransition(out: Camera.Snapshot, t: number, source: Camera.Snapshot, target: Camera.Snapshot): void { Camera.copySnapshot(out, target); // Rotate up Quat.slerp(_rot, Quat.Identity, Quat.rotationTo(_rot, source.up, target.up), t); Vec3.transformQuat(out.up, source.up, _rot); // Lerp target, position & radius Vec3.lerp(out.target, source.target, target.target, t); Vec3.lerp(out.position, source.position, target.position, t); out.radius = lerp(source.radius, target.radius, t); // TODO take change of `clipFar` into account out.radiusMax = lerp(source.radiusMax, target.radiusMax, t); // Lerp fov & fog out.fov = lerp(source.fov, target.fov, t); out.fog = lerp(source.fog, target.fog, t); } }