<commit_before>/*global define*/
define([
        '../Core/defaultValue',
        '../Core/DeveloperError',
        '../Core/Math',
        '../Core/Cartesian2',
        '../Core/Cartesian3',
        '../Core/Cartesian4',
        '../Core/Cartographic',
        '../Core/Quaternion',
        '../Core/Matrix3',
        '../Core/Matrix4',
        '../Core/Ellipsoid',
        '../Core/Transforms',
        '../Scene/CameraColumbusViewMode',
        '../Scene/SceneMode'
       ], function(
         defaultValue,
         DeveloperError,
         CesiumMath,
         Cartesian2,
         Cartesian3,
         Cartesian4,
         Cartographic,
         Quaternion,
         Matrix3,
         Matrix4,
         Ellipsoid,
         Transforms,
         CameraColumbusViewMode,
         SceneMode) {
    "use strict";

    function update2D(that, camera, objectChanged, offset, positionProperty, time, projection) {
        var viewDistance;
        var scene = that.scene;
        var modeChanged = scene.mode !== that._mode;

        if (modeChanged) {
            that._mode = scene.mode;
            that._screenSpaceCameraController.enableTranslate = false;
            viewDistance = offset.magnitude();
        } else if (objectChanged) {
            viewDistance = offset.magnitude();
        } else {
            viewDistance = camera.position.z;
        }

        var cartographic = positionProperty.getValueCartographic(time, that._lastCartographic);
        if (typeof cartographic !== 'undefined') {
            //We are assigning the position of the camera, not of the object, so modify the height appropriately.
            cartographic.height = viewDistance;
            if (objectChanged || modeChanged) {
                camera.controller.setPositionCartographic(cartographic);

                //Set rotation to match offset.
                Cartesian3.normalize(offset, camera.up);
                Cartesian3.negate(camera.up, camera.up);
                Cartesian3.cross(camera.direction, camera.up, camera.right);

                //z is always zero in 2D for up and right
                camera.up.z = 0;
                Cartesian3.normalize(camera.up, camera.up);
                camera.right.z = 0;
                Cartesian3.normalize(camera.right, camera.right);

                //Remember what up was when we started, so we
                //can detect rotation when we are finished.
                Cartesian2.clone(camera.right, that._first2dUp);
            } else {
                camera.position = projection.project(cartographic);
            }

            //Store last view distance and up vector.
            that._lastDistance = camera.frustum.right - camera.frustum.left;
            Cartesian2.clone(camera.right, that._last2dUp);
        }
    }

    var update3DTransform;
    function update3D(that, camera, objectChanged, offset, positionProperty, time, ellipsoid) {
        update3DController(that, camera, objectChanged, offset);

        var cartesian = positionProperty.getValueCartesian(time, that._lastCartesian);
        if (typeof cartesian !== 'undefined') {
            //if (that.referenceFrameType === ReferenceFrameType.EAST_NORTH_UP) {
            //    camera.transform = Transforms.eastNorthUpToFixedFrame(cartesian, ellipsoid, update3DTransform);
            //} else if (that.referenceFrameType === ReferenceFrameType.LOCAL_VERTICAL_LOCAL_HORIZONTAL) {
                var deltaTime = time.addSeconds(0.001);
                var deltaCartesian = positionProperty.getValueCartesian(deltaTime);
                if (typeof deltaCartesian !== 'undefined') {
                    var toInertial = Transforms.computeFixedToIcrfMatrix(time);
                    var toFixed = Transforms.computeIcrfToFixedMatrix(time);

                    // Z along the position
                    var zBasis = new Cartesian3();
                    Cartesian3.normalize(cartesian, zBasis);
                    Cartesian3.normalize(deltaCartesian, deltaCartesian);

                    Matrix3.multiplyByVector(toInertial, zBasis, zBasis);
                    Matrix3.multiplyByVector(toInertial, deltaCartesian, deltaCartesian);

                    // Y is along the angular momentum vector (e.g. "orbit normal")
                    var yBasis = Cartesian3.cross(zBasis, deltaCartesian);

                    //Cartesian3.normalize(zBasis, zBasis);
                    Cartesian3.normalize(yBasis, yBasis);

                    // X is along the cross of y and z (right handed basis / in the direction of motion)
                    var xBasis = Cartesian3.cross(yBasis, zBasis);

                    Cartesian3.normalize(xBasis, xBasis);

                    Matrix3.multiplyByVector(toFixed, xBasis, xBasis);
                    Matrix3.multiplyByVector(toFixed, yBasis, yBasis);
                    Matrix3.multiplyByVector(toFixed, zBasis, zBasis);

                    Cartesian3.normalize(xBasis, xBasis);
                    Cartesian3.normalize(yBasis, yBasis);
                    Cartesian3.normalize(zBasis, zBasis);

                    camera.transform = new Matrix4(
                            xBasis.x, yBasis.x, zBasis.x, cartesian.x,
                            xBasis.y, yBasis.y, zBasis.y, cartesian.y,
                            xBasis.z, yBasis.z, zBasis.z, cartesian.z,
                            0.0,       0.0,         0.0,      1.0);
                }
            //}

            that._screenSpaceCameraController.setEllipsoid(Ellipsoid.UNIT_SPHERE);

            var position = camera.position;
            Cartesian3.clone(position, that._lastOffset);
            that._lastDistance = Cartesian3.magnitude(position);
        }
    }

    var updateColumbusCartesian4 = new Cartesian4(0.0, 0.0, 0.0, 1.0);
    function updateColumbus(that, camera, objectChanged, offset, positionProperty, time, ellipsoid, projection) {
        update3DController(that, camera, objectChanged, offset);

        //The swizzling here is intentional because ColumbusView uses a different coordinate system.
        var cartographic = positionProperty.getValueCartographic(time, that._lastCartographic);
        if (typeof cartographic !== 'undefined') {
            var projectedPosition = projection.project(cartographic);
            updateColumbusCartesian4.x = projectedPosition.z;
            updateColumbusCartesian4.y = projectedPosition.x;
            updateColumbusCartesian4.z = projectedPosition.y;

            var tranform = camera.transform;
            tranform.setColumn(3, updateColumbusCartesian4, tranform);

            var controller = that._screenSpaceCameraController;
            controller.enableTranslate = false;
            controller.setEllipsoid(Ellipsoid.UNIT_SPHERE);
            controller.columbusViewMode = CameraColumbusViewMode.LOCKED;

            camera.controller.constrainedAxis = Cartesian3.UNIT_Z;

            var position = camera.position;
            Cartesian3.clone(position, that._lastOffset);
            that._lastDistance = Cartesian3.magnitude(position);
        }
    }

    var update3DControllerQuaternion = new Quaternion();
    var update3DControllerMatrix3 = new Matrix3();

    function update3DController(that, camera, objectChanged, offset) {
        var scene = that.scene;
        that._screenSpaceCameraController.enableTilt = false;
        camera.controller.constrainedAxis = Cartesian3.UNIT_Z;

        if (objectChanged) {
            camera.controller.lookAt(offset, Cartesian3.ZERO, Cartesian3.UNIT_Z);
        } else if (scene.mode !== that._mode) {
            that._mode = scene.mode;

            //If we're switching from 2D and any rotation was applied to the camera,
            //apply that same rotation to the last offset used in 3D or Columbus view.
            var first2dUp = that._first2dUp;
            var last2dUp = that._last2dUp;
            if (!Cartesian2.equals(first2dUp, last2dUp)) {
                var startTheta = Math.acos(first2dUp.x);
                if (first2dUp.y < 0) {
                    startTheta = CesiumMath.TWO_PI - startTheta;
                }
                var endTheta = Math.acos(last2dUp.x);
                if (last2dUp.y < 0) {
                    endTheta = CesiumMath.TWO_PI - endTheta;
                }
                last2dUp.x = 0.0;
                last2dUp.y = 0.0;
                first2dUp.x = 0.0;
                first2dUp.y = 0.0;

                var theta = startTheta - endTheta;
                var rotation = Quaternion.fromAxisAngle(Cartesian3.UNIT_Z, theta, update3DControllerQuaternion);
                Matrix3.fromQuaternion(rotation, update3DControllerMatrix3).multiplyByVector(offset, offset);
            }
            offset.normalize(offset).multiplyByScalar(that._lastDistance, offset);
            camera.controller.lookAt(offset, Cartesian3.ZERO, Cartesian3.UNIT_Z);
        }
    }

    var dynamicObjectViewDefaultOffset = new Cartesian3(10000, -10000, 10000);
    var dynamicObjectViewCartesian3Scratch = new Cartesian3();

    /**
     * A utility object for tracking an object with the camera.
     * @alias DynamicObject
     * @constructor
     *
     * @param {DynamicObject} dynamicObject The object to track with the camera.
     * @param {Scene} scene The scene to use.
     * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid to use for orienting the camera.
     */
    var DynamicObjectView = function(dynamicObject, scene, ellipsoid) {
        /**
         * The object to track with the camera.
         * @type DynamicObject
         */
        this.dynamicObject = dynamicObject;

        /**
         * The scene in which to track the object.
         * @type Scene
         */
        this.scene = scene;
        this._lastScene = undefined;

        /**
         * The ellipsoid to use for orienting the camera.
         * @type Ellipsoid
         */
        this.ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);

        //Shadow copies of the objects so we can detect changes.
        this._lastDynamicObject = undefined;
        this._mode = undefined;

        //Re-usable objects to be used for retrieving position.
        this._lastCartesian = new Cartesian3();
        this._lastCartographic = new Cartographic();

        //Current distance of dynamicObject from camera so we can maintain view distance across scene modes.
        this._lastDistance = undefined;

        //Last viewing offset in 3D/Columbus view, this way we can restore to a sensible view across scene modes.
        this._lastOffset = new Cartesian3();

        //Scratch value for calculating offsets
        this._offsetScratch = new Cartesian3();

        //Tracks camera up so that we can detect 2D camera rotation and modify the 3D/Columbus view to match when switching modes.
        this._first2dUp = new Cartesian2();
        this._last2dUp = new Cartesian2();
    };

    /**
    * Should be called each animation frame to update the camera
    * to the latest settings.
    * @param {JulianDate} time The current animation time.
    *
    * @exception {DeveloperError} time is required.
    * @exception {DeveloperError} DynamicObjectView.scene is required.
    * @exception {DeveloperError} DynamicObjectView.dynamicObject is required.
    * @exception {DeveloperError} DynamicObjectView.ellipsoid is required.
    * @exception {DeveloperError} DynamicObjectView.dynamicObject.position is required.
    */
    DynamicObjectView.prototype.update = function(time) {
        if (typeof time === 'undefined') {
            throw new DeveloperError('time is required.');
        }

        var scene = this.scene;
        if (typeof scene === 'undefined') {
            throw new DeveloperError('DynamicObjectView.scene is required.');
        }

        if (scene !== this._lastScene) {
            this._lastScene = scene;
            this._screenSpaceCameraController = scene.getScreenSpaceCameraController();
        }

        var dynamicObject = this.dynamicObject;
        if (typeof dynamicObject === 'undefined') {
            throw new DeveloperError('DynamicObjectView.dynamicObject is required.');
        }

        var ellipsoid = this.ellipsoid;
        if (typeof ellipsoid === 'undefined') {
            throw new DeveloperError('DynamicObjectView.ellipsoid is required.');
        }

        var positionProperty = this.dynamicObject.position;
        if (typeof positionProperty === 'undefined') {
            throw new DeveloperError('dynamicObject.position is required.');
        }

        var objectChanged = dynamicObject !== this._lastDynamicObject;

        //Determine what the current camera offset should be, this is used
        //to either set the default view when a new object is selected or
        //maintain a similar view when changing scene modes.
        var offset = this._offsetScratch;
        if (objectChanged) {
            this._lastDynamicObject = dynamicObject;

            var viewFromProperty = this.dynamicObject.viewFrom;
            if (typeof viewFromProperty === 'undefined' || typeof viewFromProperty.getValue(time, offset) === 'undefined') {
                Cartesian3.clone(dynamicObjectViewDefaultOffset, offset);
            }

            //Reset object-based cached values.
            var first2dUp = this._first2dUp;
            var last2dUp = this._last2dUp;
            first2dUp.x = first2dUp.y = 0;
            last2dUp.x = last2dUp.y = 0;
            Cartesian3.clone(offset, this._lastOffset);
            this._lastDistance = offset.magnitude();

            //If looking straight down, move the camera slightly south the avoid gimbal lock.
            if (Cartesian3.equals(offset.normalize(dynamicObjectViewCartesian3Scratch), Cartesian3.UNIT_Z)) {
                offset.y -= 0.01;
            }
        } else if (typeof this._lastOffset !== 'undefined') {
            offset = this._lastOffset;
        } else {
            Cartesian3.clone(dynamicObjectViewDefaultOffset, offset);
        }

        var mode = scene.mode;
        if (mode === SceneMode.SCENE2D) {
            update2D(this, scene.getCamera(), objectChanged, offset, positionProperty, time, scene.scene2D.projection);
        } else if (mode === SceneMode.SCENE3D) {
            update3D(this, scene.getCamera(), objectChanged, offset, positionProperty, time, ellipsoid);
        } else if (mode === SceneMode.COLUMBUS_VIEW) {
            updateColumbus(this, scene.getCamera(), objectChanged, offset, positionProperty, time, ellipsoid, scene.scene2D.projection);
        }
    };

    return DynamicObjectView;
});<commit_msg>Fix LVLH transform. Thanks @rcpage3!<commit_after>/*global define*/
define([
        '../Core/defaultValue',
        '../Core/DeveloperError',
        '../Core/Math',
        '../Core/Cartesian2',
        '../Core/Cartesian3',
        '../Core/Cartesian4',
        '../Core/Cartographic',
        '../Core/Quaternion',
        '../Core/Matrix3',
        '../Core/Matrix4',
        '../Core/Ellipsoid',
        '../Core/Transforms',
        '../Scene/CameraColumbusViewMode',
        '../Scene/SceneMode'
       ], function(
         defaultValue,
         DeveloperError,
         CesiumMath,
         Cartesian2,
         Cartesian3,
         Cartesian4,
         Cartographic,
         Quaternion,
         Matrix3,
         Matrix4,
         Ellipsoid,
         Transforms,
         CameraColumbusViewMode,
         SceneMode) {
    "use strict";

    function update2D(that, camera, objectChanged, offset, positionProperty, time, projection) {
        var viewDistance;
        var scene = that.scene;
        var modeChanged = scene.mode !== that._mode;

        if (modeChanged) {
            that._mode = scene.mode;
            that._screenSpaceCameraController.enableTranslate = false;
            viewDistance = offset.magnitude();
        } else if (objectChanged) {
            viewDistance = offset.magnitude();
        } else {
            viewDistance = camera.position.z;
        }

        var cartographic = positionProperty.getValueCartographic(time, that._lastCartographic);
        if (typeof cartographic !== 'undefined') {
            //We are assigning the position of the camera, not of the object, so modify the height appropriately.
            cartographic.height = viewDistance;
            if (objectChanged || modeChanged) {
                camera.controller.setPositionCartographic(cartographic);

                //Set rotation to match offset.
                Cartesian3.normalize(offset, camera.up);
                Cartesian3.negate(camera.up, camera.up);
                Cartesian3.cross(camera.direction, camera.up, camera.right);

                //z is always zero in 2D for up and right
                camera.up.z = 0;
                Cartesian3.normalize(camera.up, camera.up);
                camera.right.z = 0;
                Cartesian3.normalize(camera.right, camera.right);

                //Remember what up was when we started, so we
                //can detect rotation when we are finished.
                Cartesian2.clone(camera.right, that._first2dUp);
            } else {
                camera.position = projection.project(cartographic);
            }

            //Store last view distance and up vector.
            that._lastDistance = camera.frustum.right - camera.frustum.left;
            Cartesian2.clone(camera.right, that._last2dUp);
        }
    }

    var update3DTransform;
    function update3D(that, camera, objectChanged, offset, positionProperty, time, ellipsoid) {
        update3DController(that, camera, objectChanged, offset);

        var cartesian = positionProperty.getValueCartesian(time, that._lastCartesian);
        if (typeof cartesian !== 'undefined') {
            //if (that.referenceFrameType === ReferenceFrameType.EAST_NORTH_UP) {
            //    camera.transform = Transforms.eastNorthUpToFixedFrame(cartesian, ellipsoid, update3DTransform);
            //} else if (that.referenceFrameType === ReferenceFrameType.LOCAL_VERTICAL_LOCAL_HORIZONTAL) {
                var deltaTime = time.addSeconds(1.0);
                var deltaCartesian = positionProperty.getValueCartesian(deltaTime);
                if (typeof deltaCartesian !== 'undefined') {
                    var toInertial = Transforms.computeFixedToIcrfMatrix(time);
                    var toInertialDelta = Transforms.computeFixedToIcrfMatrix(deltaTime);
                    var toFixed = Transforms.computeIcrfToFixedMatrix(time);

                    // Z along the position
                    var zBasis = new Cartesian3();
                    Cartesian3.normalize(cartesian, zBasis);
                    Cartesian3.normalize(deltaCartesian, deltaCartesian);

                    Matrix3.multiplyByVector(toInertial, zBasis, zBasis);
                    Matrix3.multiplyByVector(toInertialDelta, deltaCartesian, deltaCartesian);

                    // Y is along the angular momentum vector (e.g. "orbit normal")
                    var yBasis = Cartesian3.cross(zBasis, deltaCartesian);

                    //Cartesian3.normalize(zBasis, zBasis);
                    Cartesian3.normalize(yBasis, yBasis);

                    // X is along the cross of y and z (right handed basis / in the direction of motion)
                    var xBasis = Cartesian3.cross(yBasis, zBasis);

                    Cartesian3.normalize(xBasis, xBasis);

                    Matrix3.multiplyByVector(toFixed, xBasis, xBasis);
                    Matrix3.multiplyByVector(toFixed, yBasis, yBasis);
                    Matrix3.multiplyByVector(toFixed, zBasis, zBasis);

                    Cartesian3.normalize(xBasis, xBasis);
                    Cartesian3.normalize(yBasis, yBasis);
                    Cartesian3.normalize(zBasis, zBasis);

                    camera.transform = new Matrix4(
                            xBasis.x, yBasis.x, zBasis.x, cartesian.x,
                            xBasis.y, yBasis.y, zBasis.y, cartesian.y,
                            xBasis.z, yBasis.z, zBasis.z, cartesian.z,
                            0.0,       0.0,         0.0,      1.0);
                }
            //}

            that._screenSpaceCameraController.setEllipsoid(Ellipsoid.UNIT_SPHERE);

            var position = camera.position;
            Cartesian3.clone(position, that._lastOffset);
            that._lastDistance = Cartesian3.magnitude(position);
        }
    }

    var updateColumbusCartesian4 = new Cartesian4(0.0, 0.0, 0.0, 1.0);
    function updateColumbus(that, camera, objectChanged, offset, positionProperty, time, ellipsoid, projection) {
        update3DController(that, camera, objectChanged, offset);

        //The swizzling here is intentional because ColumbusView uses a different coordinate system.
        var cartographic = positionProperty.getValueCartographic(time, that._lastCartographic);
        if (typeof cartographic !== 'undefined') {
            var projectedPosition = projection.project(cartographic);
            updateColumbusCartesian4.x = projectedPosition.z;
            updateColumbusCartesian4.y = projectedPosition.x;
            updateColumbusCartesian4.z = projectedPosition.y;

            var tranform = camera.transform;
            tranform.setColumn(3, updateColumbusCartesian4, tranform);

            var controller = that._screenSpaceCameraController;
            controller.enableTranslate = false;
            controller.setEllipsoid(Ellipsoid.UNIT_SPHERE);
            controller.columbusViewMode = CameraColumbusViewMode.LOCKED;

            camera.controller.constrainedAxis = Cartesian3.UNIT_Z;

            var position = camera.position;
            Cartesian3.clone(position, that._lastOffset);
            that._lastDistance = Cartesian3.magnitude(position);
        }
    }

    var update3DControllerQuaternion = new Quaternion();
    var update3DControllerMatrix3 = new Matrix3();

    function update3DController(that, camera, objectChanged, offset) {
        var scene = that.scene;
        that._screenSpaceCameraController.enableTilt = false;
        camera.controller.constrainedAxis = Cartesian3.UNIT_Z;

        if (objectChanged) {
            camera.controller.lookAt(offset, Cartesian3.ZERO, Cartesian3.UNIT_Z);
        } else if (scene.mode !== that._mode) {
            that._mode = scene.mode;

            //If we're switching from 2D and any rotation was applied to the camera,
            //apply that same rotation to the last offset used in 3D or Columbus view.
            var first2dUp = that._first2dUp;
            var last2dUp = that._last2dUp;
            if (!Cartesian2.equals(first2dUp, last2dUp)) {
                var startTheta = Math.acos(first2dUp.x);
                if (first2dUp.y < 0) {
                    startTheta = CesiumMath.TWO_PI - startTheta;
                }
                var endTheta = Math.acos(last2dUp.x);
                if (last2dUp.y < 0) {
                    endTheta = CesiumMath.TWO_PI - endTheta;
                }
                last2dUp.x = 0.0;
                last2dUp.y = 0.0;
                first2dUp.x = 0.0;
                first2dUp.y = 0.0;

                var theta = startTheta - endTheta;
                var rotation = Quaternion.fromAxisAngle(Cartesian3.UNIT_Z, theta, update3DControllerQuaternion);
                Matrix3.fromQuaternion(rotation, update3DControllerMatrix3).multiplyByVector(offset, offset);
            }
            offset.normalize(offset).multiplyByScalar(that._lastDistance, offset);
            camera.controller.lookAt(offset, Cartesian3.ZERO, Cartesian3.UNIT_Z);
        }
    }

    var dynamicObjectViewDefaultOffset = new Cartesian3(10000, -10000, 10000);
    var dynamicObjectViewCartesian3Scratch = new Cartesian3();

    /**
     * A utility object for tracking an object with the camera.
     * @alias DynamicObject
     * @constructor
     *
     * @param {DynamicObject} dynamicObject The object to track with the camera.
     * @param {Scene} scene The scene to use.
     * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid to use for orienting the camera.
     */
    var DynamicObjectView = function(dynamicObject, scene, ellipsoid) {
        /**
         * The object to track with the camera.
         * @type DynamicObject
         */
        this.dynamicObject = dynamicObject;

        /**
         * The scene in which to track the object.
         * @type Scene
         */
        this.scene = scene;
        this._lastScene = undefined;

        /**
         * The ellipsoid to use for orienting the camera.
         * @type Ellipsoid
         */
        this.ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);

        //Shadow copies of the objects so we can detect changes.
        this._lastDynamicObject = undefined;
        this._mode = undefined;

        //Re-usable objects to be used for retrieving position.
        this._lastCartesian = new Cartesian3();
        this._lastCartographic = new Cartographic();

        //Current distance of dynamicObject from camera so we can maintain view distance across scene modes.
        this._lastDistance = undefined;

        //Last viewing offset in 3D/Columbus view, this way we can restore to a sensible view across scene modes.
        this._lastOffset = new Cartesian3();

        //Scratch value for calculating offsets
        this._offsetScratch = new Cartesian3();

        //Tracks camera up so that we can detect 2D camera rotation and modify the 3D/Columbus view to match when switching modes.
        this._first2dUp = new Cartesian2();
        this._last2dUp = new Cartesian2();
    };

    /**
    * Should be called each animation frame to update the camera
    * to the latest settings.
    * @param {JulianDate} time The current animation time.
    *
    * @exception {DeveloperError} time is required.
    * @exception {DeveloperError} DynamicObjectView.scene is required.
    * @exception {DeveloperError} DynamicObjectView.dynamicObject is required.
    * @exception {DeveloperError} DynamicObjectView.ellipsoid is required.
    * @exception {DeveloperError} DynamicObjectView.dynamicObject.position is required.
    */
    DynamicObjectView.prototype.update = function(time) {
        if (typeof time === 'undefined') {
            throw new DeveloperError('time is required.');
        }

        var scene = this.scene;
        if (typeof scene === 'undefined') {
            throw new DeveloperError('DynamicObjectView.scene is required.');
        }

        if (scene !== this._lastScene) {
            this._lastScene = scene;
            this._screenSpaceCameraController = scene.getScreenSpaceCameraController();
        }

        var dynamicObject = this.dynamicObject;
        if (typeof dynamicObject === 'undefined') {
            throw new DeveloperError('DynamicObjectView.dynamicObject is required.');
        }

        var ellipsoid = this.ellipsoid;
        if (typeof ellipsoid === 'undefined') {
            throw new DeveloperError('DynamicObjectView.ellipsoid is required.');
        }

        var positionProperty = this.dynamicObject.position;
        if (typeof positionProperty === 'undefined') {
            throw new DeveloperError('dynamicObject.position is required.');
        }

        var objectChanged = dynamicObject !== this._lastDynamicObject;

        //Determine what the current camera offset should be, this is used
        //to either set the default view when a new object is selected or
        //maintain a similar view when changing scene modes.
        var offset = this._offsetScratch;
        if (objectChanged) {
            this._lastDynamicObject = dynamicObject;

            var viewFromProperty = this.dynamicObject.viewFrom;
            if (typeof viewFromProperty === 'undefined' || typeof viewFromProperty.getValue(time, offset) === 'undefined') {
                Cartesian3.clone(dynamicObjectViewDefaultOffset, offset);
            }

            //Reset object-based cached values.
            var first2dUp = this._first2dUp;
            var last2dUp = this._last2dUp;
            first2dUp.x = first2dUp.y = 0;
            last2dUp.x = last2dUp.y = 0;
            Cartesian3.clone(offset, this._lastOffset);
            this._lastDistance = offset.magnitude();

            //If looking straight down, move the camera slightly south the avoid gimbal lock.
            if (Cartesian3.equals(offset.normalize(dynamicObjectViewCartesian3Scratch), Cartesian3.UNIT_Z)) {
                offset.y -= 0.01;
            }
        } else if (typeof this._lastOffset !== 'undefined') {
            offset = this._lastOffset;
        } else {
            Cartesian3.clone(dynamicObjectViewDefaultOffset, offset);
        }

        var mode = scene.mode;
        if (mode === SceneMode.SCENE2D) {
            update2D(this, scene.getCamera(), objectChanged, offset, positionProperty, time, scene.scene2D.projection);
        } else if (mode === SceneMode.SCENE3D) {
            update3D(this, scene.getCamera(), objectChanged, offset, positionProperty, time, ellipsoid);
        } else if (mode === SceneMode.COLUMBUS_VIEW) {
            updateColumbus(this, scene.getCamera(), objectChanged, offset, positionProperty, time, ellipsoid, scene.scene2D.projection);
        }
    };

    return DynamicObjectView;
});<|endoftext|>


