<commit_before>define(function (require, exports, module) {

    'use strict';

    var _        = require('underscore');
    var Backbone = require('backbone');

    var Rectangle        = require('common/math/rectangle');
    var Vector2          = require('common/math/vector2');
    var LineIntersection = require('common/math/line-intersection');
    var clamp            = require('common/math/clamp');

    var BendingLightSimulation = require('models/simulation');
    var Medium                 = require('models/medium');
    var LightRay               = require('models/light-ray');
    var Ray                    = require('models/ray');
    var Prism                  = require('models/prism');
    var Polygon                = require('models/shape/polygon');
    var Circle                 = require('models/shape/circle');
    var ShapeIntersection      = require('models/shape/shape-intersection');
    var ShapeDifference        = require('models/shape/shape-difference');

    /**
     * Constants
     */
    var Constants = require('constants');
    var MediumPropertiesPresets = require('medium-properties-presets');
    var WAVELENGTH_RED = Constants.WAVELENGTH_RED;

    /**
     * Wraps the update function in
     */
    var PrismBreakSimulation = BendingLightSimulation.extend({

        defaults: _.extend(BendingLightSimulation.prototype.defaults, {
            manyRays: false,       // Show multiple beams to help show how lenses work
            showReflections: false // If false, will hide non TIR reflections
        }),

        initialize: function(attributes, options) {
            options = _.extend({
                laserDistanceFromPivot: Constants.DEFAULT_LASER_DISTANCE_FROM_PIVOT * 0.9,
                laserAngle: Math.PI,
                topLeftQuadrant: false
            }, options);

            this.initPrismPrototypes();

            BendingLightSimulation.prototype.initialize.apply(this, [attributes, options]);
        },

        initPrismPrototypes: function() {
            var prismPrototypes = [];

            var a = Constants.CHARACTERISTIC_LENGTH * 10; // characteristic length scale
            var b = a / 4; // characteristic length scale
            var h = a * Math.sqrt(3) / 2;

            // Square
            var square = new Prism({}, {
                referencePointIndex: 3, // Attach at bottom right
                points: [
                    new Vector2(),
                    new Vector2(0, a),
                    new Vector2(a, a),
                    new Vector2(a, 0)
                ]
            });
            square.shape.centerOnCentroid();
            prismPrototypes.push(square);

            // Triangle
            var triangle = new Prism({}, {
                referencePointIndex: 1, // Attach at bottom right
                points: [
                    new Vector2(0,     0),
                    new Vector2(a,     0),
                    new Vector2(a / 2, h)
                ]
            });
            triangle.shape.centerOnCentroid();
            prismPrototypes.push(triangle);

            // Trapezoid
            var trapezoid = new Prism({}, {
                referencePointIndex: 1, // Attach at bottom right
                points: [
                    new Vector2(),
                    new Vector2(a, 0),
                    new Vector2(a / 2 + b, h),
                    new Vector2(a / 2 - b, h)
                ]
            });
            trapezoid.shape.centerOnCentroid();
            prismPrototypes.push(trapezoid);

            var radius = a / 2;

            // Continuous Circle
            prismPrototypes.push(new Prism({}, {
                shape: new Circle(radius)
            }));

            // Continuous Semicircle
            var semicircle = new Prism({}, {
                shape: new ShapeIntersection(
                    new Circle(radius),
                    new Polygon([
                        new Vector2(0,        radius),
                        new Vector2(0,       -radius),
                        new Vector2(-radius, -radius),
                        new Vector2(-radius,  radius)
                    ], 1 /* Attach at bottom right */)
                )
            });
            // The only problem with calculating these centroids from the polygons
            //   is that, because the circle approximations have a lot of points,
            //   the circles end up biasing the centroid calculation towards the
            //   circle edges.
            semicircle.shape.centerOnCentroid();
            prismPrototypes.push(semicircle);

            // Continuous Diverging Lens
            var divergingLens = new Prism({}, {
                shape: new ShapeDifference(
                    new Polygon([
                        new Vector2(0,                    -radius),
                        new Vector2(radius * (0.6 / 0.5), -radius),
                        new Vector2(radius * (0.6 / 0.5),  radius),
                        new Vector2(0,                     radius)
                    ], 1 /* Attach at bottom right */),
                    new Circle(radius)
                )
            });
            divergingLens.shape.centerOnCentroid();
            prismPrototypes.push(divergingLens);

            this.prismPrototypes = prismPrototypes;
        },

        /**
         * Initializes the models used in the simulation
         */
        initComponents: function() {
            BendingLightSimulation.prototype.initComponents.apply(this, arguments);

            this.intersections = [];
            this.prisms = new Backbone.Collection();

            this.environment = new Medium({
                shape: new Rectangle(-1, 0, 2, 1), // In Meters, very large compared to visible model region in the stage
                mediumProperties: MediumPropertiesPresets.AIR
            });

            this.prismMedium = new Medium({
                shape: new Rectangle(-1, -1, 2, 1), // In Meters, very large compared to visible model region in the stage
                mediumProperties: MediumPropertiesPresets.GLASS
            });

            this._from       = new Vector2();
            this._offset     = new Vector2();
            this._point      = new Vector2();
            this._direction  = new Vector2();
            this._scratchVec = new Vector2();
            this._scratchL   = new Vector2();
            this._scratchN   = new Vector2();
            this._scratchP   = new Vector2();
            this._scratchU   = new Vector2();
            this._vReflect   = new Vector2();
            this._vRefract   = new Vector2();

            this._intersectionCompare = this._intersectionCompare.bind(this);

            this.listenTo(this.environment, 'change',           this.mediumChanged);
            this.listenTo(this.prisms,      'change',           this.prismChanged);
            this.listenTo(this.prisms,      'add remove reset', this.prismChanged);
        },

        createPrismFromPrototype: function(prototypeIndex) {
            return this.prismPrototypes[prototypeIndex].clone();
        },

        addPrism: function(prism) {
            this.prisms.add(prism);
        },

        removePrism: function() {
            this.prisms.remove(prism);
        },

        /**
         * Clear the model in preparation for another ray propagation update phase
         */
        clear: function() {
            BendingLightSimulation.prototype.clear.apply(this, arguments);

            for (var i = this.intersections.length - 1; i >= 0; i--) {
                this.intersections[i].destroy();
                this.intersections.splice(i, 1);
            }
        },

        /**
         * Algorithm that computes the trajectories of the rays throughout the system
         */
        propagateRays: function() {
            if (this.laser.get('on')) {
                var tail = this.laser.get('emissionPoint');
                var laserInPrism = this.isLaserInPrism();
                var directionUnitVector = this.laser.getDirectionUnitVector();
                if (!this.get('manyRays')) {
                    // Just one main, central ray
                    this.propagateFrom(tail, directionUnitVector, 1.0, laserInPrism);
                }
                else {
                    // Many parallel rays
                    var offset = this._offset;
                    for (var x = -WAVELENGTH_RED; x <= WAVELENGTH_RED * 1.1; x += WAVELENGTH_RED / 2) {
                        var offset = this._offset
                            .set(directionUnitVector)
                            .rotate(Math.PI / 2)
                            .scale(x);

                        this.propagateFrom(offset.add(tail), directionUnitVector, 1.0, laserInPrism);
                    }
                }
            }
        },

        /**
         * Starts the ray propagation from a new starting location
         */
        propagateFrom: function(tail, directionUnitVector, power, laserInPrism) {
            if (this.laser.getWavelength() === Constants.WHITE_LIGHT) {
                var min = Constants.MIN_WAVELENGTH / Constants.METERS_TO_NANOMETERS;
                var max = Constants.MAX_WAVELENGTH / Constants.METERS_TO_NANOMETERS;
                // This number sets the number of (equally spaced wavelength) rays to
                //   show in a white beam.  More rays looks better but is more
                //   computationally intensive.
                var dw = (max - min) / 16;
                for (var wavelength = min; wavelength <= max; wavelength += dw) {
                    var mediumIndexOfRefraction = laserInPrism ?
                        this.prismMedium.getIndexOfRefraction(wavelength) :
                        this.environment.getIndexOfRefraction(wavelength);

                    var ray = Ray.create(
                        tail,
                        directionUnitVector,
                        power,
                        wavelength,
                        mediumIndexOfRefraction,
                        Constants.SPEED_OF_LIGHT / wavelength
                    );

                    this.propagateRay(ray, 0);

                    ray.destroy();
                }
            }
            else {
                var mediumIndexOfRefraction = laserInPrism ?
                    this.prismMedium.getIndexOfRefraction(this.laser.getWavelength()) :
                    this.environment.getIndexOfRefraction(this.laser.getWavelength());

                var ray = Ray.create(
                    tail,
                    directionUnitVector,
                    power,
                    this.laser.getWavelength(),
                    mediumIndexOfRefraction,
                    this.laser.getFrequency()
                );

                this.propagateRay(ray, 0);

                ray.destroy();
            }
        },

        /**
         * Recursive algorithm to compute the pattern of rays in the system.  This is
         *   the main computation of this model, rays are cleared beforehand and this
         *   algorithm adds them as it goes
         */
        propagateRay: function(incidentRay, count) {
            var waveWidth = Constants.CHARACTERISTIC_LENGTH * 5;

            // Termination condition of we have reached too many iterations or if the
            //   ray is very weak
            if (count > 50 || incidentRay.power < 0.001)
                return;

            // Check for an intersection
            var intersection = this.getIntersection(incidentRay, this.prisms);
            var L = incidentRay.directionUnitVector;
            var n1 = incidentRay.mediumIndexOfRefraction;
            var wavelengthInN1 = incidentRay.wavelength / n1;
            if (intersection) {
                // There was an intersection, so reflect and refract the light

                // List the intersection in the model
                this.addIntersection(intersection);

                var pointOnOtherSide = this._point
                    .set(intersection.getPoint())
                    .add(this._direction
                        .set(incidentRay.directionUnitVector)
                        .scale(1E-12)
                    );

                var outputInsidePrism = false;
                for (var i = 0; i < this.prisms.length; i++) {
                    if (this.prisms.at(i).contains(pointOnOtherSide)) {
                        outputInsidePrism = true;
                        break;
                    }
                }

                // Index of refraction of the other medium
                var n2 = outputInsidePrism ?
                    this.prismMedium.getIndexOfRefraction(incidentRay.getBaseWavelength()) :
                    this.environment.getIndexOfRefraction(incidentRay.getBaseWavelength());

                // Precompute for readability
                var point = intersection.getPoint();
                var n = intersection.getUnitNormal();
                var scratchL = this._scratchL;
                var scratchN = this._scratchN;
                var scratchP = this._scratchP;
                var scratchU = this._scratchU;

                // Clean up; we don't need this anymore
                intersection.destroy();

                // Compute the output rays; see http://en.wikipedia.org/wiki/Snell's_law#Vector_form
                var cosTheta1 = n.dot(scratchL.set(L).scale(-1));
                var cosTheta2Radicand = 1 - Math.pow(n1 / n2, 2) * (1 - Math.pow(cosTheta1, 2));
                var cosTheta2 = Math.sqrt(cosTheta2Radicand);
                var shouldTotalInternalReflection = cosTheta2Radicand < 0;

                var vReflect = this._vReflect.set(L).add(scratchN.set(n).scale(2 * cosTheta1));
                var vRefract = cosTheta1 > 0 ?
                    this._vRefract.set(L).scale(n1 / n2).add(scratchN.set(n).scale(n1 / n2 * cosTheta1 - cosTheta2)) :
                    this._vRefract.set(L).scale(n1 / n2).add(scratchN.set(n).scale(n1 / n2 * cosTheta1 + cosTheta2));

                var reflectedPower   = shouldTotalInternalReflection ? 1 : clamp(0, this.getReflectedPower(  n1, n2, cosTheta1, cosTheta2), 1);
                var transmittedPower = shouldTotalInternalReflection ? 0 : clamp(0, this.getTransmittedPower(n1, n2, cosTheta1, cosTheta2), 1);

                // Create the new rays and propagate them recursively
                var reflected = Ray.create(scratchP.set(point).add(scratchU.set(incidentRay.directionUnitVector).scale(-1E-12)), vReflect, incidentRay.power * reflectedPower,   incidentRay.wavelength, incidentRay.mediumIndexOfRefraction, incidentRay.frequency);
                var refracted = Ray.create(scratchP.set(point).add(scratchU.set(incidentRay.directionUnitVector).scale(+1E-12)), vRefract, incidentRay.power * transmittedPower, incidentRay.wavelength, n2,                                  incidentRay.frequency);

                if (this.get('showReflections') || shouldTotalInternalReflection)
                    this.propagateRay(reflected, count + 1);

                this.propagateRay(refracted, count + 1);

                // Add the incident ray itself
                this.addRay(LightRay.create(
                    incidentRay.tail,
                    intersection.getPoint(),
                    n1,
                    wavelengthInN1,
                    incidentRay.power,
                    incidentRay.wavelength,
                    waveWidth,
                    0,
                    null,
                    true,
                    false
                ));

                // Clean up; we don't need this anymore
                reflected.destroy();
                refracted.destroy();
            }
            else {
                // No intersection, so the light ray should just keep going
                this.addRay(LightRay.create(
                    incidentRay.tail,
                    this._scratchVec.set(incidentRay.tail).add(incidentRay.directionUnitVector), // 1 meter long ray (long enough to seem like infinity for the sim which is at nm scale)
                    n1,
                    wavelengthInN1,
                    incidentRay.power,
                    incidentRay.wavelength,
                    waveWidth,
                    0,
                    null,
                    true,
                    false
                ));
            }
        },

        /**
         * Find the nearest intersection between a light ray and the set of prisms
         *   in the play area and return it or return null.
         */
        getIntersection: function(incidentRay, prisms) {
            // Get all the intersections
            var allIntersections = [];
            for (var i = 0; i < this.prisms.length; i++){
                var intersections = this.prisms.at(i).getIntersections(incidentRay);
                for (var j = 0; j < intersections.length; j++)
                    allIntersections.push(intersections[j]);
            }

            if (allIntersections.length) {
                // Get the closest one (which would be hit first)
                this._incidentRay = incidentRay;
                allIntersections.sort(this._intersectionCompare);

                var keeper = allIntersections[0];
                this.destroyIntersections(allIntersections, keeper);

                return keeper;
            }
            else {
                return null;
            }
        },

        _intersectionCompare: function(i1, i2) {
            return i1.getPoint().distance(this._incidentRay.tail) - i2.getPoint().distance(this._incidentRay.tail);
        },

        destroyIntersections: function(intersections, except) {
            for (var i = 0; i < intersections.length; i++) {
                if (intersections[i] !== except)
                    intersections[i].destroy();
            }
        },

        /**
         * Signify that another ray/interface collision occurred
         */
        addIntersection: function(intersection) {
            this.intersections.push(intersection);
        },

        isLaserInPrism: function() {
            for (var i = 0; i < this.prisms.length; i++) {
                if (this.prisms.at(i).contains(this.laser.get('emissionPoint')))
                    return true;
            }
            return false;
        },

        /**
         * Responds to changes in mediums by telling the simulation to update
         */
        mediumChanged: function() {
            this.updateOnNextFrame();
        },

        prismChanged: function() {
            this.updateOnNextFrame();
        }

    });

    return PrismBreakSimulation;
});
<commit_msg>Switching workstations. What I have observed is this:<commit_after>define(function (require, exports, module) {

    'use strict';

    var _        = require('underscore');
    var Backbone = require('backbone');

    var Rectangle        = require('common/math/rectangle');
    var Vector2          = require('common/math/vector2');
    var LineIntersection = require('common/math/line-intersection');
    var clamp            = require('common/math/clamp');

    var BendingLightSimulation = require('models/simulation');
    var Medium                 = require('models/medium');
    var LightRay               = require('models/light-ray');
    var Ray                    = require('models/ray');
    var Prism                  = require('models/prism');
    var Polygon                = require('models/shape/polygon');
    var Circle                 = require('models/shape/circle');
    var ShapeIntersection      = require('models/shape/shape-intersection');
    var ShapeDifference        = require('models/shape/shape-difference');

    /**
     * Constants
     */
    var Constants = require('constants');
    var MediumPropertiesPresets = require('medium-properties-presets');
    var WAVELENGTH_RED = Constants.WAVELENGTH_RED;

    /**
     * Wraps the update function in
     */
    var PrismBreakSimulation = BendingLightSimulation.extend({

        defaults: _.extend(BendingLightSimulation.prototype.defaults, {
            manyRays: false,       // Show multiple beams to help show how lenses work
            showReflections: false // If false, will hide non TIR reflections
        }),

        initialize: function(attributes, options) {
            options = _.extend({
                laserDistanceFromPivot: Constants.DEFAULT_LASER_DISTANCE_FROM_PIVOT * 0.9,
                laserAngle: Math.PI,
                topLeftQuadrant: false
            }, options);

            this.initPrismPrototypes();

            BendingLightSimulation.prototype.initialize.apply(this, [attributes, options]);
        },

        initPrismPrototypes: function() {
            var prismPrototypes = [];

            var a = Constants.CHARACTERISTIC_LENGTH * 10; // characteristic length scale
            var b = a / 4; // characteristic length scale
            var h = a * Math.sqrt(3) / 2;

            // Square
            var square = new Prism({}, {
                referencePointIndex: 3, // Attach at bottom right
                points: [
                    new Vector2(),
                    new Vector2(0, a),
                    new Vector2(a, a),
                    new Vector2(a, 0)
                ]
            });
            square.shape.centerOnCentroid();
            prismPrototypes.push(square);

            // Triangle
            var triangle = new Prism({}, {
                referencePointIndex: 1, // Attach at bottom right
                points: [
                    new Vector2(0,     0),
                    new Vector2(a,     0),
                    new Vector2(a / 2, h)
                ]
            });
            triangle.shape.centerOnCentroid();
            prismPrototypes.push(triangle);

            // Trapezoid
            var trapezoid = new Prism({}, {
                referencePointIndex: 1, // Attach at bottom right
                points: [
                    new Vector2(),
                    new Vector2(a, 0),
                    new Vector2(a / 2 + b, h),
                    new Vector2(a / 2 - b, h)
                ]
            });
            trapezoid.shape.centerOnCentroid();
            prismPrototypes.push(trapezoid);

            var radius = a / 2;

            // Continuous Circle
            prismPrototypes.push(new Prism({}, {
                shape: new Circle(radius)
            }));

            // Continuous Semicircle
            var semicircle = new Prism({}, {
                shape: new ShapeIntersection(
                    new Circle(radius),
                    new Polygon([
                        new Vector2(0,        radius),
                        new Vector2(0,       -radius),
                        new Vector2(-radius, -radius),
                        new Vector2(-radius,  radius)
                    ], 1 /* Attach at bottom right */)
                )
            });
            // The only problem with calculating these centroids from the polygons
            //   is that, because the circle approximations have a lot of points,
            //   the circles end up biasing the centroid calculation towards the
            //   circle edges.
            semicircle.shape.centerOnCentroid();
            prismPrototypes.push(semicircle);

            // Continuous Diverging Lens
            var divergingLens = new Prism({}, {
                shape: new ShapeDifference(
                    new Polygon([
                        new Vector2(0,                    -radius),
                        new Vector2(radius * (0.6 / 0.5), -radius),
                        new Vector2(radius * (0.6 / 0.5),  radius),
                        new Vector2(0,                     radius)
                    ], 1 /* Attach at bottom right */),
                    new Circle(radius)
                )
            });
            divergingLens.shape.centerOnCentroid();
            prismPrototypes.push(divergingLens);

            this.prismPrototypes = prismPrototypes;
        },

        /**
         * Initializes the models used in the simulation
         */
        initComponents: function() {
            BendingLightSimulation.prototype.initComponents.apply(this, arguments);

            this.intersections = [];
            this.prisms = new Backbone.Collection();

            this.environment = new Medium({
                shape: new Rectangle(-1, 0, 2, 1), // In Meters, very large compared to visible model region in the stage
                mediumProperties: MediumPropertiesPresets.AIR
            });

            this.prismMedium = new Medium({
                shape: new Rectangle(-1, -1, 2, 1), // In Meters, very large compared to visible model region in the stage
                mediumProperties: MediumPropertiesPresets.GLASS
            });

            this._from       = new Vector2();
            this._offset     = new Vector2();
            this._point      = new Vector2();
            this._dirUnit    = new Vector2();
            this._direction  = new Vector2();
            this._scratchVec = new Vector2();
            this._scratchL   = new Vector2();
            this._scratchN   = new Vector2();
            this._scratchP   = new Vector2();
            this._scratchU   = new Vector2();
            this._vReflect   = new Vector2();
            this._vRefract   = new Vector2();

            this._intersectionCompare = this._intersectionCompare.bind(this);

            this.listenTo(this.environment, 'change',           this.mediumChanged);
            this.listenTo(this.prisms,      'change',           this.prismChanged);
            this.listenTo(this.prisms,      'add remove reset', this.prismChanged);
        },

        createPrismFromPrototype: function(prototypeIndex) {
            return this.prismPrototypes[prototypeIndex].clone();
        },

        addPrism: function(prism) {
            this.prisms.add(prism);
        },

        removePrism: function() {
            this.prisms.remove(prism);
        },

        /**
         * Clear the model in preparation for another ray propagation update phase
         */
        clear: function() {
            BendingLightSimulation.prototype.clear.apply(this, arguments);

            for (var i = this.intersections.length - 1; i >= 0; i--) {
                this.intersections[i].destroy();
                this.intersections.splice(i, 1);
            }
        },

        /**
         * Algorithm that computes the trajectories of the rays throughout the system
         */
        propagateRays: function() {
            if (this.laser.get('on')) {console.log('-----------------')
                var tail = this.laser.get('emissionPoint');
                var laserInPrism = this.isLaserInPrism();
                var directionUnitVector = this._dirUnit.set(this.laser.getDirectionUnitVector());
                if (!this.get('manyRays')) {
                    // Just one main, central ray
                    this.propagateFrom(tail, directionUnitVector, 1.0, laserInPrism);
                }
                else {
                    // Many parallel rays
                    var offset = this._offset;
                    for (var x = -WAVELENGTH_RED; x <= WAVELENGTH_RED * 1.1; x += WAVELENGTH_RED / 2) {
                        var offset = this._offset
                            .set(directionUnitVector)
                            .rotate(Math.PI / 2)
                            .scale(x);

                        this.propagateFrom(offset.add(tail), directionUnitVector, 1.0, laserInPrism);
                    }
                }
            }
        },

        /**
         * Starts the ray propagation from a new starting location
         */
        propagateFrom: function(tail, directionUnitVector, power, laserInPrism) {
            if (this.laser.getWavelength() === Constants.WHITE_LIGHT) {
                var min = Constants.MIN_WAVELENGTH / Constants.METERS_TO_NANOMETERS;
                var max = Constants.MAX_WAVELENGTH / Constants.METERS_TO_NANOMETERS;
                // This number sets the number of (equally spaced wavelength) rays to
                //   show in a white beam.  More rays looks better but is more
                //   computationally intensive.
                var dw = (max - min) / 16;
                for (var wavelength = min; wavelength <= max; wavelength += dw) {
                    var mediumIndexOfRefraction = laserInPrism ?
                        this.prismMedium.getIndexOfRefraction(wavelength) :
                        this.environment.getIndexOfRefraction(wavelength);

                    var ray = Ray.create(
                        tail,
                        directionUnitVector,
                        power,
                        wavelength,
                        mediumIndexOfRefraction,
                        Constants.SPEED_OF_LIGHT / wavelength
                    );

                    this.propagateRay(ray, 0);

                    ray.destroy();
                }
            }
            else {
                var mediumIndexOfRefraction = laserInPrism ?
                    this.prismMedium.getIndexOfRefraction(this.laser.getWavelength()) :
                    this.environment.getIndexOfRefraction(this.laser.getWavelength());

                var ray = Ray.create(
                    tail,
                    directionUnitVector,
                    power,
                    this.laser.getWavelength(),
                    mediumIndexOfRefraction,
                    this.laser.getFrequency()
                );

                this.propagateRay(ray, 0);

                ray.destroy();
            }
        },

        /**
         * Recursive algorithm to compute the pattern of rays in the system.  This is
         *   the main computation of this model, rays are cleared beforehand and this
         *   algorithm adds them as it goes
         */
        propagateRay: function(incidentRay, count) {
            var waveWidth = Constants.CHARACTERISTIC_LENGTH * 5;

            // Termination condition of we have reached too many iterations or if the
            //   ray is very weak
            if (count > 50 || incidentRay.power < 0.001)
                return;

            // Check for an intersection
            var intersection = this.getIntersection(incidentRay, this.prisms);
            var L = incidentRay.directionUnitVector;
            var n1 = incidentRay.mediumIndexOfRefraction;
            var wavelengthInN1 = incidentRay.wavelength / n1;
            if (intersection) {
                // There was an intersection, so reflect and refract the light

                // List the intersection in the model
                this.addIntersection(intersection);

                var pointOnOtherSide = this._point
                    .set(intersection.getPoint())
                    .add(this._direction
                        .set(incidentRay.directionUnitVector)
                        .scale(1E-12)
                    );

                var outputInsidePrism = false;
                for (var i = 0; i < this.prisms.length; i++) {
                    if (this.prisms.at(i).contains(pointOnOtherSide)) {
                        outputInsidePrism = true;
                        break;
                    }
                }

                // Index of refraction of the other medium
                var n2 = outputInsidePrism ?
                    this.prismMedium.getIndexOfRefraction(incidentRay.getBaseWavelength()) :
                    this.environment.getIndexOfRefraction(incidentRay.getBaseWavelength());

                // Precompute for readability
                var point = intersection.getPoint();
                var n = intersection.getUnitNormal();
                var scratchL = this._scratchL;
                var scratchN = this._scratchN;
                var scratchP = this._scratchP;
                var scratchU = this._scratchU;

                // Clean up; we don't need this anymore
                intersection.destroy();

                // Compute the output rays; see http://en.wikipedia.org/wiki/Snell's_law#Vector_form
                var cosTheta1 = n.dot(scratchL.set(L).scale(-1));
                var cosTheta2Radicand = 1 - Math.pow(n1 / n2, 2) * (1 - Math.pow(cosTheta1, 2));
                var cosTheta2 = Math.sqrt(cosTheta2Radicand);
                var shouldTotalInternalReflection = cosTheta2Radicand < 0;

                var vReflect = this._vReflect.set(L).add(scratchN.set(n).scale(2 * cosTheta1));
                var vRefract = cosTheta1 > 0 ?
                    this._vRefract.set(L).scale(n1 / n2).add(scratchN.set(n).scale(n1 / n2 * cosTheta1 - cosTheta2)) :
                    this._vRefract.set(L).scale(n1 / n2).add(scratchN.set(n).scale(n1 / n2 * cosTheta1 + cosTheta2));

                var reflectedPower   = shouldTotalInternalReflection ? 1 : clamp(0, this.getReflectedPower(  n1, n2, cosTheta1, cosTheta2), 1);
                var transmittedPower = shouldTotalInternalReflection ? 0 : clamp(0, this.getTransmittedPower(n1, n2, cosTheta1, cosTheta2), 1);

                // Create the new rays and propagate them recursively
                var reflected = Ray.create(scratchP.set(point).add(scratchU.set(incidentRay.directionUnitVector).scale(-1E-12)), vReflect, incidentRay.power * reflectedPower,   incidentRay.wavelength, incidentRay.mediumIndexOfRefraction, incidentRay.frequency);
                var refracted = Ray.create(scratchP.set(point).add(scratchU.set(incidentRay.directionUnitVector).scale(+1E-12)), vRefract, incidentRay.power * transmittedPower, incidentRay.wavelength, n2,                                  incidentRay.frequency);

                if (this.get('showReflections') || shouldTotalInternalReflection)
                    this.propagateRay(reflected, count + 1);

                this.propagateRay(refracted, count + 1);
console.log(intersection.getPoint())
                // Add the incident ray itself
                this.addRay(LightRay.create(
                    incidentRay.tail,
                    intersection.getPoint(),
                    n1,
                    wavelengthInN1,
                    incidentRay.power,
                    incidentRay.wavelength,
                    waveWidth,
                    0,
                    null,
                    true,
                    false
                ));

                // Clean up; we don't need this anymore
                reflected.destroy();
                refracted.destroy();
            }
            else {
                // No intersection, so the light ray should just keep going
                this.addRay(LightRay.create(
                    incidentRay.tail,
                    this._scratchVec.set(incidentRay.tail).add(incidentRay.directionUnitVector), // 1 meter long ray (long enough to seem like infinity for the sim which is at nm scale)
                    n1,
                    wavelengthInN1,
                    incidentRay.power,
                    incidentRay.wavelength,
                    waveWidth,
                    0,
                    null,
                    true,
                    false
                ));
            }
        },

        /**
         * Find the nearest intersection between a light ray and the set of prisms
         *   in the play area and return it or return null.
         */
        getIntersection: function(incidentRay, prisms) {
            // Get all the intersections
            var allIntersections = [];
            for (var i = 0; i < this.prisms.length; i++){
                var intersections = this.prisms.at(i).getIntersections(incidentRay);
                for (var j = 0; j < intersections.length; j++)
                    allIntersections.push(intersections[j]);
            }

            if (allIntersections.length) {
                // Get the closest one (which would be hit first)
                this._incidentRay = incidentRay;
                allIntersections.sort(this._intersectionCompare);

                var keeper = allIntersections[0];
                this.destroyIntersections(allIntersections, keeper);

                return keeper;
            }
            else {
                return null;
            }
        },

        _intersectionCompare: function(i1, i2) {
            return i1.getPoint().distance(this._incidentRay.tail) - i2.getPoint().distance(this._incidentRay.tail);
        },

        destroyIntersections: function(intersections, except) {
            for (var i = 0; i < intersections.length; i++) {
                if (intersections[i] !== except)
                    intersections[i].destroy();
            }
        },

        /**
         * Signify that another ray/interface collision occurred
         */
        addIntersection: function(intersection) {
            this.intersections.push(intersection);
        },

        isLaserInPrism: function() {
            for (var i = 0; i < this.prisms.length; i++) {
                if (this.prisms.at(i).contains(this.laser.get('emissionPoint')))
                    return true;
            }
            return false;
        },

        /**
         * Responds to changes in mediums by telling the simulation to update
         */
        mediumChanged: function() {
            this.updateOnNextFrame();
        },

        prismChanged: function() {
            this.updateOnNextFrame();
        }

    });

    return PrismBreakSimulation;
});
<|endoftext|>


