API Docs for: 2.0.0
Show:

File: src/viewer/animation/cameraFlyAnimation.js

/**

 **Fly** flys a {{#crossLink "Camera"}}{{/crossLink}}

 ## Overview

 ## Example

 TODO

 ````javascript
 TODO
 ````
 @class CameraFlyAnimation
 @module BIMSURFER
 @submodule animation
 @constructor
 @param [viewer] {Viewer} Parent {{#crossLink "Viewer"}}Viewer{{/crossLink}}.
 @param [cfg] {*} Fly configuration
 @param [cfg.id] {String} Optional ID, unique among all components in the parent {{#crossLink "Viewer"}}Viewer{{/crossLink}}, generated automatically when omitted.
 @param [cfg.meta] {String:Object} Optional map of user-defined metadata to attach to this CameraFlyAnimation.
 @param [cfg.camera] {Camera} Camera to control
 @extends Component
 */
(function () {

    "use strict";

    BIMSURFER.CameraFlyAnimation = BIMSURFER.Component.extend({

        /**
         JavaScript class name for this Component.

         @property className
         @type String
         @final
         */
        className: "BIMSURFER.CameraFlyAnimation",

        _init: function (cfg) {

            this._look1 = [0, 0, 0];
            this._eye1 = [0, 0, 0];
            this._up1 = [0, 0, 0];

            this._look2 = [0, 0, 0];
            this._eye2 = [0, 0, 0];
            this._up2 = [0, 0, 0];

            this._vec = [0, 0, 0];

            this._dist = 0;

            this._flying = false;

            this._ok = null;

            this._onTick = null;

            this._camera = cfg.camera;

            this._tempVec = BIMSURFER.math.vec3();

            this._eyeVec = BIMSURFER.math.vec3();
            this._lookVec = BIMSURFER.math.vec3();

            this._stopFOV = 55;

            this._time1 = null;
            this._time2 = null;

            this.easing = cfg.easing !== false;

            this.duration = cfg.duration || 0.5;

            this.camera = cfg.camera;
        },

        /**
         * Begins flying this CameraFlyAnimation's {{#crossLink "Camera"}}{{/crossLink}} to the given target.
         *
         * <ul>
         *     <li>When the target is a boundary, the {{#crossLink "Camera"}}{{/crossLink}} will fly towards the target
         *     and stop when the target fills most of the canvas.</li>
         *     <li>When the target is an explicit {{#crossLink "Camera"}}{{/crossLink}} position, given as ````eye````, ````look```` and ````up````
         *      vectors, then this CameraFlyAnimation will interpolate the {{#crossLink "Camera"}}{{/crossLink}} to that target and stop there.</li>
         * @method flyTo
         * @param params  {*} Flight parameters
         * @param[params.arc=0]  {Number} Factor in range [0..1] indicating how much the
         * {{#crossLink "Camera/eye:property"}}Camera's eye{{/crossLink}} position will
         * swing away from its {{#crossLink "Camera/eye:property"}}look{{/crossLink}} position as it flies to the target.
         * @param [params.boundary] {{xmin:Number, ymin:Number, zmin: Number, xmax: Number, ymax: Number, zmax: Number }}  Boundary target to fly to.
         * @param [params.eye] {Array of Number} Position to fly the {{#crossLink "Camera/eye:property"}}Camera's eye{{/crossLink}} position to.
         * @param [params.look] {Array of Number} Position to fly the {{#crossLink "Camera/look:property"}}Camera's look{{/crossLink}} position to.
         * @param [params.up] {Array of Number} Position to fly the {{#crossLink "Camera/up:property"}}Camera's up{{/crossLink}} vector to.
         * @param [ok] {Function} Callback fired on arrival
         */
        flyTo: function (params, ok) {

            if (this._flying) {
                this.stop();
            }

            this._ok = ok;

            this._arc = params.arc === undefined ? 0.0 : params.arc;

            var camera = this.camera;

            // Set up initial camera state

            this._look1 = camera.look;
            this._look1 = [this._look1[0], this._look1[1], this._look1[2]];

            this._eye1 = camera.eye;
            this._eye1 = [this._eye1[0], this._eye1[1], this._eye1[2]];

            this._up1 = camera.up;
            this._up1 = [this._up1[0], this._up1[1], this._up1[2]];

            // Get normalized eye->look vector

            this._vec = BIMSURFER.math.normalizeVec3(BIMSURFER.math.subVec3(this._eye1, this._look1, []));

            // Back-off factor in range of [0..1], when 0 is close, 1 is far

            var backOff = params.backOff || 0.5;

            if (backOff < 0) {
                backOff = 0;

            } else if (backOff > 1) {
                backOff = 1;
            }

            backOff = 1 - backOff;

            // Set up final camera state

            if (params.boundary) {

                // Zooming to look and eye computed from boundary

                var boundary = params.boundary;

                if (boundary.xmax <= boundary.xmin || boundary.ymax <= boundary.ymin || boundary.zmax <= boundary.zmin) {
                    return;
                }

                var dist = params.dist || 2.5;
                var lenVec = Math.abs(BIMSURFER.math.lenVec3(this._vec));
                var diag = BIMSURFER.math.getBoundaryDiag(boundary);
                var len = Math.abs((diag / (1.0 + (backOff * 0.8))) / Math.tan(this._stopFOV / 2));  /// Tweak this to set final camera distance on arrival
                var sca = (len / lenVec) * dist;

                this._look2 = BIMSURFER.math.getBoundaryCenter(boundary);
                this._look2 = [this._look2[0], this._look2[1], this._look2[2]];

                if (params.offset) {

                    this._look2[0] += params.offset[0];
                    this._look2[1] += params.offset[1];
                    this._look2[2] += params.offset[2];
                }

                this._eye2 = BIMSURFER.math.addVec3(this._look2, BIMSURFER.math.mulVec3Scalar(this._vec, sca, []));
                this._up2 = BIMSURFER.math.vec3();
                this._up2[1] = 1;

            } else {

                // Zooming to specific look and eye points

                var lookat = params;

                var look = lookat.look || camera.look;
                var eye = lookat.eye || camera.eye;
                var up = lookat.up || camera.up;

                var cameraEye = camera.eye;
                var cameraLook = camera.look;
                var cameraUp = camera.up;

                if (look) {

                    this._look2[0] = look[0];
                    this._look2[1] = look[1];
                    this._look2[2] = look[2];

                } else {

                    this._look2[0] = cameraLook[0];
                    this._look2[1] = cameraLook[1];
                    this._look2[2] = cameraLook[2];
                }

                if (eye) {

                    this._eye2[0] = eye[0];
                    this._eye2[1] = eye[1];
                    this._eye2[2] = eye[2];

                } else {

                    this._eye2[0] = cameraEye[0];
                    this._eye2[1] = cameraEye[1];
                    this._eye2[2] = cameraEye[2];
                }

                if (up) {

                    this._up2[0] = up[0];
                    this._up2[1] = up[1];
                    this._up2[2] = up[2];

                } else {

                    this._up2[0] = cameraUp[0];
                    this._up2[1] = cameraUp[1];
                    this._up2[2] = cameraUp[2];
                }
            }

            this.fire("started", params, true);

            var self = this;

            this._time1 = (new Date()).getTime();
            this._time2 = this._time1 + this._duration;

            this._tick = this.viewer.on("tick",
                function (params) {
                    self._update(params.time * 1000.0);
                });

            this._flying = true;
        },

        _update: function (time) {

            if (!this._flying) {
                return;
            }

            time = (new Date()).getTime();

            var t = (time - this._time1) / (this._time2 - this._time1);

            if (t > 1) {
                this.stop();
                return;
            }

            t = this.easing ? this._ease(t, 0, 1, 1) : t;

            this._camera.eye = BIMSURFER.math.lerpVec3(t, 0, 1, this._eye1, this._eye2, []);
            this._camera.look = BIMSURFER.math.lerpVec3(t, 0, 1, this._look1, this._look2, []);
            this._camera.up = BIMSURFER.math.lerpVec3(t, 0, 1, this._up1, this._up2, []);
        },

        // Quadratic easing out - decelerating to zero velocity
        // http://gizma.com/easing

        _ease: function (t, b, c, d) {
            t /= d;
            return -c * t * (t - 2) + b;
        },

        stop: function () {

            if (!this._flying) {
                return;
            }

            this.viewer.off(this._tick);

            this._flying = false;

            this._time1 = null;
            this._time2 = null;

            this.fire("stopped", true, true);

            var ok = this._ok;

            if (ok) {
                this._ok = false;
                ok();
            }
        },

        _props: {

            camera: {

                set: function (value) {
                    this._camera = value;
                    this.stop();
                },

                get: function () {
                    return this._camera;
                }
            },

            duration: {

                set: function (value) {
                    this._duration = value * 1000.0;
                    this.stop();
                },

                get: function () {
                    return this._duration * 0.001;
                }
            }
        },

        _destroy: function () {
            this.stop();
        }
    });

})();