<commit_before>(function (ckan, jQuery) {

  /* Returns a Leaflet map to use on the different spatial widgets
   *
   * All Leaflet based maps should use this constructor to provide consistent
   * look and feel and avoid duplication.
   *
   * container               - HTML element or id of the map container
   * mapConfig               - (Optional) CKAN config related to the base map.
   *                           These are defined in the config ini file (eg
   *                           map type, API keys if necessary, etc).
   * leafletMapOptions       - (Optional) Options to pass to the Leaflet Map constructor
   * leafletBaseLayerOptions - (Optional) Options to pass to the Leaflet TileLayer constructor
   *
   * Examples
   *
   *   // Will return a map with attribution control
   *   var map = ckan.commonLeafletMap('map', mapConfig);
   *
   *   // For smaller maps where the attribution is shown outside the map, pass
   *   // the following option:
   *   var map = ckan.commonLeafletMap('map', mapConfig, {attributionControl: false});
   *
   * Returns a Leaflet map object.
   */
  ckan.commonLeafletMap = function (container,
                                    mapConfig,
                                    leafletMapOptions,
                                    leafletBaseLayerOptions) {

      var mapConfig = mapConfig || {type: 'mapquest'};
      var leafletMapOptions = leafletMapOptions || {};
      var leafletBaseLayerOptions = jQuery.extend(leafletBaseLayerOptions, {
                maxZoom: 18
                });

      map = new L.Map(container, leafletMapOptions);

      if (mapConfig.type == 'mapbox') {
          // MapBox base map
          if (!mapConfig['mapbox.map_id']) {
            throw '[CKAN Map Widgets] You need to provide a map ID ([account].[handle]) when using a MapBox layer. ' +
                  'See http://www.mapbox.com/developers/api-overview/ for details';
          }
          baseLayerUrl = '//{s}.tiles.mapbox.com/v3/{handle}/{z}/{x}/{y}.png';
          leafletBaseLayerOptions.handle = mapConfig['mapbox.map_id'];
          leafletBaseLayerOptions.subdomains = mapConfig.subdomains || 'abcd';
          leafletBaseLayerOptions.attribution = mapConfig.attribution || 'Data: OpenStreetMap, Design: MapBox';
      } else if (mapConfig.type == 'custom') {
          // Custom XYZ layer
          baseLayerUrl = mapConfig['custom.url'];
          if (mapConfig.subdomains) leafletBaseLayerOptions.subdomains = mapConfig.subdomains;
          leafletBaseLayerOptions.attribution = mapConfig.attribution;
      } else {
          // MapQuest OpenStreetMap base map
          baseLayerUrl = '//otile{s}.mqcdn.com/tiles/1.0.0/osm/{z}/{x}/{y}.png';
          leafletBaseLayerOptions.subdomains = mapConfig.subdomains || '1234';
          leafletBaseLayerOptions.attribution = mapConfig.attribution || 'Map data © OpenStreetMap contributors, Tiles Courtesy of MapQuest ';
      }

      var baseLayer = new L.TileLayer(baseLayerUrl, leafletBaseLayerOptions);
      map.addLayer(baseLayer);

      return map;

  }

})(this.ckan, this.jQuery);
<commit_msg>[#101] Change Mapquest tiles base URL under https<commit_after>(function (ckan, jQuery) {

  /* Returns a Leaflet map to use on the different spatial widgets
   *
   * All Leaflet based maps should use this constructor to provide consistent
   * look and feel and avoid duplication.
   *
   * container               - HTML element or id of the map container
   * mapConfig               - (Optional) CKAN config related to the base map.
   *                           These are defined in the config ini file (eg
   *                           map type, API keys if necessary, etc).
   * leafletMapOptions       - (Optional) Options to pass to the Leaflet Map constructor
   * leafletBaseLayerOptions - (Optional) Options to pass to the Leaflet TileLayer constructor
   *
   * Examples
   *
   *   // Will return a map with attribution control
   *   var map = ckan.commonLeafletMap('map', mapConfig);
   *
   *   // For smaller maps where the attribution is shown outside the map, pass
   *   // the following option:
   *   var map = ckan.commonLeafletMap('map', mapConfig, {attributionControl: false});
   *
   * Returns a Leaflet map object.
   */
  ckan.commonLeafletMap = function (container,
                                    mapConfig,
                                    leafletMapOptions,
                                    leafletBaseLayerOptions) {

      var isHttps = window.location.href.substring(0, 5).toLowerCase() === 'https';
      var mapConfig = mapConfig || {type: 'mapquest'};
      var leafletMapOptions = leafletMapOptions || {};
      var leafletBaseLayerOptions = jQuery.extend(leafletBaseLayerOptions, {
                maxZoom: 18
                });

      map = new L.Map(container, leafletMapOptions);

      if (mapConfig.type == 'mapbox') {
          // MapBox base map
          if (!mapConfig['mapbox.map_id']) {
            throw '[CKAN Map Widgets] You need to provide a map ID ([account].[handle]) when using a MapBox layer. ' +
                  'See http://www.mapbox.com/developers/api-overview/ for details';
          }
          baseLayerUrl = '//{s}.tiles.mapbox.com/v3/{handle}/{z}/{x}/{y}.png';
          leafletBaseLayerOptions.handle = mapConfig['mapbox.map_id'];
          leafletBaseLayerOptions.subdomains = mapConfig.subdomains || 'abcd';
          leafletBaseLayerOptions.attribution = mapConfig.attribution || 'Data: OpenStreetMap, Design: MapBox';
      } else if (mapConfig.type == 'custom') {
          // Custom XYZ layer
          baseLayerUrl = mapConfig['custom.url'];
          if (mapConfig.subdomains) leafletBaseLayerOptions.subdomains = mapConfig.subdomains;
          leafletBaseLayerOptions.attribution = mapConfig.attribution;
      } else {
          // MapQuest OpenStreetMap base map
          if (isHttps) {
            baseLayerUrl = '//otile{s}-s.mqcdn.com/tiles/1.0.0/osm/{z}/{x}/{y}.png';
          } else {
            baseLayerUrl = '//otile{s}.mqcdn.com/tiles/1.0.0/osm/{z}/{x}/{y}.png';
          }
          leafletBaseLayerOptions.subdomains = mapConfig.subdomains || '1234';
          leafletBaseLayerOptions.attribution = mapConfig.attribution || 'Map data © OpenStreetMap contributors, Tiles Courtesy of MapQuest ';
      }

      var baseLayer = new L.TileLayer(baseLayerUrl, leafletBaseLayerOptions);
      map.addLayer(baseLayer);

      return map;

  }

})(this.ckan, this.jQuery);
<|endoftext|>