<commit_before>/**
 * 定义一个组件
 */
$.widget("iui.counter", {
    /**
     * 组件的默认选项，可以由多从覆盖关系
     */
    options: {
        minusSelector: ".counter-minus",
        plusSelector: ".counter-plus",
        inputSelector: ".counter-input",
        minValue: 0,
        maxValue: Infinity,
        step: 1
    },
    /**
     * _create 创建组件时调用一次
     */
    _create: function () {
        /**
         * this 对象为一个 组件 实例
         * 不是 Zepto/jQuery 对象
         * 也不是 Dom 对象
         */

        /**
         * this.element 组件对应的单个 Zepto/jQuery 对象
         */
        var $this = this.element;

        /**
         * 经过继承的 options
         */
        var options = this.options;

        /**
         * 建议: Zepto/jQuery 对象变量名前加 $
         */
        this.$minus = $this.find(options.minusSelector); // !!!选择器选择的时候需要指定范围!!!
        this.$plus = $this.find(options.plusSelector);
        this.$input = $this.find(options.inputSelector);
    },
    /**
     * _init 初始化的时候调用
     */
    _init: function () {
        var options = this.options;
        var minValue = Number(options.minValue);
        var maxValue = Number(options.maxValue);

        this._minValue = isNaN(minValue) ? 0 : minValue;
        this._maxValue = isNaN(maxValue) ? Infinity : maxValue;

        this._initValue();
        this._initEvent();
    },
    /**
     * _initValue 自定义的成员函数，
     * 所有以下划线开头的函数不可在外部调用
     */
    _initValue: function () {
        //var initValue = Number(this.$input.val());
        //this._value = isNaN(initValue) ? 0 : initValue;
        this.value(Number(this.$input.val()));
    },
    _initEvent: function () {
        var thisObj = this;
        var step = Number(this.options.step);
        step = isNaN(step) ? 1 : step;
        this.$plus.on("tap click", function () {
            thisObj.value(thisObj._value + step);
        });
        this.$minus.on("tap click", function () {
            thisObj.value(thisObj._value - step);
        });
        this.$input.on("blur", function () {
            thisObj._initValue();
        });
    },
    /**
     * value 自定义的成员方法,
     * 没有返回值或者返回值为 undefined 时会保持调用链，
     * 如果返回值不为 undefined 则将该值返回，不能再次链式调用
     *
     * @param n
     * @return {undefined}
     */
    value: function (n) {
        var value;
        var oldValue;
        var eventData;

        if (arguments.length > 0) {

            value = Number(n);
            if (isNaN(value)) {
                return;
            }

            value = Math.min(this._maxValue, Math.max(this._minValue, value));
            oldValue = this._value;
            if (oldValue === value) {
                return;
            }

            eventData = {
                oldValue: oldValue,
                newValue: value
            };

            /**
             * this._trigger 派发自定义事件
             * 使用 jQuery/Zepto 的事件机制
             * 监听时需要加上模块名
             * eg: $("xx").navbar().on("navbar:xxx", function(){
             *    // 可以通过 return false 影响程序执行
             *    return false;
             * });
             */
            if (this._trigger("beforeupdate", null, eventData)) {

                this.$input.val(value);
                this._value = value;
                this._trigger("update", null, eventData);
            }
        } else {
            return this._value;
        }
    }
});
<commit_msg>add boost namaspace<commit_after>/**
 * 定义一个组件
 */
