UNPKG

@itwin/core-frontend

Version:
140 lines 8.75 kB
"use strict"; /*--------------------------------------------------------------------------------------------- * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ /** @packageDocumentation * @module Views */ Object.defineProperty(exports, "__esModule", { value: true }); exports.FrustumAnimator = void 0; const core_bentley_1 = require("@itwin/core-bentley"); const core_geometry_1 = require("@itwin/core-geometry"); const core_common_1 = require("@itwin/core-common"); const Viewport_1 = require("./Viewport"); /** * Compute an intermediate eye point as it swings around a moving target with rotating axes and varying distance to target. * (eye, target, distance) is redundant -- implementation problem is to figure out which to use for compatibility with subsequent view setup. */ function interpolateSwingingEye(axes0, eye0, distance0, axes1, eye1, distance1, fraction, axesAtFraction) { const z0 = axes0.rowZ(); const z1 = axes1.rowZ(); const zA = axesAtFraction.rowZ(); // back up from the eye points to the targets. const target0 = eye0.plusScaled(z0, -distance0); const target1 = eye1.plusScaled(z1, -distance1); // RULE: Target point moves by simple interpolation const targetA = target0.interpolate(fraction, target1); // RULE: Distance from target to eye is simple interpolation const distanceA = core_geometry_1.Geometry.interpolate(distance0, fraction, distance1); // The interpolated target, interpolated distance, and specified axes give the intermediate eyepoint. const eyeA = targetA.plusScaled(zA, distanceA); return { target: targetA, eye: eyeA, distance: distanceA, }; } /** Animates the transition of a [[Viewport]] from one [Frustum]($common) to another. The viewport will render as many frames as necessary during the supplied duration. * @see [[Viewport.animateFrustumChange]] to conveniently animate a viewport from one frustum to another. * @public * @extensions */ class FrustumAnimator { options; _tweens = new core_common_1.Tweens(); _duration = 0; /** Construct an animator that animates from `begin` to `end`. */ constructor(options, viewport, begin, end) { this.options = options; const settings = Viewport_1.ScreenViewport.animation; const zoomSettings = settings.zoomOut; let duration = undefined !== options.animationTime ? options.animationTime : settings.time.normal.milliseconds; if (duration <= 0 || begin.cameraOn !== end.cameraOn) // no duration means skip animation. We can't animate if the camera toggles. return; this._duration = duration; let extentBias; let eyeBias; const zVec = begin.zVec; const view = viewport.view; const view3 = view; const begin3 = begin; const end3 = end; const beginTarget = begin.target; const endTarget = end.target; const axis = (0, core_bentley_1.expectDefined)(end.rotation.multiplyMatrixMatrixInverse(begin.rotation)).getAxisAndAngleOfRotation(); // axis to rotate begin to get to end const timing = { fraction: 0.0, height: 0, position: 0 }; // updated by tween. // don't do "zoom out" if the two views aren't pointing in the same direction, or if they request cancelOnAbort (since that implies that the view // is a linear interpolation from begin to end), or if it's disabled. if (zoomSettings.enable && !options.cancelOnAbort && zVec.isAlmostEqual(end.zVec)) { view.applyPose(end); // start with the pose at the end const viewTransform = core_geometry_1.Transform.createOriginAndMatrix(undefined, view.getRotation()); const endRange = core_geometry_1.Range3d.createTransformedArray(viewTransform, view.calculateFocusCorners()); // get the view-aligned range of the focus plane at the end const beginRange = core_geometry_1.Range3d.createTransformedArray(viewTransform, view.applyPose(begin).calculateFocusCorners()); // get the view-aligned range of the focus plane at the beginning // do the starting and ending views (plus the margin) overlap? If not we need to zoom out to show how to get from one to the other const expand = (range) => { const r = range.clone(); r.scaleAboutCenterInPlace(zoomSettings.margin); return r; }; if (!expand(beginRange).intersectsRangeXY(expand(endRange))) { view3.lookAtViewAlignedVolume(beginRange.union(endRange), viewport.viewRect.aspect); // set up a view that would show both extents duration *= zoomSettings.durationFactor; // increase duration so the zooming isn't too fast extentBias = view.getExtents().minus(begin.extents); // if the camera is off, the "bias" is the amount the union-ed view is larger than the starting view if (begin.cameraOn) eyeBias = zVec.scaleToLength(zVec.dotProduct(begin3.camera.eye.vectorTo(view3.camera.eye))); // if the camera is on, the bias is the difference in height of the two eye positions } } this._tweens.create(timing, { to: { fraction: 1.0, height: zoomSettings.heights, position: zoomSettings.positions }, duration, start: true, easing: options.easingFunction ? options.easingFunction : settings.easing, interpolation: zoomSettings.interpolation, onComplete: () => viewport.setupFromView(end), // when we're done, set up from final state onUpdate: () => { const fraction = extentBias ? timing.position : timing.fraction; // if we're zooming, fraction comes from position interpolation const rot = (0, core_bentley_1.expectDefined)(core_geometry_1.Matrix3d.createRotationAroundVector(axis.axis, core_geometry_1.Angle.createDegrees(fraction * axis.angle.degrees))).multiplyMatrixMatrix(begin.rotation); if (begin.cameraOn) { const newExtents = begin.extents.interpolate(fraction, end.extents); if (undefined !== eyeBias) { const eyePoint = begin3.camera.eye.interpolate(fraction, end3.camera.eye); eyePoint.plusScaled(eyeBias, timing.height, eyePoint); const targetPoint = eyePoint.plusScaled(rot.getRow(2), -1.0 * (core_geometry_1.Geometry.interpolate(begin3.camera.focusDist, fraction, end3.camera.focusDist))); view3.lookAt({ eyePoint, targetPoint, upVector: rot.getRow(1), newExtents }); } else { const data = interpolateSwingingEye(begin3.rotation, begin3.camera.eye, begin3.camera.focusDist, end3.rotation, end3.camera.eye, end3.camera.focusDist, fraction, rot); view3.lookAt({ eyePoint: data.eye, targetPoint: data.target, upVector: rot.getRow(1), newExtents }); } } else { const extents = begin.extents.interpolate(timing.fraction, end.extents); if (undefined !== extentBias) extents.plusScaled(extentBias, timing.height, extents); // no camera, zooming out expands extents view.setExtents(extents); view.setRotation(rot); view.setCenter(beginTarget.interpolate(fraction, endTarget)); // must be done last - depends on extents and rotation } viewport.setupFromView(); }, }); } /** @internal */ animate() { const didFinish = !this._tweens.update(); if (didFinish && this.options.animationFinishedCallback) this.options.animationFinishedCallback(true); return didFinish; } /** @internal */ interrupt() { // We were interrupted. Either go to: the final frame (normally) or, add a small fraction of the total duration (30ms for a .5 second duration) to // the current time for cancelOnAbort. That makes aborted animations show some progress, as happens when the mouse wheel rolls quickly. this._tweens.update(this.options.cancelOnAbort ? Date.now() + (this._duration * .06) : Infinity); if (this.options.animationFinishedCallback) this.options.animationFinishedCallback(false); } } exports.FrustumAnimator = FrustumAnimator; //# sourceMappingURL=FrustumAnimator.js.map