<commit_before>var Lang = A.Lang;

/**
 * Widget extension, which can be used to add trigger support to the
 * base Widget class, through the [Base.build](Base.html#method_build) method.
 *
 * @class A.WidgetTrigger
 * @param {Object} The user configuration object
 */

function WidgetTrigger() {}

/**
 * Static property used to define the default attribute
 * configuration.
 *
 * @property ATTRS
 * @type Object
 * @static
 */
WidgetTrigger.ATTRS = {
    /**
     * Determine if the Toggler should bind DOM events or not.
     *
     * @attribute bindDOMEvents
     * @default true
     * @type Boolean
     * @writeOnce
     */
    bindDOMEvents: {
        validator: Lang.isBoolean,
        value: true,
        writeOnce: true
    },

    /**
     * Trigger node to change widget visibility state.
     *
     * @attribute trigger
     */
    trigger: {
        setter: A.one
    },

    /**
     * DOM event to hide the tooltip.
     *
     * @attribute triggerHideEvent
     * @type String
     */
    triggerHideEvent: {
        value: null
    },

    /**
     * DOM event to show the tooltip.
     *
     * @attribute triggerShowEvent
     * @type String
     */
    triggerShowEvent: {
        value: null
    },

    triggerShowFn: {
        validator: Lang.isString,
        value: 'show'
    },

    triggerHideFn: {
        validator: Lang.isString,
        value: 'hide'
    },

    triggerToggleFn: {
        validator: Lang.isString,
        value: 'toggle'
    },

    /**
     * DOM event to toggle the tooltip.
     *
     * @attribute triggerToggleEvent
     * @type String
     */
    triggerToggleEvent: {
        value: null
    }
};