$.widget("boost.counter", {
    /**
     * 组件的默认选项，可以由多从覆盖关系
     */
    options: {
        minusSelector: ".counter-minus",
        plusSelector: ".counter-plus",
        inputSelector: ".counter-input",
        minValue: 0,
        maxValue: Infinity,
        step: 1
    },
    /**
     * _create 创建组件时调用一次
     */
    _create: function () {
        /**
         * this 对象为一个 组件 实例
         * 不是 Zepto/jQuery 对象
         * 也不是 Dom 对象
         */

        /**
         * this.element 组件对应的单个 Zepto/jQuery 对象
         */
        var $this = this.element;

        /**
         * 经过继承的 options
         */
        var options = this.options;

        /**
         * 建议: Zepto/jQuery 对象变量名前加 $
         */
        this.$minus = $this.find(options.minusSelector); // !!!选择器选择的时候需要指定范围!!!
        this.$plus = $this.find(options.plusSelector);
        this.$input = $this.find(options.inputSelector);
    },
    /**
     * _init 初始化的时候调用
     */
    _init: function () {
        var options = this.options;
        var minValue = Number(options.minValue);
        var maxValue = Number(options.maxValue);

        this._minValue = isNaN(minValue) ? 0 : minValue;
        this._maxValue = isNaN(maxValue) ? Infinity : maxValue;

        this._initValue();
        this._initEvent();
    },
    /**
     * _initValue 自定义的成员函数，
     * 所有以下划线开头的函数不可在外部调用
     */
    _initValue: function () {
        //var initValue = Number(this.$input.val());
        //this._value = isNaN(initValue) ? 0 : initValue;
        this.value(Number(this.$input.val()));
    },
    _initEvent: function () {
        var thisObj = this;
        var step = Number(this.options.step);
        step = isNaN(step) ? 1 : step;
        this.$plus.on("tap click", function () {
            thisObj.value(thisObj._value + step);
        });
        this.$minus.on("tap click", function () {
            thisObj.value(thisObj._value - step);
        });
        this.$input.on("blur", function () {
            thisObj._initValue();
        });
    },
    /**
     * value 自定义的成员方法,
     * 没有返回值或者返回值为 undefined 时会保持调用链，
     * 如果返回值不为 undefined 则将该值返回，不能再次链式调用
     *
     * @param n
     * @return {undefined}
     */
    value: function (n) {
        var value;
        var oldValue;
        var eventData;

        if (arguments.length > 0) {

            value = Number(n);
            if (isNaN(value)) {
                return;
            }

            value = Math.min(this._maxValue, Math.max(this._minValue, value));
            oldValue = this._value;
            if (oldValue === value) {
                return;
            }

            eventData = {
                oldValue: oldValue,
                newValue: value
            };

            /**
             * this._trigger 派发自定义事件
             * 使用 jQuery/Zepto 的事件机制
             * 监听时需要加上模块名
             * eg: $("xx").navbar().on("navbar:xxx", function(){
             *    // 可以通过 return false 影响程序执行
             *    return false;
             * });
             */
            if (this._trigger("beforeupdate", null, eventData)) {

                this.$input.val(value);
                this._value = value;
                this._trigger("update", null, eventData);
            }
        } else {
            return this._value;
        }
    }
});
<|endoftext|>


<commit_before>'use strict';

/*
* The original C++ code and copyright notice is from the [Boost library]{@link http://www.boost.org/doc/libs/1_37_0/boost/math/special_functions/beta.hpp}.
*
* The implementation has been modified for JavaScript.
*/

/*
* (C) Copyright John Maddock 2006.
* Use, modification and distribution are subject to the
* Boost Software License, Version 1.0. (See accompanying file
* LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
*/

// MAIN //

/**
* Calculates
*	(a)(a+1)(a+2)...(a+k-1)
*	_______________________
*	(b)(b+1)(b+2)...(b+k-1)
* This function computes the delta in: beta(a,b,x) = prefix + delta * beta(a+k,b,x). It is only called with small k, for large k it is grossly inefficient.
*
* @param {number} a - input value
* @param {number} b - input value
* @param {number} k - input value
* @returns {number} ratio value
*/
function rising_factorial_ratio( a, b, k ) {
	var result;
	var i;
	if ( k === 0 ) {
		return 1.0;
	}
	result = 1.0;
	for ( i = 0; i < k; ++i ) {
		result *= ( a + i ) / ( b + i );
	}
	return result;
} // end FUNCTION rising_factorial_ratio()

// EXPORTS //

module.exports = rising_factorial_ratio;
<commit_msg>Turn function name to camelCase<commit_after>'use strict';

/*
* The original C++ code and copyright notice is from the [Boost library]{@link http://www.boost.org/doc/libs/1_37_0/boost/math/special_functions/beta.hpp}.
*
* The implementation has been modified for JavaScript.
*/

/*
* (C) Copyright John Maddock 2006.
* Use, modification and distribution are subject to the
* Boost Software License, Version 1.0. (See accompanying file
* LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
*/

// MAIN //

/**
* Calculates
*	(a)(a+1)(a+2)...(a+k-1)
*	_______________________
*	(b)(b+1)(b+2)...(b+k-1)
* This function computes the delta in: beta(a,b,x) = prefix + delta * beta(a+k,b,x). It is only called with small k, for large k it is grossly inefficient.
*
* @param {number} a - input value
* @param {number} b - input value
* @param {number} k - input value
* @returns {number} ratio value
*/
function risingFactorialRatio( a, b, k ) {
	var result;
	var i;
	if ( k === 0 ) {
		return 1.0;
	}
	result = 1.0;
	for ( i = 0; i < k; ++i ) {
		result *= ( a + i ) / ( b + i );
	}
	return result;
} // end FUNCTION risingFactorialRatio()

// EXPORTS //

module.exports = risingFactorialRatio;
<|endoftext|>


<commit_before>
var nodes = []; // un tableau pour stocker les position en 3D
var stems = [];

var maxRad = 42
var maxIteration = 500
var niteration=0

var seed

var settings  // une variable pour la librairie quicksettings
var xrot,yrot,zpos // trois variables pour définir l'orientation de la caméra

function setup() {
    createCanvas(windowWidth, windowHeight, WEBGL); // on utilise le mode webgl : on peut donc faire de la 3D
    background(0);

    stems.push(new Stem(0,0,0,maxRad,0.0025))

    // initialisation de la position de notre camera
    xrot =0
    yrot =-PI/2
    zpos =-500

    // création des élément de gui pour manipuler la caméra
    // chaque élément dispose d'une fonction callback : càd qui est executée quand lorsqu'il y a une
    // interaction de l'utilisateur avec l'élément en question
    settings = QuickSettings.create(5, 5, "Controls");
    settings.addRange("camera x rotation", -TWO_PI, TWO_PI, 0, 0.1, camXChange);
    settings.addRange("camera y rotation", -PI, PI, -PI/2, 0.1, camYChange);
    settings.addRange("camera z position", -1500, 1500, -500, 1, camZChange);
    settings.addButton("camera reset", resetCam);
    settings.addButton("regenerate tree", regenerate);
}

// callback pour les éléments gui
function camXChange(val){
    xrot = val
}
function camYChange(val){
    yrot = val
}
function camZChange(val){
    zpos= val
}
function resetCam(){
    xrot =0
    yrot =-PI/2
    zpos =-500
}
function regenerate(){

    nodes = [];
    stems = [];
    maxRad = 30
    stems.push(new Stem(0,0,0,maxRad,0.0025))
    xrot =0
    yrot =-PI/2
    zpos =-500
    niteration = 0

}

function draw() {

    background(0);
    // un effet visuel de modification de l'éclairage en fonction de la position de la souris
    // http://p5js.org/examples/3d-multiple-lights.html
    var locY = (mouseX / height - 0.5) * (-1);
    var locX = (1- (mouseY / width-0.5) ) * 1;
    //ambientLight(25);
    directionalLight(5, 75, 5, 0.55, 0.35, 0.35);
    pointLight(15, 65, 15, locX, locY, 0);
    pointLight(15, 65, 5, -locX, -locY, 0);

    translate(0,400,0)

    push()
    // positionnement de notre dessin en fonction de variables dédiés à la caméra
    translate(0,0,zpos);
    rotateY(xrot);
    rotateX(yrot);

     // on dessine l'ensemble des objets stockés
    for (var i = 0 ; i < stems.length ; i++){
        stems[i].update();
    }

    // on dessine l'ensemble des objets stockés
    for (var i = 0 ; i < nodes.length ; i++){
        nodes[i].draw();
    }
    pop()
}

function Stem (x,y,z,rad, noiseIncr) {

    this.rad = rad
    this.startRad = rad

    this.noiseF = random(5000)// un facteur de bruit pour le déplacement à l'aide d'un bruit de Perlin
    this.noiseIncr = noiseIncr

    //variables pour stocker les coordonnées sphériques !
    this.theta = random(-PI/2,PI/2)
    this.phi = random(-PI/2,PI/2)
    this.r = 1

    this.x = x
    this.y = y
    this.z = z

    this.update=function(){
        // on limite le nombre d'éléments crées et la vitesse à laquelle ils se créent
        if( nodes.length < 50000){
            this.r = this.rad // on s'éloigne du centre à vitesse constante
            // avec une orientation dépendant d'un bruit de Perlin
            this.noiseF += this.noiseIncr
            this.theta += map(noise(this.noiseF,10,20),0,1,-0.15,0.15)
            this.phi += map(noise(66,this.noiseF,42),0,1,-0.25,0.25)

            // on diminue le rayon tout en le conservant dans un intevalle acceptable.
            this.rad -= this.rad*0.1618*0.1618
            this.rad = constrain(this.rad, 1,100);

            // on convertit nos coordonnées sphériques en coordonnées cartésiennes
            // https://en.wikipedia.org/wiki/Spherical_coordinate_system#Cartesian_coordinates
            this.x+= this.r * sin(this.theta) * cos(this.phi)
            this.y+= this.r * sin(this.theta) * sin(this.phi)
            this.z+= this.r * cos(this.theta)

            // on créer un nouvel objet aux positions calculées, avec le nouveau diamètre
            nodes.push(new Node(this.x,this.y,this.z,this.rad))

            if(this.rad < this.startRad*2/3 && niteration < maxIteration){
                if (niteration < 35 ){
                    stems.push(new Stem(this.x,this.y,this.z, this.rad - this.rad*0.1618, 0.065))
                    stems.push(new Stem(this.x,this.y,this.z, this.rad - this.rad*0.1618, 0.065))
                    stems.push(new Stem(this.x,this.y,this.z, this.rad - this.rad*0.1618, 0.065))
                    stems.push(new Stem(this.x,this.y,this.z, this.rad - this.rad*0.1618, 0.065))
                }
                else if(niteration > 35 && niteration <100 ){
                    stems.push(new Stem(this.x,this.y,this.z, this.rad - this.rad*0.1618, 0.39))
                    stems.push(new Stem(this.x,this.y,this.z, this.rad - this.rad*0.1618, 0.39))
                    stems.push(new Stem(this.x,this.y,this.z, this.rad - this.rad*0.1618, 0.39))
                }
                else {
                    stems.push(new Stem(this.x,this.y,this.z, this.rad - this.rad*0.1618, 0.7192))
                    stems.push(new Stem(this.x,this.y,this.z, this.rad - this.rad*0.1618, 0.7192))
                }

                niteration +=1

                var i = stems.indexOf(this);
                stems.splice(i,1)

                console.log(stems.length, niteration)
            }
        }
    }
}