A.mix(WidgetTrigger.prototype, {
    _eventHandles: null,

    /**
     * Construction logic executed during WidgetTrigger
     * instantiation. Lifecycle.
     *
     * @method initializer
     */
    initializer: function() {
        var instance = this;

        A.after(instance._afterRenderUIWT, instance, 'renderUI');

        instance.after('triggerChange', instance._afterTriggerChange);
    },

    /**
     * Destructor lifecycle implementation for the `WidgetTrigger` class.
     *
     * @method destructor
     * @protected
     */
    destructor: function() {
        var instance = this;

        (new A.EventHandle(instance._eventHandles)).detach();
    },

    /**
     * Fire after `renderUI`.
     *
     * @method _afterRenderUIWT
     * @param event
     * @protected
     */
    _afterRenderUIWT: function() {
        var instance = this;

        instance._uiSetTrigger(instance.get('trigger'));
    },

    /**
     * Fire after `trigger` changes.
     *
     * @method _afterTriggerChange
     * @param event
     * @protected
     */
    _afterTriggerChange: function(event) {
        var instance = this;

        instance._uiSetTrigger(event.newVal);
    },

    /**
     * Set the `trigger` UI.
     *
     * @method _uiSetTrigger
     * @param val
     * @protected
     */
    _uiSetTrigger: function(val) {
        var instance = this,
            eventHandles,
            triggerHideEvent,
            triggerShowEvent,
            triggerToggleEvent,
            triggerHideFn,
            triggerShowFn,
            triggerToggleFn;

        (new A.EventHandle(instance._eventHandles)).detach();

        if (val && instance.get('bindDOMEvents')) {
            eventHandles = instance._eventHandles = [];

            triggerHideEvent = instance.get('triggerHideEvent');
            triggerShowEvent = instance.get('triggerShowEvent');
            triggerToggleEvent = instance.get('triggerToggleEvent');
            triggerHideFn = instance.get('triggerHideFn');
            triggerShowFn = instance.get('triggerShowFn');
            triggerToggleFn = instance.get('triggerToggleFn');

            if (triggerHideEvent) {
                eventHandles.push(
                    val.on(instance.get('triggerHideEvent'), instance[triggerHideFn], instance));
            }

            if (triggerShowEvent) {
                eventHandles.push(
                    val.on(instance.get('triggerShowEvent'), instance[triggerShowFn], instance));
            }

            if (triggerToggleEvent) {
                eventHandles.push(
                    val.on(instance.get('triggerToggleEvent'), instance[triggerToggleFn], instance));
            }
        }
    }
});

A.WidgetTrigger = WidgetTrigger;
<commit_msg>AUI-1341 Rename variable _eventHandles of Widget Trigger to _triggerEventHandles<commit_after>var Lang = A.Lang;

/**
 * Widget extension, which can be used to add trigger support to the
 * base Widget class, through the [Base.build](Base.html#method_build) method.
 *
 * @class A.WidgetTrigger
 * @param {Object} The user configuration object
 */

function WidgetTrigger() {}

/**
 * Static property used to define the default attribute
 * configuration.
 *
 * @property ATTRS
 * @type Object
 * @static
 */
WidgetTrigger.ATTRS = {
    /**
     * Determine if the Toggler should bind DOM events or not.
     *
     * @attribute bindDOMEvents
     * @default true
     * @type Boolean
     * @writeOnce
     */
    bindDOMEvents: {
        validator: Lang.isBoolean,
        value: true,
        writeOnce: true
    },

    /**
     * Trigger node to change widget visibility state.
     *
     * @attribute trigger
     */
    trigger: {
        setter: A.one
    },

    /**
     * DOM event to hide the tooltip.
     *
     * @attribute triggerHideEvent
     * @type String
     */
    triggerHideEvent: {
        value: null
    },

    /**
     * DOM event to show the tooltip.
     *
     * @attribute triggerShowEvent
     * @type String
     */
    triggerShowEvent: {
        value: null
    },

    triggerShowFn: {
        validator: Lang.isString,
        value: 'show'
    },

    triggerHideFn: {
        validator: Lang.isString,
        value: 'hide'
    },

    triggerToggleFn: {
        validator: Lang.isString,
        value: 'toggle'
    },

    /**
     * DOM event to toggle the tooltip.
     *
     * @attribute triggerToggleEvent
     * @type String
     */
    triggerToggleEvent: {
        value: null
    }
};

A.mix(WidgetTrigger.prototype, {
    _triggerEventHandles: null,

    /**
     * Construction logic executed during WidgetTrigger
     * instantiation. Lifecycle.
     *
     * @method initializer
     */
    initializer: function() {
        var instance = this;

        A.after(instance._afterRenderUIWT, instance, 'renderUI');

        instance.after('triggerChange', instance._afterTriggerChange);
    },

    /**
     * Destructor lifecycle implementation for the `WidgetTrigger` class.
     *
     * @method destructor
     * @protected
     */
    destructor: function() {
        var instance = this;

        (new A.EventHandle(instance._triggerEventHandles)).detach();
    },

    /**
     * Fire after `renderUI`.
     *
     * @method _afterRenderUIWT
     * @param event
     * @protected
     */
    _afterRenderUIWT: function() {
        var instance = this;

        instance._uiSetTrigger(instance.get('trigger'));
    },

    /**
     * Fire after `trigger` changes.
     *
     * @method _afterTriggerChange
     * @param event
     * @protected
     */
    _afterTriggerChange: function(event) {
        var instance = this;

        instance._uiSetTrigger(event.newVal);
    },

    /**
     * Set the `trigger` UI.
     *
     * @method _uiSetTrigger
     * @param val
     * @protected
     */
    _uiSetTrigger: function(val) {
        var instance = this,
            eventHandles,
            triggerHideEvent,
            triggerShowEvent,
            triggerToggleEvent,
            triggerHideFn,
            triggerShowFn,
            triggerToggleFn;

        (new A.EventHandle(instance._triggerEventHandles)).detach();

        if (val && instance.get('bindDOMEvents')) {
            eventHandles = instance._triggerEventHandles = [];

            triggerHideEvent = instance.get('triggerHideEvent');
            triggerShowEvent = instance.get('triggerShowEvent');
            triggerToggleEvent = instance.get('triggerToggleEvent');
            triggerHideFn = instance.get('triggerHideFn');
            triggerShowFn = instance.get('triggerShowFn');
            triggerToggleFn = instance.get('triggerToggleFn');

            if (triggerHideEvent) {
                eventHandles.push(
                    val.on(instance.get('triggerHideEvent'), instance[triggerHideFn], instance));
            }

            if (triggerShowEvent) {
                eventHandles.push(
                    val.on(instance.get('triggerShowEvent'), instance[triggerShowFn], instance));
            }

            if (triggerToggleEvent) {
                eventHandles.push(
                    val.on(instance.get('triggerToggleEvent'), instance[triggerToggleFn], instance));
            }
        }
    }
});

A.WidgetTrigger = WidgetTrigger;
<|endoftext|>


<commit_before>import { onLoad, route, installSelectpickers } from './util';

onLoad(() => {
  $(document).on('ajax:success', 'a.feedback-button[data-remote]', e => {
    if (!$(e.target).hasClass('on-post')) {
      $('.post-cell-' + e.target.dataset.postId).remove();
      e.target.closest('tr').remove();
    }
  });
});

route(/\/review\/[\w-]+\/?\d*$/i, async () => {
  const queuePath = (/(\/review\/[\w-]+)\/?\d*$/i).exec(location.pathname)[1];
  const hasItemID = location.pathname.match(/\/review\/[\w-]+\/?\d+$/i);

  const loadNextPost = async () => {
    const filters = $('.review-filter').toArray();
    const params = {};
    filters.forEach(e => {
      const el = $(e);
      const val = el.val();
      if (val) {
        params[el.attr('name')] = val;
      }
    });
    const url = new URL(queuePath + '/next', `${location.protocol}//${location.host}`);
    const search = new URLSearchParams(params);
    url.search = search;

    const response = await fetch(url, {
      credentials: 'include'
    });
    const html = await response.text();
    $('.review-item-container').html(html);

    try {
      if (window.location.pathname.indexOf('/review/posts') === 0) {
        const postTitle = $('h4')[0].firstChild.textContent.trim();
        const postID = $('h4 a').attr('href').replace(/^\D*(\d+)/, '$1');
        const relativeUrl = $('.review-submit-link').first().attr('href').replace(/\?.*/, '');
        const reviewID = relativeUrl.match(/\d+/)[0];
        const title = `Posts Review ${reviewID}: post ID ${postID}: ${postTitle} - metasmoke`;
        document.title = title;
        history.pushState({}, title, relativeUrl);
      }
      else if (window.location.pathname.indexOf('/review/untagged-domains') === 0) {
        document.title = 'Review Untagged Domains - metasmoke';
      }
      else if (window.location.pathname.indexOf('/review/admin-flags') === 0) {
        document.title = 'Review Admin Flags - metasmoke';
      }
    }
    catch (err) {
      console.error(err); // eslint-disable-line no-console
    }

    installSelectpickers();
  };

  if (!hasItemID) {
    loadNextPost();
  }

  $(document).on('ajax:success', '.review-submit-link', () => {
    $('.review-item-container').text('Loading...');
    loadNextPost();
  });

  $(document).on('click', '.review-next-link', () => {
    $('.review-item-container').text('Loading...');
    loadNextPost();
  });

  $('#filter-button').click(() => {
    $('.review-item-container').text('Loading...');
    loadNextPost();
  });
});

route(/\/review\/untagged-domains(\/\d*)?/, () => {
  $(document).on('ajax:success', '.review-add-domain-tag', (e, data) => {
    const $noTags = $('.no-tags');
    if ($noTags.length > 0) {
      $noTags.remove();
      $('.domain-tag-list p').html('Tagged with: ' + data);
    }
    else {
      $('.domain-tag-list p').append(data);
    }
    $(e.target).find('select').selectpicker('val', null);
  });
});
<commit_msg>Also handle review history pages<commit_after>import { onLoad, route, installSelectpickers } from './util';

onLoad(() => {
  $(document).on('ajax:success', 'a.feedback-button[data-remote]', e => {
    if (!$(e.target).hasClass('on-post')) {
      $('.post-cell-' + e.target.dataset.postId).remove();
      e.target.closest('tr').remove();
    }
  });
});

route(/\/review\/[\w-]+\/?\d*$/i, async () => {
  const queuePath = (/(\/review\/[\w-]+)\/?\d*$/i).exec(location.pathname)[1];
  const hasItemID = location.pathname.match(/\/review\/[\w-]+\/?\d+$/i);

  const loadNextPost = async () => {
    const filters = $('.review-filter').toArray();
    const params = {};
    filters.forEach(e => {
      const el = $(e);
      const val = el.val();
      if (val) {
        params[el.attr('name')] = val;
      }
    });
    const url = new URL(queuePath + '/next', `${location.protocol}//${location.host}`);
    const search = new URLSearchParams(params);
    url.search = search;

    const response = await fetch(url, {
      credentials: 'include'
    });
    const html = await response.text();
    $('.review-item-container').html(html);

    try {
      const pathname = window.location.pathname;
      const isHistory = pathname.endsWith('/history');
      const historyText = isHistory ? '' : 'history for ';
      const reviewTypeText = pathname.split('/')[2].split('-').map(word => word[0].toUpperCase() + word.slice(1)).join(' ');
      if (!isHistory && pathname.indexOf('/review/posts') === 0) {
        const postTitle = $('h4')[0].firstChild.textContent.trim();
        const postID = $('h4 a').attr('href').replace(/^\D*(\d+)/, '$1');
        const relativeUrl = $('.review-submit-link').first().attr('href').replace(/\?.*/, '');
        const reviewID = relativeUrl.match(/\d+/)[0];
        const title = `Review Posts ${reviewID}: post ID ${postID}: ${postTitle} - metasmoke`;
        document.title = title;
        history.pushState({}, title, relativeUrl);
      }
      else {
        document.title = `Review ${reviewTypeText} ${historyText}- metasmoke`;
      }
    }
    catch (err) {
      console.error(err); // eslint-disable-line no-console
    }

    installSelectpickers();
  };

  if (!hasItemID) {
    loadNextPost();
  }

  $(document).on('ajax:success', '.review-submit-link', () => {
    $('.review-item-container').text('Loading...');
    loadNextPost();
  });

  $(document).on('click', '.review-next-link', () => {
    $('.review-item-container').text('Loading...');
    loadNextPost();
  });

  $('#filter-button').click(() => {
    $('.review-item-container').text('Loading...');
    loadNextPost();
  });
});

route(/\/review\/untagged-domains(\/\d*)?/, () => {
  $(document).on('ajax:success', '.review-add-domain-tag', (e, data) => {
    const $noTags = $('.no-tags');
    if ($noTags.length > 0) {
      $noTags.remove();
      $('.domain-tag-list p').html('Tagged with: ' + data);
    }
    else {
      $('.domain-tag-list p').append(data);
    }
    $(e.target).find('select').selectpicker('val', null);
  });
});
<|endoftext|>


<commit_before>({
  init: function(cmp) {
    var wrapper = document.getElementById('signatureWrapper'),
      canvas = wrapper.querySelector('canvas'),
      minWidth = parseFloat(cmp.get('v.minWidth')),
      maxWidth = parseFloat(cmp.get('v.maxWidth')),
      penColor = cmp.get('v.penColor');

    function resizeCanvas() {
      var ratio = Math.max(window.devicePixelRatio || 1, 1);
      canvas.width = canvas.offsetWidth * ratio;
      canvas.height = canvas.offsetHeight * ratio;
      canvas.getContext("2d").scale(ratio, ratio);
    }

    window.addEventListener('resize', resizeCanvas);
    cmp.set('v.signaturePad', new SignaturePad(canvas, {
      minWidth: minWidth,
      maxWidth: maxWidth,
      penColor: penColor
    }));
    resizeCanvas();
  },

  capture: function(cmp) {
    var dataUrl, pad = cmp.get('v.signaturePad');
    pad.trimCanvas();
    dataUrl = pad.toDataURL();
    cmp.set('v.signatureData', dataUrl);
  },

  clear: function(cmp) {
    cmp.get('v.signaturePad').clear();
  },

  touch: function(cmp) {
    var e = document.querySelector('input.esignature-hide');
    e.focus();
  }
})
<commit_msg>remove resize event listener. test on android, ipad, web browsers<commit_after>({
  init: function(cmp) {
    var wrapper = document.getElementById('signatureWrapper'),
      canvas = wrapper.querySelector('canvas'),
      minWidth = parseFloat(cmp.get('v.minWidth')),
      maxWidth = parseFloat(cmp.get('v.maxWidth')),
      penColor = cmp.get('v.penColor');

    function resizeCanvas() {
      var ratio = Math.max(window.devicePixelRatio || 1, 1);
      canvas.width = canvas.offsetWidth * ratio;
      canvas.height = canvas.offsetHeight * ratio;
      canvas.getContext("2d").scale(ratio, ratio);
    }

    cmp.set('v.signaturePad', new SignaturePad(canvas, {
      minWidth: minWidth,
      maxWidth: maxWidth,
      penColor: penColor
    }));
    resizeCanvas();
  },

  capture: function(cmp) {
    var dataUrl, pad = cmp.get('v.signaturePad');
    pad.trimCanvas();
    dataUrl = pad.toDataURL();
    cmp.set('v.signatureData', dataUrl);
  },

  clear: function(cmp) {
    cmp.get('v.signaturePad').clear();
  },

  touch: function(cmp) {
    var e = document.querySelector('input.esignature-hide');
    e.focus();
  }
})
<|endoftext|>


<commit_before>var app = angular.module("Controllers", []);

app.controller("GlobalMenuController", function($scope) {
  $scope.showCreateEntityPanelClick = function(){
    $scope.$parent.$broadcast("showCreateEntityPanel");
  };
});

app.controller("CreateEntityController", function($scope, $http){
  $scope.showCreateEntityPanel == false;

  $scope.hideAllPanels = function(){
    $scope.showCreateEntityPanel = undefined;
    $scope.showTypeErrorPanel = undefined;
  };

  $scope.showCreateEntityPanelClick = function(){
    $scope.showCreateEntityPanel = true;
  }

  $scope.$on("showCreateEntityPanel", function(event){
    $scope.showCreateEntityPanel = true;
  });

  $scope.addNewProperty = function(){
    $scope.newEntity.properties.push({name: "", type: ""});
  };

  $scope.removeProperty = function(index){
    $scope.newEntity.properties.splice(index, 1);
  };

  $scope.resetNewEntity = function(){
    $scope.newEntity = {
      entityName: "",
      properties: [{
        name: "",
        type: "",
        required: true
      }]
    };
  };

  $scope.createEntityButton = function(){
    var entityCreationPath = "http://localhost:3000/createEntity";
    $http.post(entityCreationPath, $scope.newEntity).success(function(data, statusCode) {
      if(statusCode == 200){
        $scope.$parent.$broadcast("EntityCreateSuccessful");
        $scope.resetNewEntity();
        $scope.hideAllPanels();
        $scope.$parent.$broadcast("ReloadEntityListEvent");
      };
      console.log(statusCode);
    }).error(function(data, status){
      if(status == 501){
        //console.log(data);
        $scope.showTypeError(data.name, data.type);
      }
    });
  };

  $scope.showTypeError = function(name, type){
    $scope.showTypeErrorPanel = {
      name: name,
      type: type
    };
    // $scope.showTypeErrorPanel.type = type;
    // $scope.showTypeErrorPanel.name = name;
  }

  $scope.cancelCreateEntity = function(){
    $scope.resetNewEntity();
    $scope.hideAllPanels();
  };

  $scope.resetNewEntity();
});

app.controller("AlertsController", function($scope, $http){
  $scope.success = {};
  $scope.warning = {};
  $scope.fail = {};

  $scope.$on("EntityDeleteSuccessfulEvent", function(event){
    $scope.resetAlerts();
    $scope.success.entityDelete = true;
  });

  $scope.$on("EntityDeleteFailEvent", function(event){
    $scope.resetAlerts();
    $scope.success.entityDelete = true;
  });

  $scope.$on("EntityCreateSuccessful", function(event) {
    $scope.resetAlerts();
    $scope.success.entityCreate = true;
  })

  $scope.resetAlerts = function(){
    $scope.success = {};
    $scope.warning = {};
    $scope.fail = {};
  };
});

app.controller("NavigationController", function($scope, $http){
  $scope.entities = [];

  $scope.$on("ReloadEntityListEvent", function(event){
    $scope.listEntities();
  });

  $scope.listEntities = function(){
    $http.get("/listEntities").success(function(data, status){
      if(status == 200){
        $scope.entities = data.entityList;
      }
    });
  }

  $scope.displayDetail = function(id){
    if($scope.entities == undefined || $scope.entities == []){
      return;
    }

    for(var i = 0; i < $scope.entities.length; i++){
      var entity = $scope.entities[i];
      if(entity._id == id){
        $scope.$parent.$broadcast('DisplayDetail', entity);
        break;
      }
    }
  };

  $scope.listEntities();

});

app.controller("EntityDetailController", function($scope, $http){
  $scope.newObject = {props: {}};
  $scope.instanceList = [];
  $scope.showCreateInstancePanel = false;
  $scope.showListInstancePanel = false;
  $scope.success = {};
  $scope.warning = {};
  $scope.fail = {};

  $scope.hideAllPanels = function(){
    $scope.showCreateInstancePanel = false;
    $scope.showListInstancePanel = false;
  };

  $scope.createInstancePanelClick = function(){
    $scope.hideAllPanels();
    $scope.showCreateInstancePanel = true;
  }

  $scope.listInstancePanelClick = function(){
    $scope.hideAllPanels();
    $scope.showListInstancePanel = true;
  }

  $scope.countInstances = function(){
    var instances = angular.fromJson($scope.instanceList);
    if(instances == undefined || instances.instanceList == undefined){
      return 0;
    }
    return instances.instanceList.length;
  }

  $scope.resetAlerts = function(){
    $scope.success = {};
    $scope.warning = {};
    $scope.fail = {};
  }

  $scope.clearDetail = function(){
    $scope.hideAllPanels();
    $scope.resetAlerts();
    $scope.entity = undefined;
  }

  $scope.deleteEntityConfirmClick = function(entityId){
    console.log("Just confirmed to delete " + entityId + " entity");
    var entityDeletePath = "http://localhost:3000/deleteEntity/" + entityId;

    $http.get(entityDeletePath).success(function(data, statusCode) {
      if(statusCode == 200 && data.status == 'OK'){
        $scope.$parent.$broadcast("EntityDeleteSuccessfulEvent");
        $scope.clearDetail();
        $scope.$parent.$broadcast("ReloadEntityListEvent");
      } else {
        $scope.$parent.$broadcast("EntityDeleteFailEvent");
      }
    }).error(function(){
      $scope.$parent.$broadcast("EntityDeleteFailEvent");
    });
  }

  $scope.$on("DisplayDetail", function(event, entity){
    $scope.entity = entity;

    var listPath = "http://localhost:3000/EAG/access/" + entity.name + "/list";

    $http.get(listPath).success(function(data, status){
      if(status == 200){
        $scope.instanceList = JSON.stringify(data, null, 4);
      }
    });
  });

  $scope.createInstance = function(){
    var instance = $scope.newObject.props;
    var creationPath = "http://localhost:3000/EAG/access/" + $scope.entity.name + "/create";

    $http.post(creationPath, instance).success(function(instance, status){
      if(status == 200 && instance != undefined && instance._id != undefined){
        $scope.$parent.$broadcast("DisplayDetail", $scope.entity);
        $scope.newObject.props = {};
      } else {
        console.log("Failed to create instance.");
        console.log(instance);
        console.log(status);
      }
    });
  };
});
<commit_msg>Cleanup.<commit_after>var app = angular.module("Controllers", []);

app.controller("GlobalMenuController", function($scope) {
  $scope.showCreateEntityPanelClick = function(){
    $scope.$parent.$broadcast("showCreateEntityPanel");
  };
});

app.controller("CreateEntityController", function($scope, $http){
  $scope.showCreateEntityPanel == false;

  $scope.hideAllPanels = function(){
    $scope.showCreateEntityPanel = undefined;
    $scope.showTypeErrorPanel = undefined;
  };

  $scope.showCreateEntityPanelClick = function(){
    $scope.showCreateEntityPanel = true;
  }

  $scope.$on("showCreateEntityPanel", function(event){
    $scope.showCreateEntityPanel = true;
  });

  $scope.addNewProperty = function(){
    $scope.newEntity.properties.push({name: "", type: ""});
  };

  $scope.removeProperty = function(index){
    $scope.newEntity.properties.splice(index, 1);
  };

  $scope.resetNewEntity = function(){
    $scope.newEntity = {
      entityName: "",
      properties: [{
        name: "",
        type: "",
        required: true
      }]
    };
  };

  $scope.createEntityButton = function(){
    var entityCreationPath = "http://localhost:3000/createEntity";
    $http.post(entityCreationPath, $scope.newEntity).success(function(data, statusCode) {
      if(statusCode == 200){
        $scope.$parent.$broadcast("EntityCreateSuccessful");
        $scope.resetNewEntity();
        $scope.hideAllPanels();
        $scope.$parent.$broadcast("ReloadEntityListEvent");
      };
    }).error(function(data, status){
      if(status == 501){
        $scope.showTypeError(data.name, data.type);
      }
    });
  };

  $scope.showTypeError = function(name, type){
    $scope.showTypeErrorPanel = {
      name: name,
      type: type
    };
  }

  $scope.cancelCreateEntity = function(){
    $scope.resetNewEntity();
    $scope.hideAllPanels();
  };

  $scope.resetNewEntity();
});