// Cette classe ne fait que stocker des valeurs
// et dessiner une sphère à l'endroit et à la taille définie par ces valeurs.
function Node(x,y,z,rad) {
    this.xpos = x;
    this.ypos = y;
    this.zpos = z;
    this.rad = rad;

    this.draw = function(){
        push()
        translate(this.xpos,this.ypos, this.zpos)
        sphere(this.rad)
        pop()
    }
}
<commit_msg>fix tree example<commit_after>var nodes = []; // un tableau pour stocker les position en 3D
var stems = [];
var cam // une variable pour notre caméra
var maxRad = 25
var maxIteration = 150
var niteration = 0
var seed
var settings // une variable pour la librairie quicksettings
function setup() {
    createCanvas(windowWidth, windowHeight, WEBGL); // on utilise le mode webgl : on peut donc faire de la 3D
    background(0);
    stems.push(new Stem(0, 0, 0, maxRad, 0.000, 0.0, 0.0))
        // initialisation de la position de notre camera
    cam = new EasyCam()
        // création des élément de gui pour manipuler la caméra
        // chaque élément dispose d'une fonction callback : càd qui est executée quand lorsqu'il y a une
        // interaction de l'utilisateur avec l'élément en question
    settings = QuickSettings.create(5, 5, "Controls");
    settings.addButton("camera reset", function () {
        cam.resetCam()
    });
    settings.addButton("regenerate tree", regenerate);
}

function regenerate() {
    nodes = [];
    stems = [];
    maxRad = 25
    stems.push(new Stem(0, 0, 0, maxRad, 0.00, 0.0, 0.0))
    niteration = 0
}

function draw() {
    background(0);
    ambientLight(55, 45, 3);
    pointLight(55, 65, 10, -300 * cos(frameCount / 20), 0, -300 * sin(frameCount / 20 + PI));
    pointLight(55, 65, 10, -300 * cos(frameCount / 20), -300 * sin(frameCount / 20 + PI), 0);
    cam.update()
    // on pousse un nouveau repère pour garder les transformation de la caméra mais pouvoir quand même
    // dessiner la base de l'arbre plus bas, mais de garder le fait de se déplace en abscisses pour faire
    // tourner l'abre sur lui même !
    push()
    translate(0, 800, 0) // on se décalle
    push()// on pousse un nouveau repère dans lequel on force l'orientation
    rotateY(0);
    rotateX(-PI / 2);
    // initialisation de la position de notre camera
    // on dessine l'ensemble des objets stockés
    for (var i = 0; i < stems.length; i++) {
        stems[i].update();
    }
    // on dessine l'ensemble des objets stockés
    for (var i = 0; i < nodes.length; i++) {
        nodes[i].draw();
    }
    pop()
    pop()
}
// Cette classe définit une branche, au départ il n'y en a qu'une, puis elle se sépare en 4, puis en 3, puis en 2
// à chaque fois que la branche se sépare : cela signifie qu'on ajoute un certain nombres d'objet de type Stem au tableau
// "stems", mais aussi qu'on enlève la branche en question.
// Chaque branche se déplace en 3D avec un noise comme nos tentacules précédement et dispose des instances de l'objet Node
// au bon endroit
function Stem(x, y, z, rad, noiseIncr, theta, phi) {
    // coordonnées cartésiennes du node à créer
    this.x = x
    this.y = y
    this.z = z
    this.rad = rad // rayon du node à créer
    // on garde en mémoire le rayon de départ, pour pouvoir comparer le rayon actuel au rayon de départ et déclencher les embranchements
    this.startRad = rad
    this.noiseF = random(1000) // un facteur de bruit pour le déplacement à l'aide d'un bruit de Perlin
    this.noiseIncr = noiseIncr // on fera en sorte que les branches basses tournent peu et les branches hautes un peu plus
     //variables pour stocker les coordonnées sphériques !
    this.theta = theta;
    this.phi = phi;
    this.r = 1
    this.update = function () {
        // on limite le nombre d'éléments crées et la vitesse à laquelle ils se créent
        if (nodes.length < 9000) {
            this.rad -= this.rad * 0.01 // on diminue le rayon par le nombre d'or divisé par 10 au carré
            this.rad = constrain(this.rad, maxRad / 30, maxRad);
            console.log(this.rad)
            this.r = this.rad // on s'éloigne du centre à vitesse
            this.r = constrain(this.r, 10, maxRad);
            // avec une orientation dépendant d'un bruit de Perlin
            this.noiseF += this.noiseIncr
            this.theta += map(noise(this.noiseF, 10, 20), 0, 1, -0.15, 0.15)
            this.phi += map(noise(66, this.noiseF, 42), 0, 1, -0.25, 0.25)
            // on convertit nos coordonnées sphériques en coordonnées cartésiennes
            // https://en.wikipedia.org/wiki/Spherical_coordinate_system#Cartesian_coordinates
            this.x += this.r * sin(this.theta) * cos(this.phi)
            this.y += this.r * sin(this.theta) * sin(this.phi)
            this.z += this.r * cos(this.theta)
            nodes.push(new Node(this.x, this.y, this.z, this.rad))
            // on va créer des embranchement en comparant le rayon au rayon initial avec pour limite un nombre d'embranchement
            // maximum (définit tout en haut)
            if (this.rad > maxRad / 25 && this.rad < this.startRad * 0.719 && niteration < maxIteration) {
                var i = stems.indexOf(this);
                stems.splice(i, 1)
                    // en fonction de l'iteration on crée différents types d'embranchements
                if (niteration < 20) {
                    stems.push(new Stem(this.x, this.y, this.z, this.rad, 0.065, random(-PI / 4, PI / 4), random(-PI / 4, PI / 4)))
                    stems.push(new Stem(this.x, this.y, this.z, this.rad, 0.065, random(-PI / 4, PI / 4), random(-PI / 4, PI / 4)))
                    stems.push(new Stem(this.x, this.y, this.z, this.rad, 0.065, random(-PI / 4, PI / 4), random(-PI / 4, PI / 4)))
                    stems.push(new Stem(this.x, this.y, this.z, this.rad, 0.065, random(-PI / 4, PI / 4), random(-PI / 4, PI / 4)))
                }
                else if (niteration > 20 && niteration < 45) {
                    stems.push(new Stem(this.x, this.y, this.z, this.rad, 0.119, random(-PI / 3, PI / 3), random(-PI / 3, PI / 3)))
                    stems.push(new Stem(this.x, this.y, this.z, this.rad, 0.119, random(-PI / 3, PI / 3), random(-PI / 3, PI / 3)))
                    stems.push(new Stem(this.x, this.y, this.z, this.rad, 0.119, random(-PI / 3, PI / 3), random(-PI / 3, PI / 3)))
                }
                else {
                    stems.push(new Stem(this.x, this.y, this.z, this.rad, 0.492, random(-PI / 3, PI / 3), random(-PI / 3, PI / 3)))
                    stems.push(new Stem(this.x, this.y, this.z, this.rad, 0.492, random(-PI / 3, PI / 3), random(-PI / 3, PI / 3)))
                }
                niteration += 1
            }
            console.log(nodes.length, stems.length, niteration)
        }
    }
}
// Cette classe ne fait que stocker des valeurs
// et dessiner une sphère à l'endroit et à la taille définie par ces valeurs.
function Node(x, y, z, rad) {
    this.xpos = x;
    this.ypos = y;
    this.zpos = z;
    this.rad = rad;
    this.draw = function () {
        push()
        specularMaterial(180)
        translate(this.xpos, this.ypos, this.zpos)
        sphere(this.rad)
        pop()
    }
}
// appeler les fonction de EazyCam pour en fonction des actions de l'utilisateur
function mouseDragged() {
    cam.drag(mouseX, mouseY, pmouseX, pmouseY)
}

function mouseWheel(val) {
    // val.deltaY récupère la variation de déplacement à deux doigts
    // sur le touch pad de haut en bas
    cam.move(val.deltaY)
}
// une classe pour manipuler la caméra 3D avec la souris.
// maintenir cliqué et déplacer la souris pour regarder autour
// molette de la souris pour se rapprocher ou s'éloigner.
function EasyCam() {
    // 3 variables d'états + 3 cibles pour interpolation
    this.xrot = 0
    this.zpos = 0
    this.yrot = 0
    this.xrotTarget = 0
    this.yrotTarget = 0
    this.zposTarget = -1600
    this.update = function () {
        // interpolation des varaibales d'état
        this.xrot += (this.xrotTarget - this.xrot) * 0.05
        this.yrot += (this.yrotTarget - this.yrot) * 0.05
        this.zpos += (this.zposTarget - this.zpos) * 0.1
            // orientation des dessin qui suiveront l'appel de cette fonction
        translate(0, 0, this.zpos);
        rotateX(this.xrot)
        rotateY(this.yrot)
    }
    this.resetCam = function () {
        this.xrot = 0
        this.zpos = 0
        this.yrot = 0
        this.xrotTarget = 0
        this.yrotTarget = 0
        this.zposTarget = -1600
    }
    this.drag = function (x, y, px, py) {
        // changer la valeur de la cible en fonction du déplacement de la souris
        // en abscisses et en ordonées
        this.xrotTarget += (y - py) / 100;
        this.yrotTarget += (x - px) / 100;
    }
    this.move = function (val) {
        //changer la valeur de la position cible le long de l'axe z
        this.zposTarget += val
    }
}
<|endoftext|>


<commit_before>var coordos = (function(){
    'use strict';
    var dims    = getViewportDimensions();
    // visual effect, to produce more square shape
    dims.width  = dims.width - 200;
    function init() {
        var container = getSVG('map', dims, 'body');
        var group = container.append('g');
        var ngon_group = container.append('g')
        .attr('transform', getCenterTranslation(dims));

        var map = d3_geometer.coordSpace(group, dims, 4);
        var ngon = d3_geometer.nGon(ngon_group);

        var ngon_group2 = group.append('g')
        .attr('transform', 'translate(300, 200)');
        var ngon2 = d3_geometer.nGon(ngon_group2);

        var ngon_group3 = group.append('g')
        .attr('transform', 'translate(' + dims.width / 4 + ', 500)');
        var ngon3 = d3_geometer.nGon(ngon_group3);

        ngon(200, 5)
        .drawLabels()
        .drawVertices()
        .drawCenterPoint(dims.height, dims.width, true);

        ngon2(100, 8)
        .drawLabels()
        .drawVertices()
        .drawCenterPoint(dims.height, dims.width, true);

        ngon3(120, 3)
        .drawLabels()
        .drawVertices()
        .drawCenterPoint(dims.height, dims.width, true);
    }

    return {
        'init': init
    };
})();