app.controller("AlertsController", function($scope, $http){
  $scope.success = {};
  $scope.warning = {};
  $scope.fail = {};

  $scope.$on("EntityDeleteSuccessfulEvent", function(event){
    $scope.resetAlerts();
    $scope.success.entityDelete = true;
  });

  $scope.$on("EntityDeleteFailEvent", function(event){
    $scope.resetAlerts();
    $scope.success.entityDelete = true;
  });

  $scope.$on("EntityCreateSuccessful", function(event) {
    $scope.resetAlerts();
    $scope.success.entityCreate = true;
  })

  $scope.resetAlerts = function(){
    $scope.success = {};
    $scope.warning = {};
    $scope.fail = {};
  };
});

app.controller("NavigationController", function($scope, $http){
  $scope.entities = [];

  $scope.$on("ReloadEntityListEvent", function(event){
    $scope.listEntities();
  });

  $scope.listEntities = function(){
    $http.get("/listEntities").success(function(data, status){
      if(status == 200){
        $scope.entities = data.entityList;
      }
    });
  }

  $scope.displayDetail = function(id){
    if($scope.entities == undefined || $scope.entities == []){
      return;
    }

    for(var i = 0; i < $scope.entities.length; i++){
      var entity = $scope.entities[i];
      if(entity._id == id){
        $scope.$parent.$broadcast('DisplayDetail', entity);
        break;
      }
    }
  };

  $scope.listEntities();

});

app.controller("EntityDetailController", function($scope, $http){
  $scope.newObject = {props: {}};
  $scope.instanceList = [];
  $scope.showCreateInstancePanel = false;
  $scope.showListInstancePanel = false;
  $scope.success = {};
  $scope.warning = {};
  $scope.fail = {};

  $scope.hideAllPanels = function(){
    $scope.showCreateInstancePanel = false;
    $scope.showListInstancePanel = false;
  };

  $scope.createInstancePanelClick = function(){
    $scope.hideAllPanels();
    $scope.showCreateInstancePanel = true;
  }

  $scope.listInstancePanelClick = function(){
    $scope.hideAllPanels();
    $scope.showListInstancePanel = true;
  }

  $scope.countInstances = function(){
    var instances = angular.fromJson($scope.instanceList);
    if(instances == undefined || instances.instanceList == undefined){
      return 0;
    }
    return instances.instanceList.length;
  }

  $scope.resetAlerts = function(){
    $scope.success = {};
    $scope.warning = {};
    $scope.fail = {};
  }

  $scope.clearDetail = function(){
    $scope.hideAllPanels();
    $scope.resetAlerts();
    $scope.entity = undefined;
  }

  $scope.deleteEntityConfirmClick = function(entityId){
    console.log("Just confirmed to delete " + entityId + " entity");
    var entityDeletePath = "http://localhost:3000/deleteEntity/" + entityId;

    $http.get(entityDeletePath).success(function(data, statusCode) {
      if(statusCode == 200 && data.status == 'OK'){
        $scope.$parent.$broadcast("EntityDeleteSuccessfulEvent");
        $scope.clearDetail();
        $scope.$parent.$broadcast("ReloadEntityListEvent");
      } else {
        $scope.$parent.$broadcast("EntityDeleteFailEvent");
      }
    }).error(function(){
      $scope.$parent.$broadcast("EntityDeleteFailEvent");
    });
  }

  $scope.$on("DisplayDetail", function(event, entity){
    $scope.entity = entity;

    var listPath = "http://localhost:3000/EAG/access/" + entity.name + "/list";

    $http.get(listPath).success(function(data, status){
      if(status == 200){
        $scope.instanceList = JSON.stringify(data, null, 4);
      }
    });
  });

  $scope.createInstance = function(){
    var instance = $scope.newObject.props;
    var creationPath = "http://localhost:3000/EAG/access/" + $scope.entity.name + "/create";

    $http.post(creationPath, instance).success(function(instance, status){
      if(status == 200 && instance != undefined && instance._id != undefined){
        $scope.$parent.$broadcast("DisplayDetail", $scope.entity);
        $scope.newObject.props = {};
      } else {
        console.log("Failed to create instance.");
        console.log(instance);
        console.log(status);
      }
    });
  };
});
<|endoftext|>


<commit_before>'use strict';

const dal = require('./dal');

module.exports = {
    loadData: function(credentials) {
        let myisamSortBufferSize;
        let myisamMaxSortFileSize;
        let keyBufferSize;

        return dal.connection.getConnection(credentials)
        .then(function (conn) {
            return conn.connect();
        })
        //.then(function (conn) {
        //    return conn.query(dal.commands.getDefaultConfig);
        //})
        //.then(function (result) {
        //    myisamSortBufferSize = result.results[0]['@@myisam_sort_buffer_size'];
        //    myisamMaxSortFileSize = result.results[0]['@@myisam_max_sort_file_size'];
        //    keyBufferSize = result.results[0]['@@key_buffer_size'];
        //
        //    return result.connection;
        //})
        //.then(function (conn) {
        //    return conn.query(dal.commands.setPerformanceConfig);
        //})
        //.then((result) => result.connection)
        .then(function (conn) {
            return conn.query(`
                LOAD DATA LOCAL INFILE '/home/roger/projects/github/geonames-server-js/data/zip/allcountries/allCountries.txt'
                  INTO TABLE geonames_geoname;
                LOAD DATA LOCAL INFILE '/home/roger/projects/github/geonames-server-js/data/zip/no-country/null.txt'
                  INTO TABLE geonames_geoname;

                LOAD DATA LOCAL INFILE '/home/roger/projects/github/geonames-server-js/data/zip/hierarchy/hierarchy.txt'
                  INTO TABLE geonames_hierarchy;

                LOAD DATA LOCAL INFILE '/home/roger/projects/github/geonames-server-js/data/zip/alternatenames/iso-languagecodes.txt'
                  INTO TABLE geonames_iso_language
                  IGNORE 1 LINES;

                LOAD DATA LOCAL INFILE '/home/roger/projects/github/geonames-server-js/data/zip/alternatenames/alternateNames.txt'
                  INTO TABLE geonames_alternate_name;

                LOAD DATA LOCAL INFILE '/home/roger/projects/github/geonames-server-js/data/txt/continents.txt'
                  INTO TABLE geonames_continent
                  FIELDS TERMINATED BY ',';

                LOAD DATA LOCAL INFILE '/home/roger/projects/github/geonames-server-js/data/txt/countryInfo.txt'
                  INTO TABLE geonames_country
                   IGNORE 51 LINES;

                LOAD DATA LOCAL INFILE '/home/roger/projects/github/geonames-server-js/data/txt/admin1CodesASCII.txt'
                  INTO TABLE geonames_admin_code_ascii;
                LOAD DATA LOCAL INFILE '/home/roger/projects/github/geonames-server-js/data/txt/admin2Codes.txt'
                  INTO TABLE geonames_admin_code;

                LOAD DATA LOCAL INFILE '/home/roger/projects/github/geonames-server-js/data/txt/featureCodes_en.txt'
                  INTO TABLE geonames_feature;

                LOAD DATA LOCAL INFILE '/home/roger/projects/github/geonames-server-js/data/txt/timeZones.txt'
                  INTO TABLE geonames_timezone
                  IGNORE 1 LINES;

                LOAD DATA LOCAL INFILE '/home/roger/projects/github/geonames-server-js/data/zip/postalcodes/allCountries.txt'
                  INTO TABLE geonames_postal_code;`);
        });
        //.then(function (conn) {
        //    return conn.query(dal.commands.setDefaultConfig, [myisamSortBufferSize, myisamMaxSortFileSize, keyBufferSize]);
        //});
    }
};<commit_msg>Clean up.<commit_after>'use strict';

const dal = require('./dal');

module.exports = {
    loadData: function(credentials) {
        //let myisamSortBufferSize;
        //let myisamMaxSortFileSize;
        //let keyBufferSize;

        return dal.connection.getConnection(credentials)
        .then(function (conn) {
            return conn.connect();
        })
        //.then(function (conn) {
        //    return conn.query(dal.commands.getDefaultConfig);
        //})
        //.then(function (result) {
        //    myisamSortBufferSize = result.results[0]['@@myisam_sort_buffer_size'];
        //    myisamMaxSortFileSize = result.results[0]['@@myisam_max_sort_file_size'];
        //    keyBufferSize = result.results[0]['@@key_buffer_size'];
        //
        //    return result.connection;
        //})
        //.then(function (conn) {
        //    return conn.query(dal.commands.setPerformanceConfig);
        //})
        //.then((result) => result.connection)
        .then(function (conn) {
            return conn.query(`
                LOAD DATA LOCAL INFILE '/home/roger/projects/github/geonames-server-js/data/zip/allcountries/allCountries.txt'
                  INTO TABLE geonames_geoname;
                LOAD DATA LOCAL INFILE '/home/roger/projects/github/geonames-server-js/data/zip/no-country/null.txt'
                  INTO TABLE geonames_geoname;

                LOAD DATA LOCAL INFILE '/home/roger/projects/github/geonames-server-js/data/zip/hierarchy/hierarchy.txt'
                  INTO TABLE geonames_hierarchy;

                LOAD DATA LOCAL INFILE '/home/roger/projects/github/geonames-server-js/data/zip/alternatenames/iso-languagecodes.txt'
                  INTO TABLE geonames_iso_language
                  IGNORE 1 LINES;

                LOAD DATA LOCAL INFILE '/home/roger/projects/github/geonames-server-js/data/zip/alternatenames/alternateNames.txt'
                  INTO TABLE geonames_alternate_name;

                LOAD DATA LOCAL INFILE '/home/roger/projects/github/geonames-server-js/data/txt/continents.txt'
                  INTO TABLE geonames_continent
                  FIELDS TERMINATED BY ',';

                LOAD DATA LOCAL INFILE '/home/roger/projects/github/geonames-server-js/data/txt/countryInfo.txt'
                  INTO TABLE geonames_country
                   IGNORE 51 LINES;

                LOAD DATA LOCAL INFILE '/home/roger/projects/github/geonames-server-js/data/txt/admin1CodesASCII.txt'
                  INTO TABLE geonames_admin_code_ascii;
                LOAD DATA LOCAL INFILE '/home/roger/projects/github/geonames-server-js/data/txt/admin2Codes.txt'
                  INTO TABLE geonames_admin_code;

                LOAD DATA LOCAL INFILE '/home/roger/projects/github/geonames-server-js/data/txt/featureCodes_en.txt'
                  INTO TABLE geonames_feature;

                LOAD DATA LOCAL INFILE '/home/roger/projects/github/geonames-server-js/data/txt/timeZones.txt'
                  INTO TABLE geonames_timezone
                  IGNORE 1 LINES;

                LOAD DATA LOCAL INFILE '/home/roger/projects/github/geonames-server-js/data/zip/postalcodes/allCountries.txt'
                  INTO TABLE geonames_postal_code;`);
        });
        //.then(function (conn) {
        //    return conn.query(dal.commands.setDefaultConfig, [myisamSortBufferSize, myisamMaxSortFileSize, keyBufferSize]);
        //});
    }
};<|endoftext|>


<commit_before>var wp = module.exports;
var shell = require("shelljs");
var inquirer = require("inquirer");