window.onload = coordos.init;
<commit_msg>Change number for demo<commit_after>var coordos = (function(){
    'use strict';
    var dims    = getViewportDimensions();
    // visual effect, to produce more square shape
    dims.width  = dims.width - 200;
    function init() {
        var container = getSVG('map', dims, 'body');
        var group = container.append('g');
        var ngon_group = container.append('g')
        .attr('transform', getCenterTranslation(dims));

        var map = d3_geometer.coordSpace(group, dims, 10);
        var ngon = d3_geometer.nGon(ngon_group);

        var ngon_group2 = group.append('g')
        .attr('transform', 'translate(300, 200)');
        var ngon2 = d3_geometer.nGon(ngon_group2);

        var ngon_group3 = group.append('g')
        .attr('transform', 'translate(' + dims.width / 4 + ', 500)');
        var ngon3 = d3_geometer.nGon(ngon_group3);

        ngon(200, 5)
        .drawLabels()
        .drawVertices()
        .drawCenterPoint(dims.height, dims.width, true);

        ngon2(100, 8)
        .drawLabels()
        .drawVertices()
        .drawCenterPoint(dims.height, dims.width, true);

        ngon3(120, 3)
        .drawLabels()
        .drawVertices()
        .drawCenterPoint(dims.height, dims.width, true);
    }

    return {
        'init': init
    };
})();

window.onload = coordos.init;
<|endoftext|>


<commit_before>/*************************************************************
 *
 *  [Contrib]/a11y/accessibility-menu.js
 *
 *  A thin extension to add opt-in menu items for the accessibility
 *  extensions in the a11y contributed directory.
 *
 *  ---------------------------------------------------------------------
 *
 *  Copyright (c) 2016-2017 The MathJax Consortium
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

(function(HUB,EXTENSIONS) {
  var SETTINGS = HUB.config.menuSettings;
  var ITEM, MENU; // filled in when MathMenu extension loads

  var BIND = (Function.prototype.bind ? function (f,t) {return f.bind(t)} :
              function (f,t) {return function () {f.apply(t,arguments)}});
  var KEYS = Object.keys || function (obj) {
    var keys = [];
    for (var id in obj) {if (obj.hasOwnProperty(id)) keys.push(id)}
    return keys;
  };

  //
  //  Set up the a11y path,if it isn't already in place
  //
  var PATH = MathJax.Ajax.config.path;
  if (!PATH.a11y) PATH.a11y = HUB.config.root + "/extensions/a11y";

  var Accessibility = EXTENSIONS["accessibility-menu"] = {
    version: '1.2.2',
    prefix: '', //'Accessibility-',
    default: {},
    modules: [],
    MakeOption: function(name) {
      return Accessibility.prefix + name;
    },
    GetOption: function(option) {
      return SETTINGS[Accessibility.MakeOption(option)];
    },
    AddDefaults: function() {
      var keys = KEYS(Accessibility.default);
      for (var i = 0, key; key = keys[i]; i++) {
        var option = Accessibility.MakeOption(key);
        if (typeof(SETTINGS[option]) === 'undefined') {
          SETTINGS[option] = Accessibility.default[key];
        }
      }
    },
    // Attaches the menu items;
    AddMenu: function() {
      var items = Array(this.modules.length);
      for (var i = 0, module; module = this.modules[i]; i++) items[i] = module.placeHolder;
      var menu = MENU.FindId('Accessibility');
      if (menu) {
        items.unshift(ITEM.RULE());
        menu.submenu.items.push.apply(menu.submenu.items,items);
      } else {
        var renderer = (MENU.FindId("Settings","Renderer")||{}).submenu;
        if (renderer) {
          // move AssitiveMML and InTabOrder from Renderer to Accessibility menu
          items.unshift(ITEM.RULE());
          items.unshift(renderer.items.pop());
          items.unshift(renderer.items.pop());
        }
        items.unshift("Accessibility");
        var menu = ITEM.SUBMENU.apply(ITEM.SUBMENU,items);
        var locale = MENU.IndexOfId('Locale');
        if (locale) {
          MENU.items.splice(locale,0,menu);
        } else {
          MENU.items.push(ITEM.RULE(), menu);
        }
      }
    },
    Register: function(module) {
      Accessibility.default[module.option] = false;
      Accessibility.modules.push(module);
    },
    Startup: function() {
      ITEM = MathJax.Menu.ITEM;
      MENU = MathJax.Menu.menu;
      for (var i = 0, module; module = this.modules[i]; i++) module.CreateMenu();
      this.AddMenu();
    },
    LoadExtensions: function () {
      var extensions = [];
      for (var i = 0, mpdule; module = this.modules[i]; i++) {
        if (SETTINGS[module.option]) extensions.push(module.module);
      }
      return (extensions.length ? HUB.Startup.loadArray(extensions) : null);
    }
  };

  var ModuleLoader = MathJax.Extension.ModuleLoader = MathJax.Object.Subclass({
    option: '',
    name: ['',''],
    module: '',
    placeHolder: null,
    submenu: false,
    extension: null,
    Init: function(option, name, module, extension, submenu) {
      this.option = option;
      this.name = [name.replace(/ /g,''),name];
      this.module = module;
      this.extension = extension;
      this.submenu = (submenu || false);
    },
    CreateMenu: function() {
      var load = BIND(this.Load,this);
      if (this.submenu) {
        this.placeHolder =
          ITEM.SUBMENU(this.name,
            ITEM.CHECKBOX(["Activate","Activate"],
                          Accessibility.MakeOption(this.option), {action: load}),
            ITEM.RULE(),
            ITEM.COMMAND(["OptionsWhenActive","(Options when Active)"],null,{disabled:true})
          );
      } else {
        this.placeHolder = ITEM.CHECKBOX(
          this.name, Accessibility.MakeOption(this.option), {action: load}
        );
      }
    },
    Load: function() {
      HUB.Queue(["Require",MathJax.Ajax,this.module,["Enable",this]]);
    },
    Enable: function(menu) {
      var extension = MathJax.Extension[this.extension];
      if (extension) {
        extension.Enable(true,true);
        MathJax.Menu.saveCookie();
      }
    }
  });

  Accessibility.Register(
    ModuleLoader(
      'collapsible', 'Collapsible Math', '[a11y]/collapsible.js', 'collapsible'
    )
  );
  Accessibility.Register(
    ModuleLoader(
      'autocollapse', 'Auto Collapse', '[a11y]/auto-collapse.js', 'auto-collapse'
    )
  );
  Accessibility.Register(
    ModuleLoader(
      'explorer', 'Explorer', '[a11y]/explorer.js', 'explorer', true
    )
  );

  Accessibility.AddDefaults();

  HUB.Register.StartupHook('End Extensions', function () {
    HUB.Register.StartupHook('MathMenu Ready', function () {
      Accessibility.Startup();
      HUB.Startup.signal.Post('Accessibility Menu Ready');
    },5);   // run before other extensions' menu hooks even if they are loaded first
  },5);

  MathJax.Callback.Queue(
    ["LoadExtensions",Accessibility],
    ["loadComplete",MathJax.Ajax,"[a11y]/accessibility-menu.js"]
  );

})(MathJax.Hub,MathJax.Extension);

<commit_msg>Fix timing so that accessible-menu works as part of a combined configuration file.  Resolves issue #197.<commit_after>/*************************************************************
 *
 *  [Contrib]/a11y/accessibility-menu.js
 *
 *  A thin extension to add opt-in menu items for the accessibility
 *  extensions in the a11y contributed directory.
 *
 *  ---------------------------------------------------------------------
 *
 *  Copyright (c) 2016-2017 The MathJax Consortium
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

(function(HUB,EXTENSIONS) {
  var SETTINGS = HUB.config.menuSettings;
  var ITEM, MENU; // filled in when MathMenu extension loads

  var BIND = (Function.prototype.bind ? function (f,t) {return f.bind(t)} :
              function (f,t) {return function () {f.apply(t,arguments)}});
  var KEYS = Object.keys || function (obj) {
    var keys = [];
    for (var id in obj) {if (obj.hasOwnProperty(id)) keys.push(id)}
    return keys;
  };

  //
  //  Set up the a11y path,if it isn't already in place
  //
  var PATH = MathJax.Ajax.config.path;
  if (!PATH.a11y) PATH.a11y = HUB.config.root + "/extensions/a11y";

  var Accessibility = EXTENSIONS["accessibility-menu"] = {
    version: '1.2.2',
    prefix: '', //'Accessibility-',
    default: {},
    modules: [],
    MakeOption: function(name) {
      return Accessibility.prefix + name;
    },
    GetOption: function(option) {
      return SETTINGS[Accessibility.MakeOption(option)];
    },
    AddDefaults: function() {
      var keys = KEYS(Accessibility.default);
      for (var i = 0, key; key = keys[i]; i++) {
        var option = Accessibility.MakeOption(key);
        if (typeof(SETTINGS[option]) === 'undefined') {
          SETTINGS[option] = Accessibility.default[key];
        }
      }
    },
    // Attaches the menu items;
    AddMenu: function() {
      var items = Array(this.modules.length);
      for (var i = 0, module; module = this.modules[i]; i++) items[i] = module.placeHolder;
      var menu = MENU.FindId('Accessibility');
      if (menu) {
        items.unshift(ITEM.RULE());
        menu.submenu.items.push.apply(menu.submenu.items,items);
      } else {
        var renderer = (MENU.FindId("Settings","Renderer")||{}).submenu;
        if (renderer) {
          // move AssitiveMML and InTabOrder from Renderer to Accessibility menu
          items.unshift(ITEM.RULE());
          items.unshift(renderer.items.pop());
          items.unshift(renderer.items.pop());
        }
        items.unshift("Accessibility");
        var menu = ITEM.SUBMENU.apply(ITEM.SUBMENU,items);
        var locale = MENU.IndexOfId('Locale');
        if (locale) {
          MENU.items.splice(locale,0,menu);
        } else {
          MENU.items.push(ITEM.RULE(), menu);
        }
      }
    },
    Register: function(module) {
      Accessibility.default[module.option] = false;
      Accessibility.modules.push(module);
    },
    Startup: function() {
      ITEM = MathJax.Menu.ITEM;
      MENU = MathJax.Menu.menu;
      for (var i = 0, module; module = this.modules[i]; i++) module.CreateMenu();
      this.AddMenu();
    },
    LoadExtensions: function () {
      var extensions = [];
      for (var i = 0, module; module = this.modules[i]; i++) {
        if (SETTINGS[module.option]) extensions.push(module.module);
      }
      return (extensions.length ? HUB.Startup.loadArray(extensions) : null);
    }
  };

  var ModuleLoader = MathJax.Extension.ModuleLoader = MathJax.Object.Subclass({
    option: '',
    name: ['',''],
    module: '',
    placeHolder: null,
    submenu: false,
    extension: null,
    Init: function(option, name, module, extension, submenu) {
      this.option = option;
      this.name = [name.replace(/ /g,''),name];
      this.module = module;
      this.extension = extension;
      this.submenu = (submenu || false);
    },
    CreateMenu: function() {
      var load = BIND(this.Load,this);
      if (this.submenu) {
        this.placeHolder =
          ITEM.SUBMENU(this.name,
            ITEM.CHECKBOX(["Activate","Activate"],
                          Accessibility.MakeOption(this.option), {action: load}),
            ITEM.RULE(),
            ITEM.COMMAND(["OptionsWhenActive","(Options when Active)"],null,{disabled:true})
          );
      } else {
        this.placeHolder = ITEM.CHECKBOX(
          this.name, Accessibility.MakeOption(this.option), {action: load}
        );
      }
    },
    Load: function() {
      HUB.Queue(["Require",MathJax.Ajax,this.module,["Enable",this]]);
    },
    Enable: function(menu) {
      var extension = MathJax.Extension[this.extension];
      if (extension) {
        extension.Enable(true,true);
        MathJax.Menu.saveCookie();
      }
    }
  });

  Accessibility.Register(
    ModuleLoader(
      'collapsible', 'Collapsible Math', '[a11y]/collapsible.js', 'collapsible'
    )
  );
  Accessibility.Register(
    ModuleLoader(
      'autocollapse', 'Auto Collapse', '[a11y]/auto-collapse.js', 'auto-collapse'
    )
  );
  Accessibility.Register(
    ModuleLoader(
      'explorer', 'Explorer', '[a11y]/explorer.js', 'explorer', true
    )
  );

  Accessibility.AddDefaults();

  HUB.Register.StartupHook('End Extensions', function () {
    HUB.Register.StartupHook('MathMenu Ready', function () {
      Accessibility.Startup();
      HUB.Startup.signal.Post('Accessibility Menu Ready');
    },5);   // run before other extensions' menu hooks even if they are loaded first
  },5);

  MathJax.Hub.Register.StartupHook("End Cookie", function () {
    MathJax.Callback.Queue(
      ["LoadExtensions",Accessibility],
      ["loadComplete",MathJax.Ajax,"[a11y]/accessibility-menu.js"]
    );
  });

})(MathJax.Hub,MathJax.Extension);

<|endoftext|>


<commit_before>/** @jsx React.DOM */
'use strict';
var React = require('react');
var globals = require('./globals');

var SignIn = module.exports.SignIn = React.createClass({
    render: function() {
        var hidden = !this.props.session || this.props.session.persona;
        var disabled = !this.props.personaReady;
        return (

Data Providers

                Sign In

No access? Request an account.

Authentication by Mozilla Persona.

        );
    }
});

var Home = module.exports.Home = React.createClass({
    render: function() {
        var hidden = !this.props.session || this.props.session.persona;
        return (

ENCODE

The Encyclopedia of DNA Elements

                        {this.transferPropsTo(<SignIn />)}

        );
    }
});

globals.content_views.register(Home, 'portal');
<commit_msg>Basic layout for home page mod.<commit_after>/** @jsx React.DOM */
'use strict';
var React = require('react');
var globals = require('./globals');

var SignIn = module.exports.SignIn = React.createClass({
    render: function() {
        var hidden = !this.props.session || this.props.session.persona;
        var disabled = !this.props.personaReady;
        return (

Data Providers

                Sign In

No access? Request an account.

Authentication by Mozilla Persona.

        );
    }
});

var Info = module.exports.Info = React.createClass({
    render: function() {
        return (

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas feugiat posuere lorem, id posuere odio commodo at.

Curabitur in eleifend mauris, ut malesuada tellus. Maecenas facilisis venenatis odio, laoreet tincidunt eros bibendum a. Sed pulvinar justo eu scelerisque ullamcorper. Vivamus eget mollis purus. Maecenas condimentum convallis ante, sit amet tincidunt lacus porta vel.

        );
    }
});

var Home = module.exports.Home = React.createClass({
    render: function() {
        var hidden = !this.props.session || this.props.session.persona;
        return (

ENCODE

The Encyclopedia of DNA Elements

                        <Info />

        );
    }
});

globals.content_views.register(Home, 'portal');
<|endoftext|>


<commit_before>/**
 * Except
 */
var exceptAttribute = function except(property, propertyValue, attributeValue, propertyAttributes, callback) {
  if (attributeValue.indexOf(propertyValue) !== -1) {
    this.addError('except', property);
  }
  return callback();
};

// Export
Validation.prototype.addAttribute('except', exceptAttribute);<commit_msg>Minor improvements to the ‘except’ attribute<commit_after>/**
 * Except
 */
var exceptAttribute = function except(property, propertyValue, attributeValue, propertyAttributes, callback) {

  if (attributeValue.indexOf(propertyValue) !== -1) {
    this.addError();
  }

  return callback();

};

// Export
Validation.prototype.addAttribute('except', exceptAttribute);<|endoftext|>


<commit_before>/**
 * Copyright 2013 Facebook, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @providesModule SelectEventPlugin
 */

"use strict";

var EventConstants = require('EventConstants');
var EventPluginHub = require('EventPluginHub');
var EventPropagators = require('EventPropagators');
var ExecutionEnvironment = require('ExecutionEnvironment');
var ReactInputSelection = require('ReactInputSelection');
var SyntheticEvent = require('SyntheticEvent');

var getActiveElement = require('getActiveElement');
var isEventSupported = require('isEventSupported');
var isTextInputElement = require('isTextInputElement');
var keyOf = require('keyOf');
var shallowEqual = require('shallowEqual');

var topLevelTypes = EventConstants.topLevelTypes;

var eventTypes = {
  select: {
    phasedRegistrationNames: {
      bubbled: keyOf({onSelect: null}),
      captured: keyOf({onSelectCapture: null})
    }
  }
};

var useSelectionChange = false;

if (ExecutionEnvironment.canUseDOM) {
  useSelectionChange = 'onselectionchange' in document;
}

var activeElement = null;
var activeElementID = null;
var activeNativeEvent = null;
var lastSelection = null;
var mouseDown = false;

/**
 * Get an object which is a unique representation of the current selection.
 *
 * The return value will not be consistent across nodes or browsers, but
 * two identical selections on the same node will return identical objects.
 *
 * @param {DOMElement} node
 * @param {object}
 */
function getSelection(node) {
  if ('selectionStart' in node &&
      ReactInputSelection.hasSelectionCapabilities(node)) {
    return {
      start: node.selectionStart,
      end: node.selectionEnd
    };
  } else if (document.selection) {
    var range = document.selection.createRange();
    return {
      parentElement: range.parentElement(),
      text: range.text,
      top: range.boundingTop,
      left: range.boundingLeft
    };
  } else {
    var selection = window.getSelection();
    return {
      anchorNode: selection.anchorNode,
      anchorOffset: selection.anchorOffset,
      focusNode: selection.focusNode,
      focusOffset: selection.focusOffset
    };
  }
}

/**
 * Poll selection to see whether it's changed.
 *
 * @param {object} nativeEvent
 * @return {?SyntheticEvent}
 */
function constructSelectEvent(nativeEvent) {
  // Ensure we have the right element, and that the user is not dragging a
  // selection (this matches native `select` event behavior).
  if (mouseDown || activeElement != getActiveElement()) {
    return;
  }

  // Only fire when selection has actually changed.
  var currentSelection = getSelection(activeElement);
  if (!lastSelection || !shallowEqual(lastSelection, currentSelection)) {
    lastSelection = currentSelection;

    var syntheticEvent = SyntheticEvent.getPooled(
      eventTypes.select,
      activeElementID,
      nativeEvent
    );

    syntheticEvent.type = 'select';
    syntheticEvent.target = activeElement;

    EventPropagators.accumulateTwoPhaseDispatches(syntheticEvent);

    return syntheticEvent;
  }
}

/**
 * Handle deferred event. And manually dispatch synthetic events.
 */
function dispatchDeferredSelectEvent() {
  if (!activeNativeEvent) {
    return;
  }

  var syntheticEvent = constructSelectEvent(activeNativeEvent);
  activeNativeEvent = null;

  // Enqueue and process the abstract event manually.
  if (syntheticEvent) {
    EventPluginHub.enqueueEvents(syntheticEvent);
    EventPluginHub.processEventQueue();
  }
}

/**
 * This plugin creates an `onSelect` event that normalizes select events
 * across form elements.
 *
 * Supported elements are:
 * - input (see `isTextInputElement`)
 * - textarea
 * - contentEditable
 *
 * This differs from native browser implementations in the following ways:
 * - Fires on contentEditable fields as well as inputs.
 * - Fires for collapsed selection.
 * - Fires after user input.
 */
var SelectEventPlugin = {

  eventTypes: eventTypes,

  /**
   * @param {string} topLevelType Record from `EventConstants`.
   * @param {DOMEventTarget} topLevelTarget The listening component root node.
   * @param {string} topLevelTargetID ID of `topLevelTarget`.
   * @param {object} nativeEvent Native browser event.
   * @return {*} An accumulation of synthetic events.
   * @see {EventPluginHub.extractEvents}
   */
  extractEvents: function(
      topLevelType,
      topLevelTarget,
      topLevelTargetID,
      nativeEvent) {

    switch (topLevelType) {
      // Track the input node that has focus.
      case topLevelTypes.topFocus:
        if (isTextInputElement(topLevelTarget) ||
            topLevelTarget.contentEditable === 'true') {
          activeElement = topLevelTarget;
          activeElementID = topLevelTargetID;
          lastSelection = null;
        }
        break;
      case topLevelTypes.topBlur:
        activeElement = null;
        activeElementID = null;
        lastSelection = null;
        break;

      // Don't fire the event while the user is dragging. This matches the
      // semantics of the native select event.
      case topLevelTypes.topMouseDown:
        mouseDown = true;
        break;
      case topLevelTypes.topMouseUp:
        mouseDown = false;
        return constructSelectEvent(nativeEvent);

      // Chrome and IE fire non-standard event when selection is changed (and
      // sometimes when it hasn't).
      case topLevelTypes.topSelectionChange:
        return constructSelectEvent(nativeEvent);

      // Firefox doesn't support selectionchange, so check selection status
      // after each key entry.
      case topLevelTypes.topKeyDown:
        if (!useSelectionChange) {
          activeNativeEvent = nativeEvent;
          setTimeout(dispatchDeferredSelectEvent, 0);
        }
        break;
    }
  }
};

module.exports = SelectEventPlugin;
<commit_msg>Removing 'isEventSupported'<commit_after>/**
 * Copyright 2013 Facebook, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @providesModule SelectEventPlugin
 */

"use strict";

var EventConstants = require('EventConstants');
var EventPluginHub = require('EventPluginHub');
var EventPropagators = require('EventPropagators');
var ExecutionEnvironment = require('ExecutionEnvironment');
var ReactInputSelection = require('ReactInputSelection');
var SyntheticEvent = require('SyntheticEvent');

var getActiveElement = require('getActiveElement');
var isTextInputElement = require('isTextInputElement');
var keyOf = require('keyOf');
var shallowEqual = require('shallowEqual');

var topLevelTypes = EventConstants.topLevelTypes;

var eventTypes = {
  select: {
    phasedRegistrationNames: {
      bubbled: keyOf({onSelect: null}),
      captured: keyOf({onSelectCapture: null})
    }
  }
};

var useSelectionChange = false;

if (ExecutionEnvironment.canUseDOM) {
  useSelectionChange = 'onselectionchange' in document;
}

var activeElement = null;
var activeElementID = null;
var activeNativeEvent = null;
var lastSelection = null;
var mouseDown = false;

/**
 * Get an object which is a unique representation of the current selection.
 *
 * The return value will not be consistent across nodes or browsers, but
 * two identical selections on the same node will return identical objects.
 *
 * @param {DOMElement} node
 * @param {object}
 */
function getSelection(node) {
  if ('selectionStart' in node &&
      ReactInputSelection.hasSelectionCapabilities(node)) {
    return {
      start: node.selectionStart,
      end: node.selectionEnd
    };
  } else if (document.selection) {
    var range = document.selection.createRange();
    return {
      parentElement: range.parentElement(),
      text: range.text,
      top: range.boundingTop,
      left: range.boundingLeft
    };
  } else {
    var selection = window.getSelection();
    return {
      anchorNode: selection.anchorNode,
      anchorOffset: selection.anchorOffset,
      focusNode: selection.focusNode,
      focusOffset: selection.focusOffset
    };
  }
}