wp.choices = [
	{
		name:"1) Watch Main (Main theme SCSS and JS)",
		value: "watch:main"
	},
	{
		name:"2) Watch All (Main Theme and Login SCSS and JS)",
		value: "watch:all"
	},
	{
		name:"3) Build Main (Main theme SCSS and JS)",
		value: "build:main:all"
	},
	{
		name: "4) Build All (Main Theme/Login SCSS/JS, Favicon, Imagemin)",
		value: "build:all"
	},
	{
		name: "5) Process Favicon (Connect to realfavicongenerator api and produce favicons)",
		value: "favicon"
	},
	{
		name: "6) Minimize Images",
		value: "imagemin"
	},
	{
		name: "7) Build Login CSS",
		value: "login:build:css"
	},
	{
		name: "8) Zip Theme",
		value: "zip"
	}
];

wp.display  = inquirer.prompt;<commit_msg>varios menues<commit_after>var wp = module.exports;
var shell = require("shelljs");
var inquirer = require("inquirer");

wp.choices = [
	{
		name:"1) Create CTP",
		value: "watch:main"
	},
	{
		name:"2) Create imagesize",
		value: "watch:all"
	}
];

wp.display  = inquirer.prompt;<|endoftext|>


<commit_before>/*
 *	@author zz85 / http://twitter.com/blurspline / http://www.lab4games.net/zz85/blog
 *
 *	Simplification Geometry Modifier
 *    - based on code and technique
 *	  - by Stan Melax in 1998
 *	  - Progressive Mesh type Polygon Reduction Algorithm
 *    - http://www.melax.com/polychop/
 */

THREE.SimplifyModifier = function () {};

( function () {

	var cb = new THREE.Vector3(), ab = new THREE.Vector3();

	function pushIfUnique( array, object ) {

		if ( array.indexOf( object ) === - 1 ) array.push( object );

	}

	function removeFromArray( array, object ) {

		var k = array.indexOf( object );
		if ( k > - 1 ) array.splice( k, 1 );

	}

	function computeEdgeCollapseCost( u, v ) {

		// if we collapse edge uv by moving u to v then how
		// much different will the model change, i.e. the "error".

		var edgelength = v.position.distanceTo( u.position );
		var curvature = 0;

		var sideFaces = [];
		var i, il = u.faces.length, face, sideFace;

		// find the "sides" triangles that are on the edge uv
		for ( i = 0; i < il; i ++ ) {

			face = u.faces[ i ];

			if ( face.hasVertex( v ) ) {

				sideFaces.push( face );

			}

		}

		// use the triangle facing most away from the sides
		// to determine our curvature term
		for ( i = 0; i < il; i ++ ) {

			var minCurvature = 1;
			face = u.faces[ i ];

			for ( var j = 0; j < sideFaces.length; j ++ ) {

				sideFace = sideFaces[ j ];
				// use dot product of face normals.
				var dotProd = face.normal.dot( sideFace.normal );
				minCurvature = Math.min( minCurvature, ( 1.001 - dotProd ) / 2 );

			}

			curvature = Math.max( curvature, minCurvature );

		}

		// crude approach in attempt to preserve borders
		// though it seems not to be totally correct
		var borders = 0;
		if ( sideFaces.length < 2 ) {

			// we add some arbitrary cost for borders,
			// borders += 10;
			curvature = 1;

		}

		var amt = edgelength * curvature + borders;

		return amt;

	}

	function computeEdgeCostAtVertex( v ) {

		// compute the edge collapse cost for all edges that start
		// from vertex v.  Since we are only interested in reducing
		// the object by selecting the min cost edge at each step, we
		// only cache the cost of the least cost edge at this vertex
		// (in member variable collapse) as well as the value of the
		// cost (in member variable collapseCost).

		if ( v.neighbors.length === 0 ) {

			// collapse if no neighbors.
			v.collapseNeighbor = null;
			v.collapseCost = - 0.01;

			return;

		}

		v.collapseCost = 100000;
		v.collapseNeighbor = null;

		// search all neighboring edges for "least cost" edge
		for ( var i = 0; i < v.neighbors.length; i ++ ) {

			var collapseCost = computeEdgeCollapseCost( v, v.neighbors[ i ] );

			if ( ! v.collapseNeighbor ) {

				v.collapseNeighbor = v.neighbors[ i ];
				v.collapseCost = collapseCost;
				v.minCost = collapseCost;
				v.totalCost = 0;
				v.costCount = 0;

			}

			v.costCount ++;
			v.totalCost += collapseCost;

			if ( collapseCost < v.minCost ) {

				v.collapseNeighbor = v.neighbors[ i ];
				v.minCost = collapseCost;

			}

		}

		// we average the cost of collapsing at this vertex
		v.collapseCost = v.totalCost / v.costCount;
		// v.collapseCost = v.minCost;

	}

	function removeVertex( v, vertices ) {

		console.assert( v.faces.length === 0 );

		while ( v.neighbors.length ) {

			var n = v.neighbors.pop();
			removeFromArray( n.neighbors, v );

		}

		removeFromArray( vertices, v );

	}

	function removeFace( f, faces ) {

		removeFromArray( faces, f );

		if ( f.v1 ) removeFromArray( f.v1.faces, f );
		if ( f.v2 ) removeFromArray( f.v2.faces, f );
		if ( f.v3 ) removeFromArray( f.v3.faces, f );

		// TODO optimize this!
		var vs = [ this.v1, this.v2, this.v3 ];
		var v1, v2;

		for ( var i = 0; i < 3; i ++ ) {

			v1 = vs[ i ];
			v2 = vs[ ( i + 1 ) % 3 ];

			if ( ! v1 || ! v2 ) continue;

			v1.removeIfNonNeighbor( v2 );
			v2.removeIfNonNeighbor( v1 );

		}

	}

	function collapse( vertices, faces, u, v ) { // u and v are pointers to vertices of an edge

		// Collapse the edge uv by moving vertex u onto v

		if ( ! v ) {

			// u is a vertex all by itself so just delete it..
			removeVertex( u, vertices );
			return;

		}

		var i;
		var tmpVertices = [];

		for ( i = 0; i < u.neighbors.length; i ++ ) {

			tmpVertices.push( u.neighbors[ i ] );

		}

		// delete triangles on edge uv:
		for ( i = u.faces.length - 1; i >= 0; i -- ) {

			if ( u.faces[ i ].hasVertex( v ) ) {

				removeFace( u.faces[ i ], faces );

			}

		}

		// update remaining triangles to have v instead of u
		for ( i = u.faces.length - 1; i >= 0; i -- ) {

			u.faces[ i ].replaceVertex( u, v );

		}

		removeVertex( u, vertices );

		// recompute the edge collapse costs in neighborhood
		for ( i = 0; i < tmpVertices.length; i ++ ) {

			computeEdgeCostAtVertex( tmpVertices[ i ] );

		}

	}

	function minimumCostEdge( vertices ) {

		// O(n * n) approach. TODO optimize this

		var least = vertices[ 0 ];

		for ( var i = 0; i < vertices.length; i ++ ) {

			if ( vertices[ i ].collapseCost < least.collapseCost ) {

				least = vertices[ i ];

			}

		}

		return least;

	}

	// we use a triangle class to represent structure of face slightly differently

	function Triangle( v1, v2, v3, a, b, c ) {

		this.a = a;
		this.b = b;
		this.c = c;

		this.v1 = v1;
		this.v2 = v2;
		this.v3 = v3;

		this.normal = new THREE.Vector3();

		this.computeNormal();

		v1.faces.push( this );
		v1.addUniqueNeighbor( v2 );
		v1.addUniqueNeighbor( v3 );

		v2.faces.push( this );
		v2.addUniqueNeighbor( v1 );
		v2.addUniqueNeighbor( v3 );

		v3.faces.push( this );
		v3.addUniqueNeighbor( v1 );
		v3.addUniqueNeighbor( v2 );

	}

	Triangle.prototype.computeNormal = function () {

		var vA = this.v1.position;
		var vB = this.v2.position;
		var vC = this.v3.position;

		cb.subVectors( vC, vB );
		ab.subVectors( vA, vB );
		cb.cross( ab ).normalize();

		this.normal.copy( cb );

	};

	Triangle.prototype.hasVertex = function ( v ) {

		return v === this.v1 || v === this.v2 || v === this.v3;

	};

	Triangle.prototype.replaceVertex = function ( oldv, newv ) {

		if ( oldv === this.v1 ) this.v1 = newv;
		else if ( oldv === this.v2 ) this.v2 = newv;
		else if ( oldv === this.v3 ) this.v3 = newv;

		removeFromArray( oldv.faces, this );
		newv.faces.push( this );

		oldv.removeIfNonNeighbor( this.v1 );
		this.v1.removeIfNonNeighbor( oldv );

		oldv.removeIfNonNeighbor( this.v2 );
		this.v2.removeIfNonNeighbor( oldv );

		oldv.removeIfNonNeighbor( this.v3 );
		this.v3.removeIfNonNeighbor( oldv );

		this.v1.addUniqueNeighbor( this.v2 );
		this.v1.addUniqueNeighbor( this.v3 );

		this.v2.addUniqueNeighbor( this.v1 );
		this.v2.addUniqueNeighbor( this.v3 );

		this.v3.addUniqueNeighbor( this.v1 );
		this.v3.addUniqueNeighbor( this.v2 );

		this.computeNormal();

	};

	function Vertex( v, id ) {

		this.position = v;

		this.id = id; // old index id

		this.faces = []; // faces vertex is connected
		this.neighbors = []; // neighbouring vertices aka "adjacentVertices"

		// these will be computed in computeEdgeCostAtVertex()
		this.collapseCost = 0; // cost of collapsing this vertex, the less the better. aka objdist
		this.collapseNeighbor = null; // best candinate for collapsing

	}

	Vertex.prototype.addUniqueNeighbor = function ( vertex ) {

		pushIfUnique( this.neighbors, vertex );

	};

	Vertex.prototype.removeIfNonNeighbor = function ( n ) {

		var neighbors = this.neighbors;
		var faces = this.faces;

		var offset = neighbors.indexOf( n );
		if ( offset === - 1 ) return;
		for ( var i = 0; i < faces.length; i ++ ) {

			if ( faces[ i ].hasVertex( n ) ) return;

		}

		neighbors.splice( offset, 1 );

	};

	THREE.SimplifyModifier.prototype.modify = function ( geometry, count ) {

		if ( geometry.isBufferGeometry ) {

			geometry = new THREE.Geometry().fromBufferGeometry( geometry );

		}

		geometry.mergeVertices();

		var oldVertices = geometry.vertices; // Three Position
		var oldFaces = geometry.faces; // Three Face

		// conversion
		var vertices = [];
		var faces = [];

		var i, il, face;

		//
		// put data of original geometry in different data structures
		//

		// add vertices

		for ( i = 0, il = oldVertices.length; i < il; i ++ ) {

			vertices[ i ] = new Vertex( oldVertices[ i ], i );

		}

		// add faces

		for ( i = 0, il = oldFaces.length; i < il; i ++ ) {

			face = oldFaces[ i ];
			faces[ i ] = new Triangle( vertices[ face.a ], vertices[ face.b ], vertices[ face.c ], face.a, face.b, face.c );

		}

		// compute all edge collapse costs

		for ( i = 0, il = vertices.length; i < il; i ++ ) {

			computeEdgeCostAtVertex( vertices[ i ] );

		}

		var nextVertex;

		var z = count;

		while ( z -- ) {

			nextVertex = minimumCostEdge( vertices );

			if ( ! nextVertex ) {

				console.log( 'THREE.SimplifyModifier: No next vertex' );
				break;

			}

			collapse( vertices, faces, nextVertex, nextVertex.collapseNeighbor );

		}

		//

		var simplifiedGeometry = new THREE.BufferGeometry();
		var position = [];
		var index = [];

		//

		for ( i = 0; i < vertices.length; i ++ ) {

			var vertex = vertices[ i ].position;
			position.push( vertex.x, vertex.y, vertex.z );

		}

		//

		for ( i = 0; i < faces.length; i ++ ) {

			var face = faces[ i ];

			var a = vertices.indexOf( face.v1 );
			var b = vertices.indexOf( face.v2 );
			var c = vertices.indexOf( face.v3 );

			index.push( a, b, c );

		}

		//

		simplifiedGeometry.addAttribute( 'position', new THREE.Float32BufferAttribute( position, 3 ) );
		simplifiedGeometry.setIndex( index );

		return simplifiedGeometry;

	};

} )();
<commit_msg>SimplifyModifier: Clean up<commit_after>/*
 *	@author zz85 / http://twitter.com/blurspline / http://www.lab4games.net/zz85/blog
 *
 *	Simplification Geometry Modifier
 *    - based on code and technique
 *	  - by Stan Melax in 1998
 *	  - Progressive Mesh type Polygon Reduction Algorithm
 *    - http://www.melax.com/polychop/
 */

THREE.SimplifyModifier = function () {};

( function () {

	var cb = new THREE.Vector3(), ab = new THREE.Vector3();

	function pushIfUnique( array, object ) {

		if ( array.indexOf( object ) === - 1 ) array.push( object );

	}

	function removeFromArray( array, object ) {

		var k = array.indexOf( object );
		if ( k > - 1 ) array.splice( k, 1 );

	}

	function computeEdgeCollapseCost( u, v ) {

		// if we collapse edge uv by moving u to v then how
		// much different will the model change, i.e. the "error".

		var edgelength = v.position.distanceTo( u.position );
		var curvature = 0;

		var sideFaces = [];
		var i, il = u.faces.length, face, sideFace;

		// find the "sides" triangles that are on the edge uv
		for ( i = 0; i < il; i ++ ) {

			face = u.faces[ i ];

			if ( face.hasVertex( v ) ) {

				sideFaces.push( face );

			}

		}

		// use the triangle facing most away from the sides
		// to determine our curvature term
		for ( i = 0; i < il; i ++ ) {

			var minCurvature = 1;
			face = u.faces[ i ];

			for ( var j = 0; j < sideFaces.length; j ++ ) {

				sideFace = sideFaces[ j ];
				// use dot product of face normals.
				var dotProd = face.normal.dot( sideFace.normal );
				minCurvature = Math.min( minCurvature, ( 1.001 - dotProd ) / 2 );

			}

			curvature = Math.max( curvature, minCurvature );

		}

		// crude approach in attempt to preserve borders
		// though it seems not to be totally correct
		var borders = 0;
		if ( sideFaces.length < 2 ) {

			// we add some arbitrary cost for borders,
			// borders += 10;
			curvature = 1;

		}

		var amt = edgelength * curvature + borders;

		return amt;

	}

	function computeEdgeCostAtVertex( v ) {

		// compute the edge collapse cost for all edges that start
		// from vertex v.  Since we are only interested in reducing
		// the object by selecting the min cost edge at each step, we
		// only cache the cost of the least cost edge at this vertex
		// (in member variable collapse) as well as the value of the
		// cost (in member variable collapseCost).

		if ( v.neighbors.length === 0 ) {

			// collapse if no neighbors.
			v.collapseNeighbor = null;
			v.collapseCost = - 0.01;

			return;

		}

		v.collapseCost = 100000;
		v.collapseNeighbor = null;

		// search all neighboring edges for "least cost" edge
		for ( var i = 0; i < v.neighbors.length; i ++ ) {

			var collapseCost = computeEdgeCollapseCost( v, v.neighbors[ i ] );

			if ( ! v.collapseNeighbor ) {

				v.collapseNeighbor = v.neighbors[ i ];
				v.collapseCost = collapseCost;
				v.minCost = collapseCost;
				v.totalCost = 0;
				v.costCount = 0;

			}

			v.costCount ++;
			v.totalCost += collapseCost;

			if ( collapseCost < v.minCost ) {

				v.collapseNeighbor = v.neighbors[ i ];
				v.minCost = collapseCost;

			}

		}

		// we average the cost of collapsing at this vertex
		v.collapseCost = v.totalCost / v.costCount;
		// v.collapseCost = v.minCost;

	}

	function removeVertex( v, vertices ) {

		console.assert( v.faces.length === 0 );

		while ( v.neighbors.length ) {

			var n = v.neighbors.pop();
			removeFromArray( n.neighbors, v );

		}

		removeFromArray( vertices, v );

	}

	function removeFace( f, faces ) {

		removeFromArray( faces, f );

		if ( f.v1 ) removeFromArray( f.v1.faces, f );
		if ( f.v2 ) removeFromArray( f.v2.faces, f );
		if ( f.v3 ) removeFromArray( f.v3.faces, f );

		// TODO optimize this!
		var vs = [ this.v1, this.v2, this.v3 ];
		var v1, v2;

		for ( var i = 0; i < 3; i ++ ) {

			v1 = vs[ i ];
			v2 = vs[ ( i + 1 ) % 3 ];

			if ( ! v1 || ! v2 ) continue;

			v1.removeIfNonNeighbor( v2 );
			v2.removeIfNonNeighbor( v1 );

		}

	}

	function collapse( vertices, faces, u, v ) { // u and v are pointers to vertices of an edge

		// Collapse the edge uv by moving vertex u onto v

		if ( ! v ) {

			// u is a vertex all by itself so just delete it..
			removeVertex( u, vertices );
			return;

		}

		var i;
		var tmpVertices = [];

		for ( i = 0; i < u.neighbors.length; i ++ ) {

			tmpVertices.push( u.neighbors[ i ] );

		}

		// delete triangles on edge uv:
		for ( i = u.faces.length - 1; i >= 0; i -- ) {

			if ( u.faces[ i ].hasVertex( v ) ) {

				removeFace( u.faces[ i ], faces );

			}

		}

		// update remaining triangles to have v instead of u
		for ( i = u.faces.length - 1; i >= 0; i -- ) {

			u.faces[ i ].replaceVertex( u, v );

		}

		removeVertex( u, vertices );

		// recompute the edge collapse costs in neighborhood
		for ( i = 0; i < tmpVertices.length; i ++ ) {

			computeEdgeCostAtVertex( tmpVertices[ i ] );

		}

	}

	function minimumCostEdge( vertices ) {

		// O(n * n) approach. TODO optimize this

		var least = vertices[ 0 ];

		for ( var i = 0; i < vertices.length; i ++ ) {

			if ( vertices[ i ].collapseCost < least.collapseCost ) {

				least = vertices[ i ];

			}

		}

		return least;

	}

	// we use a triangle class to represent structure of face slightly differently

	function Triangle( v1, v2, v3, a, b, c ) {

		this.a = a;
		this.b = b;
		this.c = c;

		this.v1 = v1;
		this.v2 = v2;
		this.v3 = v3;

		this.normal = new THREE.Vector3();

		this.computeNormal();

		v1.faces.push( this );
		v1.addUniqueNeighbor( v2 );
		v1.addUniqueNeighbor( v3 );

		v2.faces.push( this );
		v2.addUniqueNeighbor( v1 );
		v2.addUniqueNeighbor( v3 );

		v3.faces.push( this );
		v3.addUniqueNeighbor( v1 );
		v3.addUniqueNeighbor( v2 );

	}

	Triangle.prototype.computeNormal = function () {

		var vA = this.v1.position;
		var vB = this.v2.position;
		var vC = this.v3.position;

		cb.subVectors( vC, vB );
		ab.subVectors( vA, vB );
		cb.cross( ab ).normalize();

		this.normal.copy( cb );

	};

	Triangle.prototype.hasVertex = function ( v ) {

		return v === this.v1 || v === this.v2 || v === this.v3;

	};

	Triangle.prototype.replaceVertex = function ( oldv, newv ) {

		if ( oldv === this.v1 ) this.v1 = newv;
		else if ( oldv === this.v2 ) this.v2 = newv;
		else if ( oldv === this.v3 ) this.v3 = newv;

		removeFromArray( oldv.faces, this );
		newv.faces.push( this );

		oldv.removeIfNonNeighbor( this.v1 );
		this.v1.removeIfNonNeighbor( oldv );

		oldv.removeIfNonNeighbor( this.v2 );
		this.v2.removeIfNonNeighbor( oldv );

		oldv.removeIfNonNeighbor( this.v3 );
		this.v3.removeIfNonNeighbor( oldv );

		this.v1.addUniqueNeighbor( this.v2 );
		this.v1.addUniqueNeighbor( this.v3 );

		this.v2.addUniqueNeighbor( this.v1 );
		this.v2.addUniqueNeighbor( this.v3 );

		this.v3.addUniqueNeighbor( this.v1 );
		this.v3.addUniqueNeighbor( this.v2 );

		this.computeNormal();

	};

	function Vertex( v, id ) {

		this.position = v;

		this.id = id; // old index id

		this.faces = []; // faces vertex is connected
		this.neighbors = []; // neighbouring vertices aka "adjacentVertices"

		// these will be computed in computeEdgeCostAtVertex()
		this.collapseCost = 0; // cost of collapsing this vertex, the less the better. aka objdist
		this.collapseNeighbor = null; // best candinate for collapsing

	}

	Vertex.prototype.addUniqueNeighbor = function ( vertex ) {

		pushIfUnique( this.neighbors, vertex );

	};

	Vertex.prototype.removeIfNonNeighbor = function ( n ) {

		var neighbors = this.neighbors;
		var faces = this.faces;

		var offset = neighbors.indexOf( n );
		if ( offset === - 1 ) return;
		for ( var i = 0; i < faces.length; i ++ ) {

			if ( faces[ i ].hasVertex( n ) ) return;

		}

		neighbors.splice( offset, 1 );

	};

	THREE.SimplifyModifier.prototype.modify = function ( geometry, count ) {

		if ( geometry.isBufferGeometry ) {

			geometry = new THREE.Geometry().fromBufferGeometry( geometry );

		}

		geometry.mergeVertices();

		var oldVertices = geometry.vertices; // Three Position
		var oldFaces = geometry.faces; // Three Face

		// conversion
		var vertices = [];
		var faces = [];

		var i, il;

		//
		// put data of original geometry in different data structures
		//

		// add vertices

		for ( i = 0, il = oldVertices.length; i < il; i ++ ) {

			var vertex = new Vertex( oldVertices[ i ], i );
			vertices.push( vertex );

		}

		// add faces

		for ( i = 0, il = oldFaces.length; i < il; i ++ ) {

			var face = oldFaces[ i ];

			var a = face.a;
			var b = face.b;
			var c = face.c;

			var triangle = new Triangle( vertices[ a ], vertices[ b ], vertices[ c ], a, b, c );
			faces.push( triangle );

		}

		// compute all edge collapse costs

		for ( i = 0, il = vertices.length; i < il; i ++ ) {

			computeEdgeCostAtVertex( vertices[ i ] );

		}

		var nextVertex;

		var z = count;

		while ( z -- ) {

			nextVertex = minimumCostEdge( vertices );

			if ( ! nextVertex ) {

				console.log( 'THREE.SimplifyModifier: No next vertex' );
				break;

			}

			collapse( vertices, faces, nextVertex, nextVertex.collapseNeighbor );

		}

		//

		var simplifiedGeometry = new THREE.BufferGeometry();
		var position = [];
		var index = [];

		//

		for ( i = 0; i < vertices.length; i ++ ) {

			var vertex = vertices[ i ].position;
			position.push( vertex.x, vertex.y, vertex.z );

		}

		//

		for ( i = 0; i < faces.length; i ++ ) {

			var face = faces[ i ];

			var a = vertices.indexOf( face.v1 );
			var b = vertices.indexOf( face.v2 );
			var c = vertices.indexOf( face.v3 );

			index.push( a, b, c );

		}

		//

		simplifiedGeometry.addAttribute( 'position', new THREE.Float32BufferAttribute( position, 3 ) );
		simplifiedGeometry.setIndex( index );

		return simplifiedGeometry;

	};

} )();
<|endoftext|>