/**
 * Poll selection to see whether it's changed.
 *
 * @param {object} nativeEvent
 * @return {?SyntheticEvent}
 */
function constructSelectEvent(nativeEvent) {
  // Ensure we have the right element, and that the user is not dragging a
  // selection (this matches native `select` event behavior).
  if (mouseDown || activeElement != getActiveElement()) {
    return;
  }

  // Only fire when selection has actually changed.
  var currentSelection = getSelection(activeElement);
  if (!lastSelection || !shallowEqual(lastSelection, currentSelection)) {
    lastSelection = currentSelection;

    var syntheticEvent = SyntheticEvent.getPooled(
      eventTypes.select,
      activeElementID,
      nativeEvent
    );

    syntheticEvent.type = 'select';
    syntheticEvent.target = activeElement;

    EventPropagators.accumulateTwoPhaseDispatches(syntheticEvent);

    return syntheticEvent;
  }
}

/**
 * Handle deferred event. And manually dispatch synthetic events.
 */
function dispatchDeferredSelectEvent() {
  if (!activeNativeEvent) {
    return;
  }

  var syntheticEvent = constructSelectEvent(activeNativeEvent);
  activeNativeEvent = null;

  // Enqueue and process the abstract event manually.
  if (syntheticEvent) {
    EventPluginHub.enqueueEvents(syntheticEvent);
    EventPluginHub.processEventQueue();
  }
}

/**
 * This plugin creates an `onSelect` event that normalizes select events
 * across form elements.
 *
 * Supported elements are:
 * - input (see `isTextInputElement`)
 * - textarea
 * - contentEditable
 *
 * This differs from native browser implementations in the following ways:
 * - Fires on contentEditable fields as well as inputs.
 * - Fires for collapsed selection.
 * - Fires after user input.
 */
var SelectEventPlugin = {

  eventTypes: eventTypes,

  /**
   * @param {string} topLevelType Record from `EventConstants`.
   * @param {DOMEventTarget} topLevelTarget The listening component root node.
   * @param {string} topLevelTargetID ID of `topLevelTarget`.
   * @param {object} nativeEvent Native browser event.
   * @return {*} An accumulation of synthetic events.
   * @see {EventPluginHub.extractEvents}
   */
  extractEvents: function(
      topLevelType,
      topLevelTarget,
      topLevelTargetID,
      nativeEvent) {

    switch (topLevelType) {
      // Track the input node that has focus.
      case topLevelTypes.topFocus:
        if (isTextInputElement(topLevelTarget) ||
            topLevelTarget.contentEditable === 'true') {
          activeElement = topLevelTarget;
          activeElementID = topLevelTargetID;
          lastSelection = null;
        }
        break;
      case topLevelTypes.topBlur:
        activeElement = null;
        activeElementID = null;
        lastSelection = null;
        break;

      // Don't fire the event while the user is dragging. This matches the
      // semantics of the native select event.
      case topLevelTypes.topMouseDown:
        mouseDown = true;
        break;
      case topLevelTypes.topMouseUp:
        mouseDown = false;
        return constructSelectEvent(nativeEvent);

      // Chrome and IE fire non-standard event when selection is changed (and
      // sometimes when it hasn't).
      case topLevelTypes.topSelectionChange:
        return constructSelectEvent(nativeEvent);

      // Firefox doesn't support selectionchange, so check selection status
      // after each key entry.
      case topLevelTypes.topKeyDown:
        if (!useSelectionChange) {
          activeNativeEvent = nativeEvent;
          setTimeout(dispatchDeferredSelectEvent, 0);
        }
        break;
    }
  }
};

module.exports = SelectEventPlugin;
<|endoftext|>


<commit_before>'use strict';

export default class extends think.base {
  /**
   * init
   * @return {} []
   */
  init(){
    this.comparison = {
      'EQ': '$eq',
      '=': '$eq',
      'NEQ': '$ne',
      '!=': '$ne',
      '<>': '$ne',
      'GT': '$gt',
      '>': '$gt',
      'EGT': '$gte',
      '>=': '$gte',
      'LT': '$lt',
      '<': '$lt',
      'ELT': '$lte',
      '<=': '$lte',
      'OR': '$or',
      'IN': '$in',
      'NOTIN': '$nin'
    };
  }
  /**
   * parse field
   * @param  {String} field   []
   * @param  {Boolean} reverse []
   * @return {Object}         []
   */
  parseField(field, reverse){
    if(!field){
      return {};
    }
    if(think.isString(field)){
      field = field.split(/\s*,\s*/);
    }
    if(think.isArray(field)){
      let result = {};
      field.forEach(item => {
        result[item] = reverse ? 0 : 1;
      });
      return result;
    }
    if(reverse){
      for(let key in field){
        field[key] = 0;
      }
    }
    return field;
  }
  /**
   * parse limit
   * @param  {Object} collection []
   * @param  {Array} limit      []
   * @return {Object}            []
   */
  parseLimit(limit){
    if(!limit){
      return [];
    }
    if(think.isNumber(limit)){
      return [0, limit];
    }
    if(think.isString(limit)){
      limit = limit.split(/\s*,\s*/);
    }
    let skip = limit[0] | 0;
    let limitNum = limit[1] | 0;
    if(limitNum){
      return [skip, limitNum];
    }
    return [0, skip];
  }
  /**
   * parse order
   * @param  {String} order []
   * @return {Object}       []
   */
  parseOrder(order){
    if(!order){
      return {};
    }
    if(order === true || order === 'natural'){
      return {
        $natural: 1
      };
    }
    if(think.isString(order)){
      order = order.split(/\s*,\s*/);
      let result = {};
      order.forEach(item => {
        item = item.split(' ');
        let type = (item[1] || '').toLowerCase();
        result[item[0].trim()] = type === 'desc' ? -1 : 1;
      });
      return result;
    }
    for(let key in order){
      if(order[key] === false || order[key] === 0){
        order[key] = -1;
      }else if(order[key] !== -1){
        order[key] = 1;
      }
    }
    return order;
  }
  /**
   * parse group
   * @param  {String} group []
   * @return {Object}       []
   */
  parseGroup(group){
    if (think.isEmpty(group)) {
      return '';
    }
    if (think.isString(group)) {
      group = group.split(/\s*,\s*/);
    }
    return group;
  }
  /**
   * parse where
   * http://docs.mongodb.org/manual/reference/operator/query/
   * @param  {Object} where []
   * @return {Object}       []
   */
  parseWhere(where){

    if(think.isArray(where)){
      return where.map(item => {
        return this.parseWhere(item);
      });
    }

    if(think.isObject(where)){
      let result = {};
      for(let key in where){
        let value = where[key];
        let {ObjectID} = think.require('mongodb');
        if(key === '_id' && think.isString(value) || ObjectID(value) === ObjectID(value)){
          let validator = think.require('validator');
          if(validator.mongoId(value)){
            result[key] = ObjectID(value);
            continue;
          }
        }
        key = this.comparison[key] || key;
        if(think.isObject(value) || think.isArray(value)){
          value = this.parseWhere(value);
        }
        result[key] = value;
      }
      return result;
    }
    return (where === undefined ? {} : where);
  }
  /**
   * parse distinct
   * @param  {String} distinct []
   * @return {String}          []
   */
  parseDistinct(distinct){
    return distinct;
  }
}
<commit_msg>Fix ObjectId error<commit_after>'use strict';

const isMayBeObjectID = id => {
  if(!think.isObject(id)) return false
  try{
    const str = id.toHexString()
    const validator = think.require('validator');
    if(validator.mongoId(str)){
      return true
    }
  }catch(e){}
  return false
}

export default class extends think.base {
  /**
   * init
   * @return {} []
   */
  init(){
    this.comparison = {
      'EQ': '$eq',
      '=': '$eq',
      'NEQ': '$ne',
      '!=': '$ne',
      '<>': '$ne',
      'GT': '$gt',
      '>': '$gt',
      'EGT': '$gte',
      '>=': '$gte',
      'LT': '$lt',
      '<': '$lt',
      'ELT': '$lte',
      '<=': '$lte',
      'OR': '$or',
      'IN': '$in',
      'NOTIN': '$nin'
    };
  }
  /**
   * parse field
   * @param  {String} field   []
   * @param  {Boolean} reverse []
   * @return {Object}         []
   */
  parseField(field, reverse){
    if(!field){
      return {};
    }
    if(think.isString(field)){
      field = field.split(/\s*,\s*/);
    }
    if(think.isArray(field)){
      let result = {};
      field.forEach(item => {
        result[item] = reverse ? 0 : 1;
      });
      return result;
    }
    if(reverse){
      for(let key in field){
        field[key] = 0;
      }
    }
    return field;
  }
  /**
   * parse limit
   * @param  {Object} collection []
   * @param  {Array} limit      []
   * @return {Object}            []
   */
  parseLimit(limit){
    if(!limit){
      return [];
    }
    if(think.isNumber(limit)){
      return [0, limit];
    }
    if(think.isString(limit)){
      limit = limit.split(/\s*,\s*/);
    }
    let skip = limit[0] | 0;
    let limitNum = limit[1] | 0;
    if(limitNum){
      return [skip, limitNum];
    }
    return [0, skip];
  }
  /**
   * parse order
   * @param  {String} order []
   * @return {Object}       []
   */
  parseOrder(order){
    if(!order){
      return {};
    }
    if(order === true || order === 'natural'){
      return {
        $natural: 1
      };
    }
    if(think.isString(order)){
      order = order.split(/\s*,\s*/);
      let result = {};
      order.forEach(item => {
        item = item.split(' ');
        let type = (item[1] || '').toLowerCase();
        result[item[0].trim()] = type === 'desc' ? -1 : 1;
      });
      return result;
    }
    for(let key in order){
      if(order[key] === false || order[key] === 0){
        order[key] = -1;
      }else if(order[key] !== -1){
        order[key] = 1;
      }
    }
    return order;
  }
  /**
   * parse group
   * @param  {String} group []
   * @return {Object}       []
   */
  parseGroup(group){
    if (think.isEmpty(group)) {
      return '';
    }
    if (think.isString(group)) {
      group = group.split(/\s*,\s*/);
    }
    return group;
  }
  /**
   * parse where
   * http://docs.mongodb.org/manual/reference/operator/query/
   * @param  {Object} where []
   * @return {Object}       []
   */
  parseWhere(where){

    if(think.isArray(where)){
      return where.map(item => {
        return this.parseWhere(item);
      });
    }

    if(think.isObject(where)){
      let result = {};
      for(let key in where){
        let value = where[key];
        if(isMayBeObjectID(value)) {
          result[key] = value;
          continue
        }
        if(key === '_id' && think.isString(value)){
          let validator = think.require('validator');
          if(validator.mongoId(value)){
            let {ObjectID} = think.require('mongodb');
            result[key] = ObjectID(value);
            continue;
          }
        }
        key = this.comparison[key] || key;
        if(think.isObject(value) || think.isArray(value)){
          value = this.parseWhere(value);
        }
        result[key] = value;
      }
      return result;
    }
    return (where === undefined ? {} : where);
  }
  /**
   * parse distinct
   * @param  {String} distinct []
   * @return {String}          []
   */
  parseDistinct(distinct){
    return distinct;
  }
}
<|endoftext|>