diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index bb607cf..0000000 Binary files a/.DS_Store and /dev/null differ diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..89f3886 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ + +.DS_Store + +.DS_Store + +.DS_Store diff --git a/assets/leap.js b/assets/leap.js new file mode 100644 index 0000000..f022c26 --- /dev/null +++ b/assets/leap.js @@ -0,0 +1,6882 @@ +;(function(e,t,n){function i(n,s){if(!t[n]){if(!e[n]){var o=typeof require=="function"&&require;if(!s&&o)return o(n,!0);if(r)return r(n,!0);throw new Error("Cannot find module '"+n+"'")}var u=t[n]={exports:{}};e[n][0].call(u.exports,function(t){var r=e[n][1][t];return i(r?r:t)},u,u.exports)}return t[n].exports}var r=typeof require=="function"&&require;for(var s=0;s= this.size) return undefined; + if (i >= this._buf.length) return undefined; + return this._buf[(this.pos - i - 1) % this.size]; +} + +CircularBuffer.prototype.push = function(o) { + this._buf[this.pos % this.size] = o; + return this.pos++; +} + +},{}],3:[function(require,module,exports){ +var Connection = module.exports = require('./base_connection') + +Connection.prototype.setupSocket = function() { + var connection = this; + var socket = new WebSocket(this.getUrl()); + socket.onopen = function() { connection.handleOpen() }; + socket.onmessage = function(message) { connection.handleData(message.data) }; + socket.onclose = function() { connection.handleClose() }; + return socket; +} + +Connection.prototype.startHeartbeat = function() { + if (!this.protocol.sendHeartbeat || this.heartbeatTimer) return; + var connection = this; + var propertyName = null; + if (typeof document.hidden !== "undefined") { + propertyName = "hidden"; + } else if (typeof document.mozHidden !== "undefined") { + propertyName = "mozHidden"; + } else if (typeof document.msHidden !== "undefined") { + propertyName = "msHidden"; + } else if (typeof document.webkitHidden !== "undefined") { + propertyName = "webkitHidden"; + } else { + propertyName = undefined; + } + + var windowVisible = true; + + var focusBlurHandler = function(e) { + windowVisible = e.type === 'focus'; + }; + + window.addEventListener('focus', focusBlurHandler); + window.addEventListener('blur', focusBlurHandler); + + this.on('disconnect', function() { + if (connection.heartbeatTimer) { + clearTimeout(connection.heartbeatTimer); + delete connection.heartbeatTimer; + } + window.removeEventListener('focus', focusBlurHandler); + window.removeEventListener('blur', focusBlurHandler); + }); + + this.heartbeatTimer = setInterval(function() { + var isVisible = propertyName === undefined ? true : document[propertyName] === false; + if (isVisible && windowVisible) { + connection.sendHeartbeat(); + } else { + connection.setHeartbeatState(false); + } + }, this.opts.heartbeatInterval); +} + +},{"./base_connection":1}],4:[function(require,module,exports){ +(function(process){var Frame = require('./frame') + , CircularBuffer = require("./circular_buffer") + , Pipeline = require("./pipeline") + , EventEmitter = require('events').EventEmitter + , gestureListener = require('./gesture').gestureListener + , _ = require('underscore'); + +/** + * Constructs a Controller object. + * + * When creating a Controller object, you may optionally pass in options + * to set the host , set the port, enable gestures, or select the frame event type. + * + * ```javascript + * var controller = new Leap.Controller({ + * host: '127.0.0.1', + * port: 6437, + * enableGestures: true, + * frameEventName: 'animationFrame' + * }); + * ``` + * + * @class Controller + * @memberof Leap + * @classdesc + * The Controller class is your main interface to the Leap Motion Controller. + * + * Create an instance of this Controller class to access frames of tracking data + * and configuration information. Frame data can be polled at any time using the + * [Controller.frame]{@link Leap.Controller#frame}() function. Call frame() or frame(0) to get the most recent + * frame. Set the history parameter to a positive integer to access previous frames. + * A controller stores up to 60 frames in its frame history. + * + * Polling is an appropriate strategy for applications which already have an + * intrinsic update loop, such as a game. + */ +var Controller = module.exports = function(opts) { + var inNode = typeof(process) !== 'undefined' && process.title === 'node'; + + opts = _.defaults(opts || {}, { + inNode: inNode + }); + + this.inNode = opts.inNode; + + opts = _.defaults(opts || {}, { + frameEventName: this.useAnimationLoop() ? 'animationFrame' : 'deviceFrame', + suppressAnimationLoop: false + }); + + this.suppressAnimationLoop = opts.suppressAnimationLoop; + this.frameEventName = opts.frameEventName; + this.history = new CircularBuffer(200); + this.lastFrame = Frame.Invalid; + this.lastValidFrame = Frame.Invalid; + this.lastConnectionFrame = Frame.Invalid; + this.accumulatedGestures = []; + if (opts.connectionType === undefined) { + this.connectionType = (this.inBrowser() ? require('./connection') : require('./node_connection')); + } else { + this.connectionType = opts.connectionType; + } + this.connection = new this.connectionType(opts); + this.setupConnectionEvents(); +} + +Controller.prototype.gesture = function(type, cb) { + var creator = gestureListener(this, type); + if (cb !== undefined) { + creator.stop(cb); + } + return creator; +} + +Controller.prototype.inBrowser = function() { + return !this.inNode; +} + +Controller.prototype.useAnimationLoop = function() { + return this.inBrowser() && typeof(chrome) === "undefined"; +} + +Controller.prototype.connect = function() { + var controller = this; + if (this.connection.connect() && this.inBrowser() && !controller.suppressAnimationLoop) { + var callback = function() { + controller.emit('animationFrame', controller.lastConnectionFrame); + window.requestAnimationFrame(callback); + } + window.requestAnimationFrame(callback); + } +} + +Controller.prototype.disconnect = function() { + this.connection.disconnect(); +} + +/** + * Returns a frame of tracking data from the Leap. + * + * Use the optional history parameter to specify which frame to retrieve. + * Call frame() or frame(0) to access the most recent frame; call frame(1) to + * access the previous frame, and so on. If you use a history value greater + * than the number of stored frames, then the controller returns an invalid frame. + * + * @method frame + * @memberof Leap.Controller.prototype + * @param {number} history The age of the frame to return, counting backwards from + * the most recent frame (0) into the past and up to the maximum age (59). + * @returns {Leap.Frame} The specified frame; or, if no history + * parameter is specified, the newest frame. If a frame is not available at + * the specified history position, an invalid Frame is returned. + */ +Controller.prototype.frame = function(num) { + return this.history.get(num) || Frame.Invalid; +} + +Controller.prototype.loop = function(callback) { + switch (callback.length) { + case 1: + this.on(this.frameEventName, callback); + break; + case 2: + var controller = this; + var scheduler = null; + var immediateRunnerCallback = function(frame) { + callback(frame, function() { + if (controller.lastFrame != frame) { + immediateRunnerCallback(controller.lastFrame); + } else { + controller.once(controller.frameEventName, immediateRunnerCallback); + } + }); + } + this.once(this.frameEventName, immediateRunnerCallback); + break; + } + this.connect(); +} + +Controller.prototype.addStep = function(step) { + if (!this.pipeline) this.pipeline = new Pipeline(this); + this.pipeline.addStep(step); +} + +Controller.prototype.processFrame = function(frame) { + if (frame.gestures) { + this.accumulatedGestures = this.accumulatedGestures.concat(frame.gestures); + } + if (this.pipeline) { + frame = this.pipeline.run(frame); + if (!frame) frame = Frame.Invalid; + } + this.lastConnectionFrame = frame; + this.emit('deviceFrame', frame); +} + +Controller.prototype.processFinishedFrame = function(frame) { + this.lastFrame = frame; + if (frame.valid) { + this.lastValidFrame = frame; + } + frame.controller = this; + frame.historyIdx = this.history.push(frame); + if (frame.gestures) { + frame.gestures = this.accumulatedGestures; + this.accumulatedGestures = []; + for (var gestureIdx = 0; gestureIdx != frame.gestures.length; gestureIdx++) { + this.emit("gesture", frame.gestures[gestureIdx], frame); + } + } + this.emit('frame', frame); +} + +Controller.prototype.setupConnectionEvents = function() { + var controller = this; + this.connection.on('frame', function(frame) { + controller.processFrame(frame); + }); + this.on(this.frameEventName, function(frame) { + controller.processFinishedFrame(frame); + }); + + // Delegate connection events + this.connection.on('disconnect', function() { controller.emit('disconnect'); }); + this.connection.on('ready', function() { controller.emit('ready'); }); + this.connection.on('connect', function() { controller.emit('connect'); }); + this.connection.on('focus', function() { controller.emit('focus') }); + this.connection.on('blur', function() { controller.emit('blur') }); + this.connection.on('protocol', function(protocol) { controller.emit('protocol', protocol); }); + this.connection.on('deviceConnect', function(evt) { controller.emit(evt.state ? 'deviceConnected' : 'deviceDisconnected'); }); +} + +_.extend(Controller.prototype, EventEmitter.prototype); + +})(require("__browserify_process")) +},{"./circular_buffer":2,"./connection":3,"./frame":5,"./gesture":6,"./node_connection":16,"./pipeline":10,"__browserify_process":18,"events":17,"underscore":20}],5:[function(require,module,exports){ +var Hand = require("./hand") + , Pointable = require("./pointable") + , createGesture = require("./gesture").createGesture + , glMatrix = require("gl-matrix") + , mat3 = glMatrix.mat3 + , vec3 = glMatrix.vec3 + , InteractionBox = require("./interaction_box") + , _ = require("underscore"); + +/** + * Constructs a Frame object. + * + * Frame instances created with this constructor are invalid. + * Get valid Frame objects by calling the + * [Controller.frame]{@link Leap.Controller#frame}() function. + * + * @class Frame + * @memberof Leap + * @classdesc + * The Frame class represents a set of hand and finger tracking data detected + * in a single frame. + * + * The Leap detects hands, fingers and tools within the tracking area, reporting + * their positions, orientations and motions in frames at the Leap frame rate. + * + * Access Frame objects using the [Controller.frame]{@link Leap.Controller#frame}() function. + */ +var Frame = module.exports = function(data) { + /** + * Reports whether this Frame instance is valid. + * + * A valid Frame is one generated by the Controller object that contains + * tracking data for all detected entities. An invalid Frame contains no + * actual tracking data, but you can call its functions without risk of a + * undefined object exception. The invalid Frame mechanism makes it more + * convenient to track individual data across the frame history. For example, + * you can invoke: + * + * ```javascript + * var finger = controller.frame(n).finger(fingerID); + * ``` + * + * for an arbitrary Frame history value, "n", without first checking whether + * frame(n) returned a null object. (You should still check that the + * returned Finger instance is valid.) + * + * @member valid + * @memberof Leap.Frame.prototype + * @type {Boolean} + */ + this.valid = true; + /** + * A unique ID for this Frame. Consecutive frames processed by the Leap + * have consecutive increasing values. + * @member id + * @memberof Leap.Frame.prototype + * @type {String} + */ + this.id = data.id; + /** + * The frame capture time in microseconds elapsed since the Leap started. + * @member timestamp + * @memberof Leap.Frame.prototype + * @type {number} + */ + this.timestamp = data.timestamp; + /** + * The list of Hand objects detected in this frame, given in arbitrary order. + * The list can be empty if no hands are detected. + * + * @member hands[] + * @memberof Leap.Frame.prototype + * @type {Leap.Hand} + */ + this.hands = []; + this.handsMap = {}; + /** + * The list of Pointable objects (fingers and tools) detected in this frame, + * given in arbitrary order. The list can be empty if no fingers or tools are + * detected. + * + * @member pointables[] + * @memberof Leap.Frame.prototype + * @type {Leap.Pointable} + */ + this.pointables = []; + /** + * The list of Tool objects detected in this frame, given in arbitrary order. + * The list can be empty if no tools are detected. + * + * @member tools[] + * @memberof Leap.Frame.prototype + * @type {Leap.Pointable} + */ + this.tools = []; + /** + * The list of Finger objects detected in this frame, given in arbitrary order. + * The list can be empty if no fingers are detected. + * @member fingers[] + * @memberof Leap.Frame.prototype + * @type {Leap.Pointable} + */ + this.fingers = []; + + if (data.interactionBox) { + this.interactionBox = new InteractionBox(data.interactionBox); + } + this.gestures = []; + this.pointablesMap = {}; + this._translation = data.t; + this._rotation = _.flatten(data.r); + this._scaleFactor = data.s; + this.data = data; + this.type = 'frame'; // used by event emitting + this.currentFrameRate = data.currentFrameRate; + var handMap = {}; + for (var handIdx = 0, handCount = data.hands.length; handIdx != handCount; handIdx++) { + var hand = new Hand(data.hands[handIdx]); + hand.frame = this; + this.hands.push(hand); + this.handsMap[hand.id] = hand; + handMap[hand.id] = handIdx; + } + for (var pointableIdx = 0, pointableCount = data.pointables.length; pointableIdx != pointableCount; pointableIdx++) { + var pointable = new Pointable(data.pointables[pointableIdx]); + pointable.frame = this; + this.pointables.push(pointable); + this.pointablesMap[pointable.id] = pointable; + (pointable.tool ? this.tools : this.fingers).push(pointable); + if (pointable.handId !== undefined && handMap.hasOwnProperty(pointable.handId)) { + var hand = this.hands[handMap[pointable.handId]]; + hand.pointables.push(pointable); + (pointable.tool ? hand.tools : hand.fingers).push(pointable); + } + } + + if (data.gestures) { + /** + * The list of Gesture objects detected in this frame, given in arbitrary order. + * The list can be empty if no gestures are detected. + * + * Circle and swipe gestures are updated every frame. Tap gestures + * only appear in the list for a single frame. + * @member gestures[] + * @memberof Leap.Frame.prototype + * @type {Leap.Gesture} + */ + for (var gestureIdx = 0, gestureCount = data.gestures.length; gestureIdx != gestureCount; gestureIdx++) { + this.gestures.push(createGesture(data.gestures[gestureIdx])); + } + } +} + +/** + * The tool with the specified ID in this frame. + * + * Use the Frame tool() function to retrieve a tool from + * this frame using an ID value obtained from a previous frame. + * This function always returns a Pointable object, but if no tool + * with the specified ID is present, an invalid Pointable object is returned. + * + * Note that ID values persist across frames, but only until tracking of a + * particular object is lost. If tracking of a tool is lost and subsequently + * regained, the new Pointable object representing that tool may have a + * different ID than that representing the tool in an earlier frame. + * + * @method tool + * @memberof Leap.Frame.prototype + * @param {String} id The ID value of a Tool object from a previous frame. + * @returns {Leap.Pointable} The tool with the + * matching ID if one exists in this frame; otherwise, an invalid Pointable object + * is returned. + */ +Frame.prototype.tool = function(id) { + var pointable = this.pointable(id); + return pointable.tool ? pointable : Pointable.Invalid; +} + +/** + * The Pointable object with the specified ID in this frame. + * + * Use the Frame pointable() function to retrieve the Pointable object from + * this frame using an ID value obtained from a previous frame. + * This function always returns a Pointable object, but if no finger or tool + * with the specified ID is present, an invalid Pointable object is returned. + * + * Note that ID values persist across frames, but only until tracking of a + * particular object is lost. If tracking of a finger or tool is lost and subsequently + * regained, the new Pointable object representing that finger or tool may have + * a different ID than that representing the finger or tool in an earlier frame. + * + * @method pointable + * @memberof Leap.Frame.prototype + * @param {String} id The ID value of a Pointable object from a previous frame. + * @returns {Leap.Pointable} The Pointable object with + * the matching ID if one exists in this frame; + * otherwise, an invalid Pointable object is returned. + */ +Frame.prototype.pointable = function(id) { + return this.pointablesMap[id] || Pointable.Invalid; +} + +/** + * The finger with the specified ID in this frame. + * + * Use the Frame finger() function to retrieve the finger from + * this frame using an ID value obtained from a previous frame. + * This function always returns a Finger object, but if no finger + * with the specified ID is present, an invalid Pointable object is returned. + * + * Note that ID values persist across frames, but only until tracking of a + * particular object is lost. If tracking of a finger is lost and subsequently + * regained, the new Pointable object representing that physical finger may have + * a different ID than that representing the finger in an earlier frame. + * + * @method finger + * @memberof Leap.Frame.prototype + * @param {String} id The ID value of a finger from a previous frame. + * @returns {Leap.Pointable} The finger with the + * matching ID if one exists in this frame; otherwise, an invalid Pointable + * object is returned. + */ +Frame.prototype.finger = function(id) { + var pointable = this.pointable(id); + return !pointable.tool ? pointable : Pointable.Invalid; +} + +/** + * The Hand object with the specified ID in this frame. + * + * Use the Frame hand() function to retrieve the Hand object from + * this frame using an ID value obtained from a previous frame. + * This function always returns a Hand object, but if no hand + * with the specified ID is present, an invalid Hand object is returned. + * + * Note that ID values persist across frames, but only until tracking of a + * particular object is lost. If tracking of a hand is lost and subsequently + * regained, the new Hand object representing that physical hand may have + * a different ID than that representing the physical hand in an earlier frame. + * + * @method hand + * @memberof Leap.Frame.prototype + * @param {String} id The ID value of a Hand object from a previous frame. + * @returns {Leap.Hand} The Hand object with the matching + * ID if one exists in this frame; otherwise, an invalid Hand object is returned. + */ +Frame.prototype.hand = function(id) { + return this.handsMap[id] || Hand.Invalid; +} + +/** + * The angle of rotation around the rotation axis derived from the overall + * rotational motion between the current frame and the specified frame. + * + * The returned angle is expressed in radians measured clockwise around + * the rotation axis (using the right-hand rule) between the start and end frames. + * The value is always between 0 and pi radians (0 and 180 degrees). + * + * The Leap derives frame rotation from the relative change in position and + * orientation of all objects detected in the field of view. + * + * If either this frame or sinceFrame is an invalid Frame object, then the + * angle of rotation is zero. + * + * @method rotationAngle + * @memberof Leap.Frame.prototype + * @param {Leap.Frame} sinceFrame The starting frame for computing the relative rotation. + * @param {number[]} [axis] The axis to measure rotation around. + * @returns {number} A positive value containing the heuristically determined + * rotational change between the current frame and that specified in the sinceFrame parameter. + */ +Frame.prototype.rotationAngle = function(sinceFrame, axis) { + if (!this.valid || !sinceFrame.valid) return 0.0; + + var rot = this.rotationMatrix(sinceFrame); + var cs = (rot[0] + rot[4] + rot[8] - 1.0)*0.5 + var angle = Math.acos(cs); + angle = isNaN(angle) ? 0.0 : angle; + + if (axis !== undefined) { + var rotAxis = this.rotationAxis(sinceFrame); + angle *= vec3.dot(rotAxis, vec3.normalize(vec3.create(), axis)); + } + + return angle; +} + +/** + * The axis of rotation derived from the overall rotational motion between + * the current frame and the specified frame. + * + * The returned direction vector is normalized. + * + * The Leap derives frame rotation from the relative change in position and + * orientation of all objects detected in the field of view. + * + * If either this frame or sinceFrame is an invalid Frame object, or if no + * rotation is detected between the two frames, a zero vector is returned. + * + * @method rotationAxis + * @memberof Leap.Frame.prototype + * @param {Leap.Frame} sinceFrame The starting frame for computing the relative rotation. + * @returns {number[]} A normalized direction vector representing the axis of the heuristically determined + * rotational change between the current frame and that specified in the sinceFrame parameter. + */ +Frame.prototype.rotationAxis = function(sinceFrame) { + if (!this.valid || !sinceFrame.valid) return vec3.create(); + return vec3.normalize(vec3.create(), [ + this._rotation[7] - sinceFrame._rotation[5], + this._rotation[2] - sinceFrame._rotation[6], + this._rotation[3] - sinceFrame._rotation[1] + ]); +} + +/** + * The transform matrix expressing the rotation derived from the overall + * rotational motion between the current frame and the specified frame. + * + * The Leap derives frame rotation from the relative change in position and + * orientation of all objects detected in the field of view. + * + * If either this frame or sinceFrame is an invalid Frame object, then + * this method returns an identity matrix. + * + * @method rotationMatrix + * @memberof Leap.Frame.prototype + * @param {Leap.Frame} sinceFrame The starting frame for computing the relative rotation. + * @returns {number[]} A transformation matrix containing the heuristically determined + * rotational change between the current frame and that specified in the sinceFrame parameter. + */ +Frame.prototype.rotationMatrix = function(sinceFrame) { + if (!this.valid || !sinceFrame.valid) return mat3.create(); + var transpose = mat3.transpose(mat3.create(), this._rotation) + return mat3.multiply(mat3.create(), sinceFrame._rotation, transpose); +} + +/** + * The scale factor derived from the overall motion between the current frame and the specified frame. + * + * The scale factor is always positive. A value of 1.0 indicates no scaling took place. + * Values between 0.0 and 1.0 indicate contraction and values greater than 1.0 indicate expansion. + * + * The Leap derives scaling from the relative inward or outward motion of all + * objects detected in the field of view (independent of translation and rotation). + * + * If either this frame or sinceFrame is an invalid Frame object, then this method returns 1.0. + * + * @method scaleFactor + * @memberof Leap.Frame.prototype + * @param {Leap.Frame} sinceFrame The starting frame for computing the relative scaling. + * @returns {number} A positive value representing the heuristically determined + * scaling change ratio between the current frame and that specified in the sinceFrame parameter. + */ +Frame.prototype.scaleFactor = function(sinceFrame) { + if (!this.valid || !sinceFrame.valid) return 1.0; + return Math.exp(this._scaleFactor - sinceFrame._scaleFactor); +} + +/** + * The change of position derived from the overall linear motion between the + * current frame and the specified frame. + * + * The returned translation vector provides the magnitude and direction of the + * movement in millimeters. + * + * The Leap derives frame translation from the linear motion of all objects + * detected in the field of view. + * + * If either this frame or sinceFrame is an invalid Frame object, then this + * method returns a zero vector. + * + * @method translation + * @memberof Leap.Frame.prototype + * @param {Leap.Frame} sinceFrame The starting frame for computing the relative translation. + * @returns {number[]} A vector representing the heuristically determined change in + * position of all objects between the current frame and that specified in the sinceFrame parameter. + */ +Frame.prototype.translation = function(sinceFrame) { + if (!this.valid || !sinceFrame.valid) return vec3.create(); + return vec3.subtract(vec3.create(), this._translation, sinceFrame._translation); +} + +/** + * A string containing a brief, human readable description of the Frame object. + * + * @method toString + * @memberof Leap.Frame.prototype + * @returns {String} A brief description of this frame. + */ +Frame.prototype.toString = function() { + var str = "Frame [ id:"+this.id+" | timestamp:"+this.timestamp+" | Hand count:("+this.hands.length+") | Pointable count:("+this.pointables.length+")"; + if (this.gestures) str += " | Gesture count:("+this.gestures.length+")"; + str += " ]"; + return str; +} + +/** + * Returns a JSON-formatted string containing the hands, pointables and gestures + * in this frame. + * + * @method dump + * @memberof Leap.Frame.prototype + * @returns {String} A JSON-formatted string. + */ +Frame.prototype.dump = function() { + var out = ''; + out += "Frame Info:
"; + out += this.toString(); + out += "

Hands:
" + for (var handIdx = 0, handCount = this.hands.length; handIdx != handCount; handIdx++) { + out += " "+ this.hands[handIdx].toString() + "
"; + } + out += "

Pointables:
"; + for (var pointableIdx = 0, pointableCount = this.pointables.length; pointableIdx != pointableCount; pointableIdx++) { + out += " "+ this.pointables[pointableIdx].toString() + "
"; + } + if (this.gestures) { + out += "

Gestures:
"; + for (var gestureIdx = 0, gestureCount = this.gestures.length; gestureIdx != gestureCount; gestureIdx++) { + out += " "+ this.gestures[gestureIdx].toString() + "
"; + } + } + out += "

Raw JSON:
"; + out += JSON.stringify(this.data); + return out; +} + +/** + * An invalid Frame object. + * + * You can use this invalid Frame in comparisons testing + * whether a given Frame instance is valid or invalid. (You can also check the + * [Frame.valid]{@link Leap.Frame#valid} property.) + * + * @static + * @type {Leap.Frame} + * @name Invalid + * @memberof Leap.Frame + */ +Frame.Invalid = { + valid: false, + hands: [], + fingers: [], + tools: [], + gestures: [], + pointables: [], + pointable: function() { return Pointable.Invalid }, + finger: function() { return Pointable.Invalid }, + hand: function() { return Hand.Invalid }, + toString: function() { return "invalid frame" }, + dump: function() { return this.toString() }, + rotationAngle: function() { return 0.0; }, + rotationMatrix: function() { return mat3.create(); }, + rotationAxis: function() { return vec3.create(); }, + scaleFactor: function() { return 1.0; }, + translation: function() { return vec3.create(); } +}; + +},{"./gesture":6,"./hand":7,"./interaction_box":9,"./pointable":11,"gl-matrix":19,"underscore":20}],6:[function(require,module,exports){ +var glMatrix = require("gl-matrix") + , vec3 = glMatrix.vec3 + , EventEmitter = require('events').EventEmitter + , _ = require('underscore'); + +/** + * Constructs a new Gesture object. + * + * An uninitialized Gesture object is considered invalid. Get valid instances + * of the Gesture class, which will be one of the Gesture subclasses, from a + * Frame object. + * + * @class Gesture + * @abstract + * @memberof Leap + * @classdesc + * The Gesture class represents a recognized movement by the user. + * + * The Leap watches the activity within its field of view for certain movement + * patterns typical of a user gesture or command. For example, a movement from side to + * side with the hand can indicate a swipe gesture, while a finger poking forward + * can indicate a screen tap gesture. + * + * When the Leap recognizes a gesture, it assigns an ID and adds a + * Gesture object to the frame gesture list. For continuous gestures, which + * occur over many frames, the Leap updates the gesture by adding + * a Gesture object having the same ID and updated properties in each + * subsequent frame. + * + * **Important:** Recognition for each type of gesture must be enabled; + * otherwise **no gestures are recognized or reported**. + * + * Subclasses of Gesture define the properties for the specific movement patterns + * recognized by the Leap. + * + * The Gesture subclasses for include: + * + * * CircleGesture -- A circular movement by a finger. + * * SwipeGesture -- A straight line movement by the hand with fingers extended. + * * ScreenTapGesture -- A forward tapping movement by a finger. + * * KeyTapGesture -- A downward tapping movement by a finger. + * + * Circle and swipe gestures are continuous and these objects can have a + * state of start, update, and stop. + * + * The screen tap gesture is a discrete gesture. The Leap only creates a single + * ScreenTapGesture object appears for each tap and it always has a stop state. + * + * Get valid Gesture instances from a Frame object. You can get a list of gestures + * from the Frame gestures array. You can also use the Frame gesture() method + * to find a gesture in the current frame using an ID value obtained in a + * previous frame. + * + * Gesture objects can be invalid. For example, when you get a gesture by ID + * using Frame.gesture(), and there is no gesture with that ID in the current + * frame, then gesture() returns an Invalid Gesture object (rather than a null + * value). Always check object validity in situations where a gesture might be + * invalid. + */ +var createGesture = exports.createGesture = function(data) { + var gesture; + switch (data.type) { + case 'circle': + gesture = new CircleGesture(data); + break; + case 'swipe': + gesture = new SwipeGesture(data); + break; + case 'screenTap': + gesture = new ScreenTapGesture(data); + break; + case 'keyTap': + gesture = new KeyTapGesture(data); + break; + default: + throw "unkown gesture type"; + } + /** + * The gesture ID. + * + * All Gesture objects belonging to the same recognized movement share the + * same ID value. Use the ID value with the Frame::gesture() method to + * find updates related to this Gesture object in subsequent frames. + * + * @member id + * @memberof Leap.Gesture.prototype + * @type {number} + */ + gesture.id = data.id; + /** + * The list of hands associated with this Gesture, if any. + * + * If no hands are related to this gesture, the list is empty. + * + * @member handIds + * @memberof Leap.Gesture.prototype + * @type {Array} + */ + gesture.handIds = data.handIds; + /** + * The list of fingers and tools associated with this Gesture, if any. + * + * If no Pointable objects are related to this gesture, the list is empty. + * + * @member pointableIds + * @memberof Leap.Gesture.prototype + * @type {Array} + */ + gesture.pointableIds = data.pointableIds; + /** + * The elapsed duration of the recognized movement up to the + * frame containing this Gesture object, in microseconds. + * + * The duration reported for the first Gesture in the sequence (with the + * start state) will typically be a small positive number since + * the movement must progress far enough for the Leap to recognize it as + * an intentional gesture. + * + * @member duration + * @memberof Leap.Gesture.prototype + * @type {number} + */ + gesture.duration = data.duration; + /** + * The gesture ID. + * + * Recognized movements occur over time and have a beginning, a middle, + * and an end. The 'state()' attribute reports where in that sequence this + * Gesture object falls. + * + * Possible values for the state field are: + * + * * start + * * update + * * stop + * + * @member state + * @memberof Leap.Gesture.prototype + * @type {String} + */ + gesture.state = data.state; + /** + * The gesture type. + * + * Possible values for the type field are: + * + * * circle + * * swipe + * * screenTap + * * keyTap + * + * @member type + * @memberof Leap.Gesture.prototype + * @type {String} + */ + gesture.type = data.type; + return gesture; +} + +var gestureListener = exports.gestureListener = function(controller, type) { + var handlers = {}; + var gestureMap = {}; + + var gestureCreator = function() { + var candidateGesture = gestureMap[gesture.id]; + if (candidateGesture !== undefined) gesture.update(gesture, frame); + if (gesture.state == "start" || gesture.state == "stop") { + if (type == gesture.type && gestureMap[gesture.id] === undefined) { + gestureMap[gesture.id] = new Gesture(gesture, frame); + gesture.update(gesture, frame); + } + if (gesture.state == "stop") { + delete gestureMap[gesture.id]; + } + } + }; + + controller.on('gesture', function(gesture, frame) { + if (gesture.type == type) { + if (gesture.state == "start" || gesture.state == "stop") { + if (gestureMap[gesture.id] === undefined) { + var gestureTracker = new Gesture(gesture, frame); + gestureMap[gesture.id] = gestureTracker; + _.each(handlers, function(cb, name) { + gestureTracker.on(name, cb); + }); + } + } + gestureMap[gesture.id].update(gesture, frame); + if (gesture.state == "stop") { + delete gestureMap[gesture.id]; + } + } + }); + var builder = { + start: function(cb) { + handlers['start'] = cb; + return builder; + }, + stop: function(cb) { + handlers['stop'] = cb; + return builder; + }, + complete: function(cb) { + handlers['stop'] = cb; + return builder; + }, + update: function(cb) { + handlers['update'] = cb; + return builder; + } + } + return builder; +} + +var Gesture = exports.Gesture = function(gesture, frame) { + this.gestures = [gesture]; + this.frames = [frame]; +} + +Gesture.prototype.update = function(gesture, frame) { + this.lastGesture = gesture; + this.lastFrame = frame; + this.gestures.push(gesture); + this.frames.push(frame); + this.emit(gesture.state, this); +} + +Gesture.prototype.translation = function() { + return vec3.subtract(vec3.create(), this.lastGesture.startPosition, this.lastGesture.position); +} + +_.extend(Gesture.prototype, EventEmitter.prototype); + +/** + * Constructs a new CircleGesture object. + * + * An uninitialized CircleGesture object is considered invalid. Get valid instances + * of the CircleGesture class from a Frame object. + * + * @class CircleGesture + * @memberof Leap + * @augments Leap.Gesture + * @classdesc + * The CircleGesture classes represents a circular finger movement. + * + * A circle movement is recognized when the tip of a finger draws a circle + * within the Leap field of view. + * + * ![CircleGesture](images/Leap_Gesture_Circle.png) + * + * Circle gestures are continuous. The CircleGesture objects for the gesture have + * three possible states: + * + * * start -- The circle gesture has just started. The movement has + * progressed far enough for the recognizer to classify it as a circle. + * * update -- The circle gesture is continuing. + * * stop -- The circle gesture is finished. + */ +var CircleGesture = function(data) { + /** + * The center point of the circle within the Leap frame of reference. + * + * @member center + * @memberof Leap.CircleGesture.prototype + * @type {number[]} + */ + this.center = data.center; + /** + * The normal vector for the circle being traced. + * + * If you draw the circle clockwise, the normal vector points in the same + * general direction as the pointable object drawing the circle. If you draw + * the circle counterclockwise, the normal points back toward the + * pointable. If the angle between the normal and the pointable object + * drawing the circle is less than 90 degrees, then the circle is clockwise. + * + * ```javascript + * var clockwiseness; + * if (circle.pointable.direction.angleTo(circle.normal) <= PI/4) { + * clockwiseness = "clockwise"; + * } + * else + * { + * clockwiseness = "counterclockwise"; + * } + * ``` + * + * @member normal + * @memberof Leap.CircleGesture.prototype + * @type {number[]} + */ + this.normal = data.normal; + /** + * The number of times the finger tip has traversed the circle. + * + * Progress is reported as a positive number of the number. For example, + * a progress value of .5 indicates that the finger has gone halfway + * around, while a value of 3 indicates that the finger has gone around + * the the circle three times. + * + * Progress starts where the circle gesture began. Since the circle + * must be partially formed before the Leap can recognize it, progress + * will be greater than zero when a circle gesture first appears in the + * frame. + * + * @member progress + * @memberof Leap.CircleGesture.prototype + * @type {number} + */ + this.progress = data.progress; + /** + * The radius of the circle in mm. + * + * @member radius + * @memberof Leap.CircleGesture.prototype + * @type {number} + */ + this.radius = data.radius; +} + +CircleGesture.prototype.toString = function() { + return "CircleGesture ["+JSON.stringify(this)+"]"; +} + +/** + * Constructs a new SwipeGesture object. + * + * An uninitialized SwipeGesture object is considered invalid. Get valid instances + * of the SwipeGesture class from a Frame object. + * + * @class SwipeGesture + * @memberof Leap + * @augments Leap.Gesture + * @classdesc + * The SwipeGesture class represents a swiping motion of a finger or tool. + * + * ![SwipeGesture](images/Leap_Gesture_Swipe.png) + * + * Swipe gestures are continuous. + */ +var SwipeGesture = function(data) { + /** + * The starting position within the Leap frame of + * reference, in mm. + * + * @member startPosition + * @memberof Leap.SwipeGesture.prototype + * @type {number[]} + */ + this.startPosition = data.startPosition; + /** + * The current swipe position within the Leap frame of + * reference, in mm. + * + * @member position + * @memberof Leap.SwipeGesture.prototype + * @type {number[]} + */ + this.position = data.position; + /** + * The unit direction vector parallel to the swipe motion. + * + * You can compare the components of the vector to classify the swipe as + * appropriate for your application. For example, if you are using swipes + * for two dimensional scrolling, you can compare the x and y values to + * determine if the swipe is primarily horizontal or vertical. + * + * @member direction + * @memberof Leap.SwipeGesture.prototype + * @type {number[]} + */ + this.direction = data.direction; + /** + * The speed of the finger performing the swipe gesture in + * millimeters per second. + * + * @member speed + * @memberof Leap.SwipeGesture.prototype + * @type {number} + */ + this.speed = data.speed; +} + +SwipeGesture.prototype.toString = function() { + return "SwipeGesture ["+JSON.stringify(this)+"]"; +} + +/** + * Constructs a new ScreenTapGesture object. + * + * An uninitialized ScreenTapGesture object is considered invalid. Get valid instances + * of the ScreenTapGesture class from a Frame object. + * + * @class ScreenTapGesture + * @memberof Leap + * @augments Leap.Gesture + * @classdesc + * The ScreenTapGesture class represents a tapping gesture by a finger or tool. + * + * A screen tap gesture is recognized when the tip of a finger pokes forward + * and then springs back to approximately the original postion, as if + * tapping a vertical screen. The tapping finger must pause briefly before beginning the tap. + * + * ![ScreenTap](images/Leap_Gesture_Tap2.png) + * + * ScreenTap gestures are discrete. The ScreenTapGesture object representing a tap always + * has the state, STATE_STOP. Only one ScreenTapGesture object is created for each + * screen tap gesture recognized. + */ +var ScreenTapGesture = function(data) { + /** + * The position where the screen tap is registered. + * + * @member position + * @memberof Leap.ScreenTapGesture.prototype + * @type {number[]} + */ + this.position = data.position; + /** + * The direction of finger tip motion. + * + * @member direction + * @memberof Leap.ScreenTapGesture.prototype + * @type {number[]} + */ + this.direction = data.direction; + /** + * The progess value is always 1.0 for a screen tap gesture. + * + * @member progress + * @memberof Leap.ScreenTapGesture.prototype + * @type {number} + */ + this.progress = data.progress; +} + +ScreenTapGesture.prototype.toString = function() { + return "ScreenTapGesture ["+JSON.stringify(this)+"]"; +} + +/** + * Constructs a new KeyTapGesture object. + * + * An uninitialized KeyTapGesture object is considered invalid. Get valid instances + * of the KeyTapGesture class from a Frame object. + * + * @class KeyTapGesture + * @memberof Leap + * @augments Leap.Gesture + * @classdesc + * The KeyTapGesture class represents a tapping gesture by a finger or tool. + * + * A key tap gesture is recognized when the tip of a finger rotates down toward the + * palm and then springs back to approximately the original postion, as if + * tapping. The tapping finger must pause briefly before beginning the tap. + * + * ![KeyTap](images/Leap_Gesture_Tap.png) + * + * Key tap gestures are discrete. The KeyTapGesture object representing a tap always + * has the state, STATE_STOP. Only one KeyTapGesture object is created for each + * key tap gesture recognized. + */ +var KeyTapGesture = function(data) { + /** + * The position where the key tap is registered. + * + * @member position + * @memberof Leap.KeyTapGesture.prototype + * @type {number[]} + */ + this.position = data.position; + /** + * The direction of finger tip motion. + * + * @member direction + * @memberof Leap.KeyTapGesture.prototype + * @type {number[]} + */ + this.direction = data.direction; + /** + * The progess value is always 1.0 for a key tap gesture. + * + * @member progress + * @memberof Leap.KeyTapGesture.prototype + * @type {number} + */ + this.progress = data.progress; +} + +KeyTapGesture.prototype.toString = function() { + return "KeyTapGesture ["+JSON.stringify(this)+"]"; +} + +},{"events":17,"gl-matrix":19,"underscore":20}],7:[function(require,module,exports){ +var Pointable = require("./pointable") + , glMatrix = require("gl-matrix") + , mat3 = glMatrix.mat3 + , vec3 = glMatrix.vec3 + , _ = require("underscore"); + +/** + * Constructs a Hand object. + * + * An uninitialized hand is considered invalid. + * Get valid Hand objects from a Frame object. + * @class Hand + * @memberof Leap + * @classdesc + * The Hand class reports the physical characteristics of a detected hand. + * + * Hand tracking data includes a palm position and velocity; vectors for + * the palm normal and direction to the fingers; properties of a sphere fit + * to the hand; and lists of the attached fingers and tools. + * + * Note that Hand objects can be invalid, which means that they do not contain + * valid tracking data and do not correspond to a physical entity. Invalid Hand + * objects can be the result of asking for a Hand object using an ID from an + * earlier frame when no Hand objects with that ID exist in the current frame. + * A Hand object created from the Hand constructor is also invalid. + * Test for validity with the [Hand.valid]{@link Leap.Hand#valid} property. + */ +var Hand = module.exports = function(data) { + /** + * A unique ID assigned to this Hand object, whose value remains the same + * across consecutive frames while the tracked hand remains visible. If + * tracking is lost (for example, when a hand is occluded by another hand + * or when it is withdrawn from or reaches the edge of the Leap field of view), + * the Leap may assign a new ID when it detects the hand in a future frame. + * + * Use the ID value with the {@link Frame.hand}() function to find this + * Hand object in future frames. + * + * @member id + * @memberof Leap.Hand.prototype + * @type {String} + */ + this.id = data.id; + /** + * The center position of the palm in millimeters from the Leap origin. + * @member palmPosition + * @memberof Leap.Hand.prototype + * @type {number[]} + */ + this.palmPosition = data.palmPosition; + /** + * The direction from the palm position toward the fingers. + * + * The direction is expressed as a unit vector pointing in the same + * direction as the directed line from the palm position to the fingers. + * + * @member direction + * @memberof Leap.Hand.prototype + * @type {number[]} + */ + this.direction = data.direction; + /** + * The rate of change of the palm position in millimeters/second. + * + * @member palmVeclocity + * @memberof Leap.Hand.prototype + * @type {number[]} + */ + this.palmVelocity = data.palmVelocity; + /** + * The normal vector to the palm. If your hand is flat, this vector will + * point downward, or "out" of the front surface of your palm. + * + * ![Palm Vectors](images/Leap_Palm_Vectors.png) + * + * The direction is expressed as a unit vector pointing in the same + * direction as the palm normal (that is, a vector orthogonal to the palm). + * @member palmNormal + * @memberof Leap.Hand.prototype + * @type {number[]} + */ + this.palmNormal = data.palmNormal; + /** + * The center of a sphere fit to the curvature of this hand. + * + * This sphere is placed roughly as if the hand were holding a ball. + * + * ![Hand Ball](images/Leap_Hand_Ball.png) + * @member sphereCenter + * @memberof Leap.Hand.prototype + * @type {number[]} + */ + this.sphereCenter = data.sphereCenter; + /** + * The radius of a sphere fit to the curvature of this hand, in millimeters. + * + * This sphere is placed roughly as if the hand were holding a ball. Thus the + * size of the sphere decreases as the fingers are curled into a fist. + * + * @member sphereRadius + * @memberof Leap.Hand.prototype + * @type {number} + */ + this.sphereRadius = data.sphereRadius; + /** + * Reports whether this is a valid Hand object. + * + * @member valid + * @memberof Leap.Hand.prototype + * @type {boolean} + */ + this.valid = true; + /** + * The list of Pointable objects (fingers and tools) detected in this frame + * that are associated with this hand, given in arbitrary order. The list + * can be empty if no fingers or tools associated with this hand are detected. + * + * Use the {@link Pointable} tool property to determine + * whether or not an item in the list represents a tool or finger. + * You can also get only the tools using the Hand.tools[] list or + * only the fingers using the Hand.fingers[] list. + * + * @member pointables[] + * @memberof Leap.Hand.prototype + * @type {Leap.Pointable[]} + */ + this.pointables = []; + /** + * The list of fingers detected in this frame that are attached to + * this hand, given in arbitrary order. + * + * The list can be empty if no fingers attached to this hand are detected. + * + * @member fingers[] + * @memberof Leap.Hand.prototype + * @type {Leap.Pointable[]} + */ + this.fingers = []; + /** + * The list of tools detected in this frame that are held by this + * hand, given in arbitrary order. + * + * The list can be empty if no tools held by this hand are detected. + * + * @member tools[] + * @memberof Leap.Hand.prototype + * @type {Leap.Pointable[]} + */ + this.tools = []; + this._translation = data.t; + this._rotation = _.flatten(data.r); + this._scaleFactor = data.s; + + /** + * Time the hand has been visible in seconds. + * + * @member Hand.prototype.timeVisible {float} + */ + this.timeVisible = data.timeVisible; + + /** + * The palm position with stabalization + * @member stabilizedPalmPosition + * @memberof Leap.Hand.prototype + * @type {number[]} + */ + this.stabilizedPalmPosition = data.stabilizedPalmPosition; +} + +/** + * The finger with the specified ID attached to this hand. + * + * Use this function to retrieve a Pointable object representing a finger + * attached to this hand using an ID value obtained from a previous frame. + * This function always returns a Pointable object, but if no finger + * with the specified ID is present, an invalid Pointable object is returned. + * + * Note that the ID values assigned to fingers persist across frames, but only + * until tracking of a particular finger is lost. If tracking of a finger is + * lost and subsequently regained, the new Finger object representing that + * finger may have a different ID than that representing the finger in an + * earlier frame. + * + * @method finger + * @memberof Leap.Hand.prototype + * @param {String} id The ID value of a finger from a previous frame. + * @returns {Leap.Pointable} The Finger object with + * the matching ID if one exists for this hand in this frame; otherwise, an + * invalid Finger object is returned. + */ +Hand.prototype.finger = function(id) { + var finger = this.frame.finger(id); + return (finger && finger.handId == this.id) ? finger : Pointable.Invalid; +} + +/** + * The angle of rotation around the rotation axis derived from the change in + * orientation of this hand, and any associated fingers and tools, between the + * current frame and the specified frame. + * + * The returned angle is expressed in radians measured clockwise around the + * rotation axis (using the right-hand rule) between the start and end frames. + * The value is always between 0 and pi radians (0 and 180 degrees). + * + * If a corresponding Hand object is not found in sinceFrame, or if either + * this frame or sinceFrame are invalid Frame objects, then the angle of rotation is zero. + * + * @method rotationAngle + * @memberof Leap.Hand.prototype + * @param {Leap.Frame} sinceFrame The starting frame for computing the relative rotation. + * @param {numnber[]} [axis] The axis to measure rotation around. + * @returns {number} A positive value representing the heuristically determined + * rotational change of the hand between the current frame and that specified in + * the sinceFrame parameter. + */ +Hand.prototype.rotationAngle = function(sinceFrame, axis) { + if (!this.valid || !sinceFrame.valid) return 0.0; + var sinceHand = sinceFrame.hand(this.id); + if(!sinceHand.valid) return 0.0; + var rot = this.rotationMatrix(sinceFrame); + var cs = (rot[0] + rot[4] + rot[8] - 1.0)*0.5 + var angle = Math.acos(cs); + angle = isNaN(angle) ? 0.0 : angle; + if (axis !== undefined) { + var rotAxis = this.rotationAxis(sinceFrame); + angle *= vec3.dot(rotAxis, vec3.normalize(vec3.create(), axis)); + } + return angle; +} + +/** + * The axis of rotation derived from the change in orientation of this hand, and + * any associated fingers and tools, between the current frame and the specified frame. + * + * The returned direction vector is normalized. + * + * If a corresponding Hand object is not found in sinceFrame, or if either + * this frame or sinceFrame are invalid Frame objects, then this method returns a zero vector. + * + * @method rotationAxis + * @memberof Leap.Hand.prototype + * @param {Leap.Frame} sinceFrame The starting frame for computing the relative rotation. + * @returns {number[]} A normalized direction Vector representing the axis of the heuristically determined + * rotational change of the hand between the current frame and that specified in the sinceFrame parameter. + */ +Hand.prototype.rotationAxis = function(sinceFrame) { + if (!this.valid || !sinceFrame.valid) return vec3.create(); + var sinceHand = sinceFrame.hand(this.id); + if (!sinceHand.valid) return vec3.create(); + return vec3.normalize(vec3.create(), [ + this._rotation[7] - sinceHand._rotation[5], + this._rotation[2] - sinceHand._rotation[6], + this._rotation[3] - sinceHand._rotation[1] + ]); +} + +/** + * The transform matrix expressing the rotation derived from the change in + * orientation of this hand, and any associated fingers and tools, between + * the current frame and the specified frame. + * + * If a corresponding Hand object is not found in sinceFrame, or if either + * this frame or sinceFrame are invalid Frame objects, then this method returns + * an identity matrix. + * + * @method rotationMatrix + * @memberof Leap.Hand.prototype + * @param {Leap.Frame} sinceFrame The starting frame for computing the relative rotation. + * @returns {number[]} A transformation Matrix containing the heuristically determined + * rotational change of the hand between the current frame and that specified in the sinceFrame parameter. + */ +Hand.prototype.rotationMatrix = function(sinceFrame) { + if (!this.valid || !sinceFrame.valid) return mat3.create(); + var sinceHand = sinceFrame.hand(this.id); + if(!sinceHand.valid) return mat3.create(); + var transpose = mat3.transpose(mat3.create(), this._rotation); + var m = mat3.multiply(mat3.create(), sinceHand._rotation, transpose); + return m; +} + +/** + * The scale factor derived from the hand's motion between the current frame and the specified frame. + * + * The scale factor is always positive. A value of 1.0 indicates no scaling took place. + * Values between 0.0 and 1.0 indicate contraction and values greater than 1.0 indicate expansion. + * + * The Leap derives scaling from the relative inward or outward motion of a hand + * and its associated fingers and tools (independent of translation and rotation). + * + * If a corresponding Hand object is not found in sinceFrame, or if either this frame or sinceFrame + * are invalid Frame objects, then this method returns 1.0. + * + * @method scaleFactor + * @memberof Leap.Hand.prototype + * @param {Leap.Frame} sinceFrame The starting frame for computing the relative scaling. + * @returns {number} A positive value representing the heuristically determined + * scaling change ratio of the hand between the current frame and that specified in the sinceFrame parameter. + */ +Hand.prototype.scaleFactor = function(sinceFrame) { + if (!this.valid || !sinceFrame.valid) return 1.0; + var sinceHand = sinceFrame.hand(this.id); + if(!sinceHand.valid) return 1.0; + + return Math.exp(this._scaleFactor - sinceHand._scaleFactor); +} + +/** + * The change of position of this hand between the current frame and the specified frame + * + * The returned translation vector provides the magnitude and direction of the + * movement in millimeters. + * + * If a corresponding Hand object is not found in sinceFrame, or if either this frame or + * sinceFrame are invalid Frame objects, then this method returns a zero vector. + * + * @method translation + * @memberof Leap.Hand.prototype + * @param {Leap.Frame} sinceFrame The starting frame for computing the relative translation. + * @returns {number[]} A Vector representing the heuristically determined change in hand + * position between the current frame and that specified in the sinceFrame parameter. + */ +Hand.prototype.translation = function(sinceFrame) { + if (!this.valid || !sinceFrame.valid) return vec3.create(); + var sinceHand = sinceFrame.hand(this.id); + if(!sinceHand.valid) return vec3.create(); + return [ + this._translation[0] - sinceHand._translation[0], + this._translation[1] - sinceHand._translation[1], + this._translation[2] - sinceHand._translation[2] + ]; +} + +/** + * A string containing a brief, human readable description of the Hand object. + * @method toString + * @memberof Leap.Hand.prototype + * @returns {String} A description of the Hand as a string. + */ +Hand.prototype.toString = function() { + return "Hand [ id: "+ this.id + " | palm velocity:"+this.palmVelocity+" | sphere center:"+this.sphereCenter+" ] "; +} + +/** + * The pitch angle in radians. + * + * Pitch is the angle between the negative z-axis and the projection of + * the vector onto the y-z plane. In other words, pitch represents rotation + * around the x-axis. + * If the vector points upward, the returned angle is between 0 and pi radians + * (180 degrees); if it points downward, the angle is between 0 and -pi radians. + * + * @method pitch + * @memberof Leap.Hand.prototype + * @returns {number} The angle of this vector above or below the horizon (x-z plane). + * + */ +Hand.prototype.pitch = function() { + return Math.atan2(this.direction[1], -this.direction[2]); +} + +/** + * The yaw angle in radians. + * + * Yaw is the angle between the negative z-axis and the projection of + * the vector onto the x-z plane. In other words, yaw represents rotation + * around the y-axis. If the vector points to the right of the negative z-axis, + * then the returned angle is between 0 and pi radians (180 degrees); + * if it points to the left, the angle is between 0 and -pi radians. + * + * @method yaw + * @memberof Leap.Hand.prototype + * @returns {number} The angle of this vector to the right or left of the y-axis. + * + */ +Hand.prototype.yaw = function() { + return Math.atan2(this.direction[0], -this.direction[2]); +} + +/** + * The roll angle in radians. + * + * Roll is the angle between the y-axis and the projection of + * the vector onto the x-y plane. In other words, roll represents rotation + * around the z-axis. If the vector points to the left of the y-axis, + * then the returned angle is between 0 and pi radians (180 degrees); + * if it points to the right, the angle is between 0 and -pi radians. + * + * @method roll + * @memberof Leap.Hand.prototype + * @returns {number} The angle of this vector to the right or left of the y-axis. + * + */ +Hand.prototype.roll = function() { + return Math.atan2(this.palmNormal[0], -this.palmNormal[1]); +} + +/** + * An invalid Hand object. + * + * You can use an invalid Hand object in comparisons testing + * whether a given Hand instance is valid or invalid. (You can also use the + * Hand valid property.) + * + * @static + * @type {Leap.Hand} + * @name Invalid + * @memberof Leap.Hand + */ +Hand.Invalid = { + valid: false, + fingers: [], + tools: [], + pointables: [], + pointable: function() { return Pointable.Invalid }, + finger: function() { return Pointable.Invalid }, + toString: function() { return "invalid frame" }, + dump: function() { return this.toString(); }, + rotationAngle: function() { return 0.0; }, + rotationMatrix: function() { return mat3.create(); }, + rotationAxis: function() { return vec3.create(); }, + scaleFactor: function() { return 1.0; }, + translation: function() { return vec3.create(); } +}; + +},{"./pointable":11,"gl-matrix":19,"underscore":20}],8:[function(require,module,exports){ +(function(){/** + * Leap is the global namespace of the Leap API. + * @namespace Leap + */ +module.exports = { + Controller: require("./controller"), + Frame: require("./frame"), + Gesture: require("./gesture"), + Hand: require("./hand"), + Pointable: require("./pointable"), + InteractionBox: require("./interaction_box"), + Connection: require("./connection"), + CircularBuffer: require("./circular_buffer"), + UI: require("./ui"), + glMatrix: require("gl-matrix"), + mat3: require("gl-matrix").mat3, + vec3: require("gl-matrix").vec3, + loopController: undefined, + /** + * The Leap.loop() function passes a frame of Leap data to your + * callback function and then calls window.requestAnimationFrame() after + * executing your callback function. + * + * Leap.loop() sets up the Leap controller and WebSocket connection for you. + * You do not need to create your own controller when using this method. + * + * Your callback function is called on an interval determined by the client + * browser. Typically, this is on an interval of 60 frames/second. The most + * recent frame of Leap data is passed to your callback function. If the Leap + * is producing frames at a slower rate than the browser frame rate, the same + * frame of Leap data can be passed to your function in successive animation + * updates. + * + * As an alternative, you can create your own Controller object and use a + * {@link Controller#onFrame onFrame} callback to process the data at + * the frame rate of the Leap device. See {@link Controller} for an + * example. + * + * @method Leap.loop + * @param {function} callback A function called when the browser is ready to + * draw to the screen. The most recent {@link Frame} object is passed to + * your callback function. + * + * ```javascript + * Leap.loop( function( frame ) { + * // ... your code here + * }) + * ``` + */ + loop: function(opts, callback) { + if (callback === undefined) { + callback = opts; + opts = {}; + } + if (!this.loopController) this.loopController = new this.Controller(opts); + this.loopController.loop(callback); + return this.loopController; + } +} + +})() +},{"./circular_buffer":2,"./connection":3,"./controller":4,"./frame":5,"./gesture":6,"./hand":7,"./interaction_box":9,"./pointable":11,"./ui":13,"gl-matrix":19}],9:[function(require,module,exports){ +var glMatrix = require("gl-matrix") + , vec3 = glMatrix.vec3; + +/** + * Constructs a InteractionBox object. + * + * @class InteractionBox + * @memberof Leap + * @classdesc + * The InteractionBox class represents a box-shaped region completely within + * the field of view of the Leap Motion controller. + * + * The interaction box is an axis-aligned rectangular prism and provides + * normalized coordinates for hands, fingers, and tools within this box. + * The InteractionBox class can make it easier to map positions in the + * Leap Motion coordinate system to 2D or 3D coordinate systems used + * for application drawing. + * + * The InteractionBox region is defined by a center and dimensions along the x, y, and z axes. + */ +var InteractionBox = module.exports = function(data) { + /** + * Indicates whether this is a valid InteractionBox object. + * + * @member valid + * @type {Boolean} + * @memberof Leap.InteractionBox.prototype + */ + this.valid = true; + /** + * The center of the InteractionBox in device coordinates (millimeters). + * This point is equidistant from all sides of the box. + * + * @member center + * @type {number[]} + * @memberof Leap.InteractionBox.prototype + */ + this.center = data.center; + + this.size = data.size; + /** + * The width of the InteractionBox in millimeters, measured along the x-axis. + * + * @member width + * @type {number} + * @memberof Leap.InteractionBox.prototype + */ + this.width = data.size[0]; + /** + * The height of the InteractionBox in millimeters, measured along the y-axis. + * + * @member height + * @type {number} + * @memberof Leap.InteractionBox.prototype + */ + this.height = data.size[1]; + /** + * The depth of the InteractionBox in millimeters, measured along the z-axis. + * + * @member depth + * @type {number} + * @memberof Leap.InteractionBox.prototype + */ + this.depth = data.size[2]; +} + +/** + * Converts a position defined by normalized InteractionBox coordinates + * into device coordinates in millimeters. + * + * This function performs the inverse of normalizePoint(). + * + * @method denormalizePoint + * @memberof Leap.InteractionBox.prototype + * @param {Leap.Vector} normalizedPosition The input position in InteractionBox coordinates. + * @returns {Leap.Vector} The corresponding denormalized position in device coordinates. + */ +InteractionBox.prototype.denormalizePoint = function(normalizedPosition) { + return vec3.fromValues( + (normalizedPosition[0] - 0.5) * this.size[0] + this.center[0], + (normalizedPosition[1] - 0.5) * this.size[1] + this.center[1], + (normalizedPosition[2] - 0.5) * this.size[2] + this.center[2] + ); +} + +/** + * Normalizes the coordinates of a point using the interaction box. + * + * Coordinates from the Leap Motion frame of reference (millimeters) are + * converted to a range of [0..1] such that the minimum value of the + * InteractionBox maps to 0 and the maximum value of the InteractionBox maps to 1. + * + * @method normalizePoint + * @memberof Leap.InteractionBox.prototype + * @param {Leap.Vector} position The input position in device coordinates. + * @param {Boolean} clamp Whether or not to limit the output value to the range [0,1] + * when the input position is outside the InteractionBox. Defaults to true. + * @returns {Leap.Vector} The normalized position. + */ +InteractionBox.prototype.normalizePoint = function(position, clamp) { + var vec = vec3.fromValues( + ((position[0] - this.center[0]) / this.size[0]) + 0.5, + ((position[1] - this.center[1]) / this.size[1]) + 0.5, + ((position[2] - this.center[2]) / this.size[2]) + 0.5 + ); + + if (clamp) { + vec[0] = Math.min(Math.max(vec[0], 0), 1); + vec[1] = Math.min(Math.max(vec[1], 0), 1); + vec[2] = Math.min(Math.max(vec[2], 0), 1); + } + return vec; +} + +/** + * Writes a brief, human readable description of the InteractionBox object. + * + * @method toString + * @memberof Leap.InteractionBox.prototype + * @returns {String} A description of the InteractionBox object as a string. + */ +InteractionBox.prototype.toString = function() { + return "InteractionBox [ width:" + this.width + " | height:" + this.height + " | depth:" + this.depth + " ]"; +} + +/** + * An invalid InteractionBox object. + * + * You can use this InteractionBox instance in comparisons testing + * whether a given InteractionBox instance is valid or invalid. (You can also use the + * InteractionBox.valid property.) + * + * @static + * @type {Leap.InteractionBox} + * @name Invalid + * @memberof Leap.InteractionBox + */ +InteractionBox.Invalid = { valid: false }; + +},{"gl-matrix":19}],10:[function(require,module,exports){ +var Pipeline = module.exports = function() { + this.steps = []; +} + +Pipeline.prototype.addStep = function(step) { + this.steps.push(step); +} + +Pipeline.prototype.run = function(frame) { + var stepsLength = this.steps.length; + for (var i = 0; i != stepsLength; i++) { + if (!frame) break; + frame = this.steps[i](frame); + } + return frame; +} + +},{}],11:[function(require,module,exports){ +var glMatrix = require("gl-matrix") + , vec3 = glMatrix.vec3; + +/** + * Constructs a Pointable object. + * + * An uninitialized pointable is considered invalid. + * Get valid Pointable objects from a Frame or a Hand object. + * + * @class Pointable + * @memberof Leap + * @classdesc + * The Pointable class reports the physical characteristics of a detected + * finger or tool. + * + * Both fingers and tools are classified as Pointable objects. Use the + * Pointable.tool property to determine whether a Pointable object represents a + * tool or finger. The Leap classifies a detected entity as a tool when it is + * thinner, straighter, and longer than a typical finger. + * + * Note that Pointable objects can be invalid, which means that they do not + * contain valid tracking data and do not correspond to a physical entity. + * Invalid Pointable objects can be the result of asking for a Pointable object + * using an ID from an earlier frame when no Pointable objects with that ID + * exist in the current frame. A Pointable object created from the Pointable + * constructor is also invalid. Test for validity with the Pointable.valid + * property. + */ +var Pointable = module.exports = function(data) { + /** + * Indicates whether this is a valid Pointable object. + * + * @member valid + * @type {Boolean} + * @memberof Leap.Pointable.prototype + */ + this.valid = true; + /** + * A unique ID assigned to this Pointable object, whose value remains the + * same across consecutive frames while the tracked finger or tool remains + * visible. If tracking is lost (for example, when a finger is occluded by + * another finger or when it is withdrawn from the Leap field of view), the + * Leap may assign a new ID when it detects the entity in a future frame. + * + * Use the ID value with the pointable() functions defined for the + * {@link Frame} and {@link Frame.Hand} classes to find this + * Pointable object in future frames. + * + * @member id + * @type {String} + * @memberof Leap.Pointable.prototype + */ + this.id = data.id; + this.handId = data.handId; + /** + * The estimated length of the finger or tool in millimeters. + * + * The reported length is the visible length of the finger or tool from the + * hand to tip. If the length isn't known, then a value of 0 is returned. + * + * @member length + * @type {number} + * @memberof Leap.Pointable.prototype + */ + this.length = data.length; + /** + * Whether or not the Pointable is believed to be a tool. + * Tools are generally longer, thinner, and straighter than fingers. + * + * If tool is false, then this Pointable must be a finger. + * + * @member tool + * @type {Boolean} + * @memberof Leap.Pointable.prototype + */ + this.tool = data.tool; + /** + * The estimated width of the tool in millimeters. + * + * The reported width is the average width of the visible portion of the + * tool from the hand to the tip. If the width isn't known, + * then a value of 0 is returned. + * + * Pointable objects representing fingers do not have a width property. + * + * @member width + * @type {number} + * @memberof Leap.Pointable.prototype + */ + this.width = data.width; + /** + * The direction in which this finger or tool is pointing. + * + * The direction is expressed as a unit vector pointing in the same + * direction as the tip. + * + * ![Finger](images/Leap_Finger_Model.png) + * @member direction + * @type {number[]} + * @memberof Leap.Pointable.prototype + */ + this.direction = data.direction; + /** + * The tip position in millimeters from the Leap origin. + * Stabilized + * + * @member stabilizedTipPosition + * @type {Leap.Vector} + * @memberof Leap.Pointable.prototype + */ + this.stabilizedTipPosition = data.stabilizedTipPosition; + /** + * The tip position in millimeters from the Leap origin. + * + * @member tipPosition + * @type {number[]} + * @memberof Leap.Pointable.prototype + */ + this.tipPosition = data.tipPosition; + /** + * The rate of change of the tip position in millimeters/second. + * + * @member tipVelocity + * @type {number[]} + * @memberof Leap.Pointable.prototype + */ + this.tipVelocity = data.tipVelocity; + /** + * Human readable string describing the 'Touch Zone' of this pointable + * + * @member Pointable.prototype.touchZone {String} + */ + this.touchZone = data.touchZone; + /** + * Distance from 'Touch Plane' + * + * @member Pointable.prototype.touchDistance {number} + */ + this.touchDistance = data.touchDistance; + + /** + * Time the pointable has been visible in seconds. + * + * @member Pointable.prototype.timeVisible {float} + */ + this.timeVisible = data.timeVisible; +} + +/** + * A string containing a brief, human readable description of the Pointable + * object. + * + * @method toString + * @memberof Leap.Pointable.prototype + * @returns {String} A description of the Pointable object as a string. + */ +Pointable.prototype.toString = function() { + if(this.tool == true){ + return "Pointable [ id:" + this.id + " " + this.length + "mmx | with:" + this.width + "mm | direction:" + this.direction + ' ]'; + } else { + return "Pointable [ id:" + this.id + " " + this.length + "mmx | direction: " + this.direction + ' ]'; + } +} + +/** + * An invalid Pointable object. + * + * You can use this Pointable instance in comparisons testing + * whether a given Pointable instance is valid or invalid. (You can also use the + * Pointable.valid property.) + + * @static + * @type {Leap.Pointable} + * @name Invalid + * @memberof Leap.Pointable + */ +Pointable.Invalid = { valid: false }; + +},{"gl-matrix":19}],12:[function(require,module,exports){ +var Frame = require('./frame') + +var Event = function(data) { + this.type = data.type; + this.state = data.state; +}; + +var chooseProtocol = exports.chooseProtocol = function(header) { + var protocol; + switch(header.version) { + case 1: + protocol = JSONProtocol(1, function(data) { + return new Frame(data); + }); + break; + case 2: + protocol = JSONProtocol(2, function(data) { + return new Frame(data); + }); + protocol.sendHeartbeat = function(connection) { + connection.send(protocol.encode({heartbeat: true})); + } + break; + case 3: + protocol = JSONProtocol(3, function(data) { + return data.event ? new Event(data.event) : new Frame(data); + + }); + protocol.sendHeartbeat = function(connection) { + connection.send(protocol.encode({heartbeat: true})); + } + break; + default: + throw "unrecognized version"; + } + return protocol; +} + +var JSONProtocol = function(version, cb) { + var protocol = cb; + protocol.encode = function(message) { + return JSON.stringify(message); + } + protocol.version = version; + protocol.versionLong = 'Version ' + version; + protocol.type = 'protocol'; + return protocol; +}; + +},{"./frame":5}],13:[function(require,module,exports){ +exports.UI = { + Region: require("./ui/region"), + Cursor: require("./ui/cursor") +}; +},{"./ui/cursor":14,"./ui/region":15}],14:[function(require,module,exports){ +var Cursor = module.exports = function() { + return function(frame) { + var pointable = frame.pointables.sort(function(a, b) { return a.z - b.z })[0] + if (pointable && pointable.valid) { + frame.cursorPosition = pointable.tipPosition + } + return frame + } +} + +},{}],15:[function(require,module,exports){ +var EventEmitter = require('events').EventEmitter + , _ = require('underscore') + +var Region = module.exports = function(start, end) { + this.start = new Vector(start) + this.end = new Vector(end) + this.enteredFrame = null +} + +Region.prototype.hasPointables = function(frame) { + for (var i = 0; i != frame.pointables.length; i++) { + var position = frame.pointables[i].tipPosition + if (position.x >= this.start.x && position.x <= this.end.x && position.y >= this.start.y && position.y <= this.end.y && position.z >= this.start.z && position.z <= this.end.z) { + return true + } + } + return false +} + +Region.prototype.listener = function(opts) { + var region = this + if (opts && opts.nearThreshold) this.setupNearRegion(opts.nearThreshold) + return function(frame) { + return region.updatePosition(frame) + } +} + +Region.prototype.clipper = function() { + var region = this + return function(frame) { + region.updatePosition(frame) + return region.enteredFrame ? frame : null + } +} + +Region.prototype.setupNearRegion = function(distance) { + var nearRegion = this.nearRegion = new Region( + [this.start.x - distance, this.start.y - distance, this.start.z - distance], + [this.end.x + distance, this.end.y + distance, this.end.z + distance] + ) + var region = this + nearRegion.on("enter", function(frame) { + region.emit("near", frame) + }) + nearRegion.on("exit", function(frame) { + region.emit("far", frame) + }) + region.on('exit', function(frame) { + region.emit("near", frame) + }) +} + +Region.prototype.updatePosition = function(frame) { + if (this.nearRegion) this.nearRegion.updatePosition(frame) + if (this.hasPointables(frame) && this.enteredFrame == null) { + this.enteredFrame = frame + this.emit("enter", this.enteredFrame) + } else if (!this.hasPointables(frame) && this.enteredFrame != null) { + this.enteredFrame = null + this.emit("exit", this.enteredFrame) + } + return frame +} + +Region.prototype.normalize = function(position) { + return new Vector([ + (position.x - this.start.x) / (this.end.x - this.start.x), + (position.y - this.start.y) / (this.end.y - this.start.y), + (position.z - this.start.z) / (this.end.z - this.start.z) + ]) +} + +Region.prototype.mapToXY = function(position, width, height) { + var normalized = this.normalize(position) + var x = normalized.x, y = normalized.y + if (x > 1) x = 1 + else if (x < -1) x = -1 + if (y > 1) y = 1 + else if (y < -1) y = -1 + return [ + (x + 1) / 2 * width, + (1 - y) / 2 * height, + normalized.z + ] +} + +_.extend(Region.prototype, EventEmitter.prototype) +},{"events":17,"underscore":20}],16:[function(require,module,exports){ + +},{}],17:[function(require,module,exports){ +(function(process){if (!process.EventEmitter) process.EventEmitter = function () {}; + +var EventEmitter = exports.EventEmitter = process.EventEmitter; +var isArray = typeof Array.isArray === 'function' + ? Array.isArray + : function (xs) { + return Object.prototype.toString.call(xs) === '[object Array]' + } +; +function indexOf (xs, x) { + if (xs.indexOf) return xs.indexOf(x); + for (var i = 0; i < xs.length; i++) { + if (x === xs[i]) return i; + } + return -1; +} + +// By default EventEmitters will print a warning if more than +// 10 listeners are added to it. This is a useful default which +// helps finding memory leaks. +// +// Obviously not all Emitters should be limited to 10. This function allows +// that to be increased. Set to zero for unlimited. +var defaultMaxListeners = 10; +EventEmitter.prototype.setMaxListeners = function(n) { + if (!this._events) this._events = {}; + this._events.maxListeners = n; +}; + + +EventEmitter.prototype.emit = function(type) { + // If there is no 'error' event listener then throw. + if (type === 'error') { + if (!this._events || !this._events.error || + (isArray(this._events.error) && !this._events.error.length)) + { + if (arguments[1] instanceof Error) { + throw arguments[1]; // Unhandled 'error' event + } else { + throw new Error("Uncaught, unspecified 'error' event."); + } + return false; + } + } + + if (!this._events) return false; + var handler = this._events[type]; + if (!handler) return false; + + if (typeof handler == 'function') { + switch (arguments.length) { + // fast cases + case 1: + handler.call(this); + break; + case 2: + handler.call(this, arguments[1]); + break; + case 3: + handler.call(this, arguments[1], arguments[2]); + break; + // slower + default: + var args = Array.prototype.slice.call(arguments, 1); + handler.apply(this, args); + } + return true; + + } else if (isArray(handler)) { + var args = Array.prototype.slice.call(arguments, 1); + + var listeners = handler.slice(); + for (var i = 0, l = listeners.length; i < l; i++) { + listeners[i].apply(this, args); + } + return true; + + } else { + return false; + } +}; + +// EventEmitter is defined in src/node_events.cc +// EventEmitter.prototype.emit() is also defined there. +EventEmitter.prototype.addListener = function(type, listener) { + if ('function' !== typeof listener) { + throw new Error('addListener only takes instances of Function'); + } + + if (!this._events) this._events = {}; + + // To avoid recursion in the case that type == "newListeners"! Before + // adding it to the listeners, first emit "newListeners". + this.emit('newListener', type, listener); + + if (!this._events[type]) { + // Optimize the case of one listener. Don't need the extra array object. + this._events[type] = listener; + } else if (isArray(this._events[type])) { + + // Check for listener leak + if (!this._events[type].warned) { + var m; + if (this._events.maxListeners !== undefined) { + m = this._events.maxListeners; + } else { + m = defaultMaxListeners; + } + + if (m && m > 0 && this._events[type].length > m) { + this._events[type].warned = true; + console.error('(node) warning: possible EventEmitter memory ' + + 'leak detected. %d listeners added. ' + + 'Use emitter.setMaxListeners() to increase limit.', + this._events[type].length); + console.trace(); + } + } + + // If we've already got an array, just append. + this._events[type].push(listener); + } else { + // Adding the second element, need to change to array. + this._events[type] = [this._events[type], listener]; + } + + return this; +}; + +EventEmitter.prototype.on = EventEmitter.prototype.addListener; + +EventEmitter.prototype.once = function(type, listener) { + var self = this; + self.on(type, function g() { + self.removeListener(type, g); + listener.apply(this, arguments); + }); + + return this; +}; + +EventEmitter.prototype.removeListener = function(type, listener) { + if ('function' !== typeof listener) { + throw new Error('removeListener only takes instances of Function'); + } + + // does not use listeners(), so no side effect of creating _events[type] + if (!this._events || !this._events[type]) return this; + + var list = this._events[type]; + + if (isArray(list)) { + var i = indexOf(list, listener); + if (i < 0) return this; + list.splice(i, 1); + if (list.length == 0) + delete this._events[type]; + } else if (this._events[type] === listener) { + delete this._events[type]; + } + + return this; +}; + +EventEmitter.prototype.removeAllListeners = function(type) { + if (arguments.length === 0) { + this._events = {}; + return this; + } + + // does not use listeners(), so no side effect of creating _events[type] + if (type && this._events && this._events[type]) this._events[type] = null; + return this; +}; + +EventEmitter.prototype.listeners = function(type) { + if (!this._events) this._events = {}; + if (!this._events[type]) this._events[type] = []; + if (!isArray(this._events[type])) { + this._events[type] = [this._events[type]]; + } + return this._events[type]; +}; + +})(require("__browserify_process")) +},{"__browserify_process":18}],18:[function(require,module,exports){ +// shim for using process in browser + +var process = module.exports = {}; + +process.nextTick = (function () { + var canSetImmediate = typeof window !== 'undefined' + && window.setImmediate; + var canPost = typeof window !== 'undefined' + && window.postMessage && window.addEventListener + ; + + if (canSetImmediate) { + return function (f) { return window.setImmediate(f) }; + } + + if (canPost) { + var queue = []; + window.addEventListener('message', function (ev) { + if (ev.source === window && ev.data === 'process-tick') { + ev.stopPropagation(); + if (queue.length > 0) { + var fn = queue.shift(); + fn(); + } + } + }, true); + + return function nextTick(fn) { + queue.push(fn); + window.postMessage('process-tick', '*'); + }; + } + + return function nextTick(fn) { + setTimeout(fn, 0); + }; +})(); + +process.title = 'browser'; +process.browser = true; +process.env = {}; +process.argv = []; + +process.binding = function (name) { + throw new Error('process.binding is not supported'); +} + +// TODO(shtylman) +process.cwd = function () { return '/' }; +process.chdir = function (dir) { + throw new Error('process.chdir is not supported'); +}; + +},{}],19:[function(require,module,exports){ +(function(){/** + * @fileoverview gl-matrix - High performance matrix and vector operations + * @author Brandon Jones + * @author Colin MacKenzie IV + * @version 2.0.0 + */ + +/* Copyright (c) 2012, Brandon Jones, Colin MacKenzie IV. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + + +(function() { + "use strict"; + + var shim = {}; + if (typeof(exports) === 'undefined') { + if(typeof define == 'function' && typeof define.amd == 'object' && define.amd) { + shim.exports = {}; + define(function() { + return shim.exports; + }); + } else { + // gl-matrix lives in a browser, define its namespaces in global + shim.exports = window; + } + } + else { + // gl-matrix lives in commonjs, define its namespaces in exports + shim.exports = exports; + } + + (function(exports) { + /* Copyright (c) 2012, Brandon Jones, Colin MacKenzie IV. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +/** + * @class 2 Dimensional Vector + * @name vec2 + */ + +var vec2 = {}; + +if(!GLMAT_EPSILON) { + var GLMAT_EPSILON = 0.000001; +} + +/** + * Creates a new, empty vec2 + * + * @returns {vec2} a new 2D vector + */ +vec2.create = function() { + return new Float32Array(2); +}; + +/** + * Creates a new vec2 initialized with values from an existing vector + * + * @param {vec2} a vector to clone + * @returns {vec2} a new 2D vector + */ +vec2.clone = function(a) { + var out = new Float32Array(2); + out[0] = a[0]; + out[1] = a[1]; + return out; +}; + +/** + * Creates a new vec2 initialized with the given values + * + * @param {Number} x X component + * @param {Number} y Y component + * @returns {vec2} a new 2D vector + */ +vec2.fromValues = function(x, y) { + var out = new Float32Array(2); + out[0] = x; + out[1] = y; + return out; +}; + +/** + * Copy the values from one vec2 to another + * + * @param {vec2} out the receiving vector + * @param {vec2} a the source vector + * @returns {vec2} out + */ +vec2.copy = function(out, a) { + out[0] = a[0]; + out[1] = a[1]; + return out; +}; + +/** + * Set the components of a vec2 to the given values + * + * @param {vec2} out the receiving vector + * @param {Number} x X component + * @param {Number} y Y component + * @returns {vec2} out + */ +vec2.set = function(out, x, y) { + out[0] = x; + out[1] = y; + return out; +}; + +/** + * Adds two vec2's + * + * @param {vec2} out the receiving vector + * @param {vec2} a the first operand + * @param {vec2} b the second operand + * @returns {vec2} out + */ +vec2.add = function(out, a, b) { + out[0] = a[0] + b[0]; + out[1] = a[1] + b[1]; + return out; +}; + +/** + * Subtracts two vec2's + * + * @param {vec2} out the receiving vector + * @param {vec2} a the first operand + * @param {vec2} b the second operand + * @returns {vec2} out + */ +vec2.sub = vec2.subtract = function(out, a, b) { + out[0] = a[0] - b[0]; + out[1] = a[1] - b[1]; + return out; +}; + +/** + * Multiplies two vec2's + * + * @param {vec2} out the receiving vector + * @param {vec2} a the first operand + * @param {vec2} b the second operand + * @returns {vec2} out + */ +vec2.mul = vec2.multiply = function(out, a, b) { + out[0] = a[0] * b[0]; + out[1] = a[1] * b[1]; + return out; +}; + +/** + * Divides two vec2's + * + * @param {vec2} out the receiving vector + * @param {vec2} a the first operand + * @param {vec2} b the second operand + * @returns {vec2} out + */ +vec2.div = vec2.divide = function(out, a, b) { + out[0] = a[0] / b[0]; + out[1] = a[1] / b[1]; + return out; +}; + +/** + * Returns the minimum of two vec2's + * + * @param {vec2} out the receiving vector + * @param {vec2} a the first operand + * @param {vec2} b the second operand + * @returns {vec2} out + */ +vec2.min = function(out, a, b) { + out[0] = Math.min(a[0], b[0]); + out[1] = Math.min(a[1], b[1]); + return out; +}; + +/** + * Returns the maximum of two vec2's + * + * @param {vec2} out the receiving vector + * @param {vec2} a the first operand + * @param {vec2} b the second operand + * @returns {vec2} out + */ +vec2.max = function(out, a, b) { + out[0] = Math.max(a[0], b[0]); + out[1] = Math.max(a[1], b[1]); + return out; +}; + +/** + * Scales a vec2 by a scalar number + * + * @param {vec2} out the receiving vector + * @param {vec2} a the vector to scale + * @param {vec2} b amount to scale the vector by + * @returns {vec2} out + */ +vec2.scale = function(out, a, b) { + out[0] = a[0] * b; + out[1] = a[1] * b; + return out; +}; + +/** + * Calculates the euclidian distance between two vec2's + * + * @param {vec2} a the first operand + * @param {vec2} b the second operand + * @returns {Number} distance between a and b + */ +vec2.dist = vec2.distance = function(a, b) { + var x = b[0] - a[0], + y = b[1] - a[1]; + return Math.sqrt(x*x + y*y); +}; + +/** + * Calculates the squared euclidian distance between two vec2's + * + * @param {vec2} a the first operand + * @param {vec2} b the second operand + * @returns {Number} squared distance between a and b + */ +vec2.sqrDist = vec2.squaredDistance = function(a, b) { + var x = b[0] - a[0], + y = b[1] - a[1]; + return x*x + y*y; +}; + +/** + * Caclulates the length of a vec2 + * + * @param {vec2} a vector to calculate length of + * @returns {Number} length of a + */ +vec2.len = vec2.length = function (a) { + var x = a[0], + y = a[1]; + return Math.sqrt(x*x + y*y); +}; + +/** + * Caclulates the squared length of a vec2 + * + * @param {vec2} a vector to calculate squared length of + * @returns {Number} squared length of a + */ +vec2.sqrLen = vec2.squaredLength = function (a) { + var x = a[0], + y = a[1]; + return x*x + y*y; +}; + +/** + * Negates the components of a vec2 + * + * @param {vec2} out the receiving vector + * @param {vec2} a vector to negate + * @returns {vec2} out + */ +vec2.negate = function(out, a) { + out[0] = -a[0]; + out[1] = -a[1]; + return out; +}; + +/** + * Normalize a vec2 + * + * @param {vec2} out the receiving vector + * @param {vec2} a vector to normalize + * @returns {vec2} out + */ +vec2.normalize = function(out, a) { + var x = a[0], + y = a[1]; + var len = x*x + y*y; + if (len > 0) { + //TODO: evaluate use of glm_invsqrt here? + len = 1 / Math.sqrt(len); + out[0] = a[0] * len; + out[1] = a[1] * len; + } + return out; +}; + +/** + * Caclulates the dot product of two vec2's + * + * @param {vec2} a the first operand + * @param {vec2} b the second operand + * @returns {Number} dot product of a and b + */ +vec2.dot = function (a, b) { + return a[0] * b[0] + a[1] * b[1]; +}; + +/** + * Computes the cross product of two vec2's + * Note that the cross product must by definition produce a 3D vector + * + * @param {vec3} out the receiving vector + * @param {vec2} a the first operand + * @param {vec2} b the second operand + * @returns {vec3} out + */ +vec2.cross = function(out, a, b) { + var z = a[0] * b[1] - a[1] * b[0]; + out[0] = out[1] = 0; + out[2] = z; + return out; +}; + +/** + * Performs a linear interpolation between two vec2's + * + * @param {vec3} out the receiving vector + * @param {vec2} a the first operand + * @param {vec2} b the second operand + * @param {Number} t interpolation amount between the two inputs + * @returns {vec2} out + */ +vec2.lerp = function (out, a, b, t) { + var ax = a[0], + ay = a[1]; + out[0] = ax + t * (b[0] - ax); + out[1] = ay + t * (b[1] - ay); + return out; +}; + +/** + * Transforms the vec2 with a mat2 + * + * @param {vec2} out the receiving vector + * @param {vec2} a the vector to transform + * @param {mat2} m matrix to transform with + * @returns {vec2} out + */ +vec2.transformMat2 = function(out, a, m) { + var x = a[0], + y = a[1]; + out[0] = x * m[0] + y * m[1]; + out[1] = x * m[2] + y * m[3]; + return out; +}; + +/** + * Perform some operation over an array of vec2s. + * + * @param {Array} a the array of vectors to iterate over + * @param {Number} stride Number of elements between the start of each vec2. If 0 assumes tightly packed + * @param {Number} offset Number of elements to skip at the beginning of the array + * @param {Number} count Number of vec2s to iterate over. If 0 iterates over entire array + * @param {Function} fn Function to call for each vector in the array + * @param {Object} [arg] additional argument to pass to fn + * @returns {Array} a + */ +vec2.forEach = (function() { + var vec = new Float32Array(2); + + return function(a, stride, offset, count, fn, arg) { + var i, l; + if(!stride) { + stride = 2; + } + + if(!offset) { + offset = 0; + } + + if(count) { + l = Math.min((count * stride) + offset, a.length); + } else { + l = a.length; + } + + for(i = offset; i < l; i += stride) { + vec[0] = a[i]; vec[1] = a[i+1]; + fn(vec, vec, arg); + a[i] = vec[0]; a[i+1] = vec[1]; + } + + return a; + }; +})(); + +/** + * Returns a string representation of a vector + * + * @param {vec2} vec vector to represent as a string + * @returns {String} string representation of the vector + */ +vec2.str = function (a) { + return 'vec2(' + a[0] + ', ' + a[1] + ')'; +}; + +if(typeof(exports) !== 'undefined') { + exports.vec2 = vec2; +} +; +/* Copyright (c) 2012, Brandon Jones, Colin MacKenzie IV. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +/** + * @class 3 Dimensional Vector + * @name vec3 + */ + +var vec3 = {}; + +if(!GLMAT_EPSILON) { + var GLMAT_EPSILON = 0.000001; +} + +/** + * Creates a new, empty vec3 + * + * @returns {vec3} a new 3D vector + */ +vec3.create = function() { + return new Float32Array(3); +}; + +/** + * Creates a new vec3 initialized with values from an existing vector + * + * @param {vec3} a vector to clone + * @returns {vec3} a new 3D vector + */ +vec3.clone = function(a) { + var out = new Float32Array(3); + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + return out; +}; + +/** + * Creates a new vec3 initialized with the given values + * + * @param {Number} x X component + * @param {Number} y Y component + * @param {Number} z Z component + * @returns {vec3} a new 3D vector + */ +vec3.fromValues = function(x, y, z) { + var out = new Float32Array(3); + out[0] = x; + out[1] = y; + out[2] = z; + return out; +}; + +/** + * Copy the values from one vec3 to another + * + * @param {vec3} out the receiving vector + * @param {vec3} a the source vector + * @returns {vec3} out + */ +vec3.copy = function(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + return out; +}; + +/** + * Set the components of a vec3 to the given values + * + * @param {vec3} out the receiving vector + * @param {Number} x X component + * @param {Number} y Y component + * @param {Number} z Z component + * @returns {vec3} out + */ +vec3.set = function(out, x, y, z) { + out[0] = x; + out[1] = y; + out[2] = z; + return out; +}; + +/** + * Adds two vec3's + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {vec3} out + */ +vec3.add = function(out, a, b) { + out[0] = a[0] + b[0]; + out[1] = a[1] + b[1]; + out[2] = a[2] + b[2]; + return out; +}; + +/** + * Subtracts two vec3's + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {vec3} out + */ +vec3.sub = vec3.subtract = function(out, a, b) { + out[0] = a[0] - b[0]; + out[1] = a[1] - b[1]; + out[2] = a[2] - b[2]; + return out; +}; + +/** + * Multiplies two vec3's + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {vec3} out + */ +vec3.mul = vec3.multiply = function(out, a, b) { + out[0] = a[0] * b[0]; + out[1] = a[1] * b[1]; + out[2] = a[2] * b[2]; + return out; +}; + +/** + * Divides two vec3's + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {vec3} out + */ +vec3.div = vec3.divide = function(out, a, b) { + out[0] = a[0] / b[0]; + out[1] = a[1] / b[1]; + out[2] = a[2] / b[2]; + return out; +}; + +/** + * Returns the minimum of two vec3's + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {vec3} out + */ +vec3.min = function(out, a, b) { + out[0] = Math.min(a[0], b[0]); + out[1] = Math.min(a[1], b[1]); + out[2] = Math.min(a[2], b[2]); + return out; +}; + +/** + * Returns the maximum of two vec3's + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {vec3} out + */ +vec3.max = function(out, a, b) { + out[0] = Math.max(a[0], b[0]); + out[1] = Math.max(a[1], b[1]); + out[2] = Math.max(a[2], b[2]); + return out; +}; + +/** + * Scales a vec3 by a scalar number + * + * @param {vec3} out the receiving vector + * @param {vec3} a the vector to scale + * @param {vec3} b amount to scale the vector by + * @returns {vec3} out + */ +vec3.scale = function(out, a, b) { + out[0] = a[0] * b; + out[1] = a[1] * b; + out[2] = a[2] * b; + return out; +}; + +/** + * Calculates the euclidian distance between two vec3's + * + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {Number} distance between a and b + */ +vec3.dist = vec3.distance = function(a, b) { + var x = b[0] - a[0], + y = b[1] - a[1], + z = b[2] - a[2]; + return Math.sqrt(x*x + y*y + z*z); +}; + +/** + * Calculates the squared euclidian distance between two vec3's + * + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {Number} squared distance between a and b + */ +vec3.sqrDist = vec3.squaredDistance = function(a, b) { + var x = b[0] - a[0], + y = b[1] - a[1], + z = b[2] - a[2]; + return x*x + y*y + z*z; +}; + +/** + * Caclulates the length of a vec3 + * + * @param {vec3} a vector to calculate length of + * @returns {Number} length of a + */ +vec3.len = vec3.length = function (a) { + var x = a[0], + y = a[1], + z = a[2]; + return Math.sqrt(x*x + y*y + z*z); +}; + +/** + * Caclulates the squared length of a vec3 + * + * @param {vec3} a vector to calculate squared length of + * @returns {Number} squared length of a + */ +vec3.sqrLen = vec3.squaredLength = function (a) { + var x = a[0], + y = a[1], + z = a[2]; + return x*x + y*y + z*z; +}; + +/** + * Negates the components of a vec3 + * + * @param {vec3} out the receiving vector + * @param {vec3} a vector to negate + * @returns {vec3} out + */ +vec3.negate = function(out, a) { + out[0] = -a[0]; + out[1] = -a[1]; + out[2] = -a[2]; + return out; +}; + +/** + * Normalize a vec3 + * + * @param {vec3} out the receiving vector + * @param {vec3} a vector to normalize + * @returns {vec3} out + */ +vec3.normalize = function(out, a) { + var x = a[0], + y = a[1], + z = a[2]; + var len = x*x + y*y + z*z; + if (len > 0) { + //TODO: evaluate use of glm_invsqrt here? + len = 1 / Math.sqrt(len); + out[0] = a[0] * len; + out[1] = a[1] * len; + out[2] = a[2] * len; + } + return out; +}; + +/** + * Caclulates the dot product of two vec3's + * + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {Number} dot product of a and b + */ +vec3.dot = function (a, b) { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; +}; + +/** + * Computes the cross product of two vec3's + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {vec3} out + */ +vec3.cross = function(out, a, b) { + var ax = a[0], ay = a[1], az = a[2], + bx = b[0], by = b[1], bz = b[2]; + + out[0] = ay * bz - az * by; + out[1] = az * bx - ax * bz; + out[2] = ax * by - ay * bx; + return out; +}; + +/** + * Performs a linear interpolation between two vec3's + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @param {Number} t interpolation amount between the two inputs + * @returns {vec3} out + */ +vec3.lerp = function (out, a, b, t) { + var ax = a[0], + ay = a[1], + az = a[2]; + out[0] = ax + t * (b[0] - ax); + out[1] = ay + t * (b[1] - ay); + out[2] = az + t * (b[2] - az); + return out; +}; + +/** + * Transforms the vec3 with a mat4. + * 4th vector component is implicitly '1' + * + * @param {vec3} out the receiving vector + * @param {vec3} a the vector to transform + * @param {mat4} m matrix to transform with + * @returns {vec3} out + */ +vec3.transformMat4 = function(out, a, m) { + var x = a[0], y = a[1], z = a[2]; + out[0] = m[0] * x + m[4] * y + m[8] * z + m[12]; + out[1] = m[1] * x + m[5] * y + m[9] * z + m[13]; + out[2] = m[2] * x + m[6] * y + m[10] * z + m[14]; + return out; +}; + +/** + * Transforms the vec3 with a quat + * + * @param {vec3} out the receiving vector + * @param {vec3} a the vector to transform + * @param {quat} q quaternion to transform with + * @returns {vec3} out + */ +vec3.transformQuat = function(out, a, q) { + var x = a[0], y = a[1], z = a[2], + qx = q[0], qy = q[1], qz = q[2], qw = q[3], + + // calculate quat * vec + ix = qw * x + qy * z - qz * y, + iy = qw * y + qz * x - qx * z, + iz = qw * z + qx * y - qy * x, + iw = -qx * x - qy * y - qz * z; + + // calculate result * inverse quat + out[0] = ix * qw + iw * -qx + iy * -qz - iz * -qy; + out[1] = iy * qw + iw * -qy + iz * -qx - ix * -qz; + out[2] = iz * qw + iw * -qz + ix * -qy - iy * -qx; + return out; +}; + +/** + * Perform some operation over an array of vec3s. + * + * @param {Array} a the array of vectors to iterate over + * @param {Number} stride Number of elements between the start of each vec3. If 0 assumes tightly packed + * @param {Number} offset Number of elements to skip at the beginning of the array + * @param {Number} count Number of vec3s to iterate over. If 0 iterates over entire array + * @param {Function} fn Function to call for each vector in the array + * @param {Object} [arg] additional argument to pass to fn + * @returns {Array} a + */ +vec3.forEach = (function() { + var vec = new Float32Array(3); + + return function(a, stride, offset, count, fn, arg) { + var i, l; + if(!stride) { + stride = 3; + } + + if(!offset) { + offset = 0; + } + + if(count) { + l = Math.min((count * stride) + offset, a.length); + } else { + l = a.length; + } + + for(i = offset; i < l; i += stride) { + vec[0] = a[i]; vec[1] = a[i+1]; vec[2] = a[i+2]; + fn(vec, vec, arg); + a[i] = vec[0]; a[i+1] = vec[1]; a[i+2] = vec[2]; + } + + return a; + }; +})(); + +/** + * Returns a string representation of a vector + * + * @param {vec3} vec vector to represent as a string + * @returns {String} string representation of the vector + */ +vec3.str = function (a) { + return 'vec3(' + a[0] + ', ' + a[1] + ', ' + a[2] + ')'; +}; + +if(typeof(exports) !== 'undefined') { + exports.vec3 = vec3; +} +; +/* Copyright (c) 2012, Brandon Jones, Colin MacKenzie IV. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +/** + * @class 4 Dimensional Vector + * @name vec4 + */ + +var vec4 = {}; + +if(!GLMAT_EPSILON) { + var GLMAT_EPSILON = 0.000001; +} + +/** + * Creates a new, empty vec4 + * + * @returns {vec4} a new 4D vector + */ +vec4.create = function() { + return new Float32Array(4); +}; + +/** + * Creates a new vec4 initialized with values from an existing vector + * + * @param {vec4} a vector to clone + * @returns {vec4} a new 4D vector + */ +vec4.clone = function(a) { + var out = new Float32Array(4); + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + return out; +}; + +/** + * Creates a new vec4 initialized with the given values + * + * @param {Number} x X component + * @param {Number} y Y component + * @param {Number} z Z component + * @param {Number} w W component + * @returns {vec4} a new 4D vector + */ +vec4.fromValues = function(x, y, z, w) { + var out = new Float32Array(4); + out[0] = x; + out[1] = y; + out[2] = z; + out[3] = w; + return out; +}; + +/** + * Copy the values from one vec4 to another + * + * @param {vec4} out the receiving vector + * @param {vec4} a the source vector + * @returns {vec4} out + */ +vec4.copy = function(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + return out; +}; + +/** + * Set the components of a vec4 to the given values + * + * @param {vec4} out the receiving vector + * @param {Number} x X component + * @param {Number} y Y component + * @param {Number} z Z component + * @param {Number} w W component + * @returns {vec4} out + */ +vec4.set = function(out, x, y, z, w) { + out[0] = x; + out[1] = y; + out[2] = z; + out[3] = w; + return out; +}; + +/** + * Adds two vec4's + * + * @param {vec4} out the receiving vector + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {vec4} out + */ +vec4.add = function(out, a, b) { + out[0] = a[0] + b[0]; + out[1] = a[1] + b[1]; + out[2] = a[2] + b[2]; + out[3] = a[3] + b[3]; + return out; +}; + +/** + * Subtracts two vec4's + * + * @param {vec4} out the receiving vector + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {vec4} out + */ +vec4.sub = vec4.subtract = function(out, a, b) { + out[0] = a[0] - b[0]; + out[1] = a[1] - b[1]; + out[2] = a[2] - b[2]; + out[3] = a[3] - b[3]; + return out; +}; + +/** + * Multiplies two vec4's + * + * @param {vec4} out the receiving vector + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {vec4} out + */ +vec4.mul = vec4.multiply = function(out, a, b) { + out[0] = a[0] * b[0]; + out[1] = a[1] * b[1]; + out[2] = a[2] * b[2]; + out[3] = a[3] * b[3]; + return out; +}; + +/** + * Divides two vec4's + * + * @param {vec4} out the receiving vector + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {vec4} out + */ +vec4.div = vec4.divide = function(out, a, b) { + out[0] = a[0] / b[0]; + out[1] = a[1] / b[1]; + out[2] = a[2] / b[2]; + out[3] = a[3] / b[3]; + return out; +}; + +/** + * Returns the minimum of two vec4's + * + * @param {vec4} out the receiving vector + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {vec4} out + */ +vec4.min = function(out, a, b) { + out[0] = Math.min(a[0], b[0]); + out[1] = Math.min(a[1], b[1]); + out[2] = Math.min(a[2], b[2]); + out[3] = Math.min(a[3], b[3]); + return out; +}; + +/** + * Returns the maximum of two vec4's + * + * @param {vec4} out the receiving vector + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {vec4} out + */ +vec4.max = function(out, a, b) { + out[0] = Math.max(a[0], b[0]); + out[1] = Math.max(a[1], b[1]); + out[2] = Math.max(a[2], b[2]); + out[3] = Math.max(a[3], b[3]); + return out; +}; + +/** + * Scales a vec4 by a scalar number + * + * @param {vec4} out the receiving vector + * @param {vec4} a the vector to scale + * @param {vec4} b amount to scale the vector by + * @returns {vec4} out + */ +vec4.scale = function(out, a, b) { + out[0] = a[0] * b; + out[1] = a[1] * b; + out[2] = a[2] * b; + out[3] = a[3] * b; + return out; +}; + +/** + * Calculates the euclidian distance between two vec4's + * + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {Number} distance between a and b + */ +vec4.dist = vec4.distance = function(a, b) { + var x = b[0] - a[0], + y = b[1] - a[1], + z = b[2] - a[2], + w = b[3] - a[3]; + return Math.sqrt(x*x + y*y + z*z + w*w); +}; + +/** + * Calculates the squared euclidian distance between two vec4's + * + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {Number} squared distance between a and b + */ +vec4.sqrDist = vec4.squaredDistance = function(a, b) { + var x = b[0] - a[0], + y = b[1] - a[1], + z = b[2] - a[2], + w = b[3] - a[3]; + return x*x + y*y + z*z + w*w; +}; + +/** + * Caclulates the length of a vec4 + * + * @param {vec4} a vector to calculate length of + * @returns {Number} length of a + */ +vec4.len = vec4.length = function (a) { + var x = a[0], + y = a[1], + z = a[2], + w = a[3]; + return Math.sqrt(x*x + y*y + z*z + w*w); +}; + +/** + * Caclulates the squared length of a vec4 + * + * @param {vec4} a vector to calculate squared length of + * @returns {Number} squared length of a + */ +vec4.sqrLen = vec4.squaredLength = function (a) { + var x = a[0], + y = a[1], + z = a[2], + w = a[3]; + return x*x + y*y + z*z + w*w; +}; + +/** + * Negates the components of a vec4 + * + * @param {vec4} out the receiving vector + * @param {vec4} a vector to negate + * @returns {vec4} out + */ +vec4.negate = function(out, a) { + out[0] = -a[0]; + out[1] = -a[1]; + out[2] = -a[2]; + out[3] = -a[3]; + return out; +}; + +/** + * Normalize a vec4 + * + * @param {vec4} out the receiving vector + * @param {vec4} a vector to normalize + * @returns {vec4} out + */ +vec4.normalize = function(out, a) { + var x = a[0], + y = a[1], + z = a[2], + w = a[3]; + var len = x*x + y*y + z*z + w*w; + if (len > 0) { + len = 1 / Math.sqrt(len); + out[0] = a[0] * len; + out[1] = a[1] * len; + out[2] = a[2] * len; + out[3] = a[3] * len; + } + return out; +}; + +/** + * Caclulates the dot product of two vec4's + * + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {Number} dot product of a and b + */ +vec4.dot = function (a, b) { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]; +}; + +/** + * Performs a linear interpolation between two vec4's + * + * @param {vec4} out the receiving vector + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @param {Number} t interpolation amount between the two inputs + * @returns {vec4} out + */ +vec4.lerp = function (out, a, b, t) { + var ax = a[0], + ay = a[1], + az = a[2], + aw = a[3]; + out[0] = ax + t * (b[0] - ax); + out[1] = ay + t * (b[1] - ay); + out[2] = az + t * (b[2] - az); + out[3] = aw + t * (b[3] - aw); + return out; +}; + +/** + * Transforms the vec4 with a mat4. + * + * @param {vec4} out the receiving vector + * @param {vec4} a the vector to transform + * @param {mat4} m matrix to transform with + * @returns {vec4} out + */ +vec4.transformMat4 = function(out, a, m) { + var x = a[0], y = a[1], z = a[2], w = a[3]; + out[0] = m[0] * x + m[4] * y + m[8] * z + m[12] * w; + out[1] = m[1] * x + m[5] * y + m[9] * z + m[13] * w; + out[2] = m[2] * x + m[6] * y + m[10] * z + m[14] * w; + out[3] = m[3] * x + m[7] * y + m[11] * z + m[15] * w; + return out; +}; + +/** + * Transforms the vec4 with a quat + * + * @param {vec4} out the receiving vector + * @param {vec4} a the vector to transform + * @param {quat} q quaternion to transform with + * @returns {vec4} out + */ +vec4.transformQuat = function(out, a, q) { + var x = a[0], y = a[1], z = a[2], + qx = q[0], qy = q[1], qz = q[2], qw = q[3], + + // calculate quat * vec + ix = qw * x + qy * z - qz * y, + iy = qw * y + qz * x - qx * z, + iz = qw * z + qx * y - qy * x, + iw = -qx * x - qy * y - qz * z; + + // calculate result * inverse quat + out[0] = ix * qw + iw * -qx + iy * -qz - iz * -qy; + out[1] = iy * qw + iw * -qy + iz * -qx - ix * -qz; + out[2] = iz * qw + iw * -qz + ix * -qy - iy * -qx; + return out; +}; + +/** + * Perform some operation over an array of vec4s. + * + * @param {Array} a the array of vectors to iterate over + * @param {Number} stride Number of elements between the start of each vec4. If 0 assumes tightly packed + * @param {Number} offset Number of elements to skip at the beginning of the array + * @param {Number} count Number of vec2s to iterate over. If 0 iterates over entire array + * @param {Function} fn Function to call for each vector in the array + * @param {Object} [arg] additional argument to pass to fn + * @returns {Array} a + */ +vec4.forEach = (function() { + var vec = new Float32Array(4); + + return function(a, stride, offset, count, fn, arg) { + var i, l; + if(!stride) { + stride = 4; + } + + if(!offset) { + offset = 0; + } + + if(count) { + l = Math.min((count * stride) + offset, a.length); + } else { + l = a.length; + } + + for(i = offset; i < l; i += stride) { + vec[0] = a[i]; vec[1] = a[i+1]; vec[2] = a[i+2]; vec[3] = a[i+3]; + fn(vec, vec, arg); + a[i] = vec[0]; a[i+1] = vec[1]; a[i+2] = vec[2]; a[i+3] = vec[3]; + } + + return a; + }; +})(); + +/** + * Returns a string representation of a vector + * + * @param {vec4} vec vector to represent as a string + * @returns {String} string representation of the vector + */ +vec4.str = function (a) { + return 'vec4(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ')'; +}; + +if(typeof(exports) !== 'undefined') { + exports.vec4 = vec4; +} +; +/* Copyright (c) 2012, Brandon Jones, Colin MacKenzie IV. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +/** + * @class 2x2 Matrix + * @name mat2 + */ + +var mat2 = {}; + +var mat2Identity = new Float32Array([ + 1, 0, + 0, 1 +]); + +if(!GLMAT_EPSILON) { + var GLMAT_EPSILON = 0.000001; +} + +/** + * Creates a new identity mat2 + * + * @returns {mat2} a new 2x2 matrix + */ +mat2.create = function() { + return new Float32Array(mat2Identity); +}; + +/** + * Creates a new mat2 initialized with values from an existing matrix + * + * @param {mat2} a matrix to clone + * @returns {mat2} a new 2x2 matrix + */ +mat2.clone = function(a) { + var out = new Float32Array(4); + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + return out; +}; + +/** + * Copy the values from one mat2 to another + * + * @param {mat2} out the receiving matrix + * @param {mat2} a the source matrix + * @returns {mat2} out + */ +mat2.copy = function(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + return out; +}; + +/** + * Set a mat2 to the identity matrix + * + * @param {mat2} out the receiving matrix + * @returns {mat2} out + */ +mat2.identity = function(out) { + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 1; + return out; +}; + +/** + * Transpose the values of a mat2 + * + * @param {mat2} out the receiving matrix + * @param {mat2} a the source matrix + * @returns {mat2} out + */ +mat2.transpose = function(out, a) { + // If we are transposing ourselves we can skip a few steps but have to cache some values + if (out === a) { + var a1 = a[1]; + out[1] = a[2]; + out[2] = a1; + } else { + out[0] = a[0]; + out[1] = a[2]; + out[2] = a[1]; + out[3] = a[3]; + } + + return out; +}; + +/** + * Inverts a mat2 + * + * @param {mat2} out the receiving matrix + * @param {mat2} a the source matrix + * @returns {mat2} out + */ +mat2.invert = function(out, a) { + var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], + + // Calculate the determinant + det = a0 * a3 - a2 * a1; + + if (!det) { + return null; + } + det = 1.0 / det; + + out[0] = a3 * det; + out[1] = -a1 * det; + out[2] = -a2 * det; + out[3] = a0 * det; + + return out; +}; + +/** + * Caclulates the adjugate of a mat2 + * + * @param {mat2} out the receiving matrix + * @param {mat2} a the source matrix + * @returns {mat2} out + */ +mat2.adjoint = function(out, a) { + // Caching this value is nessecary if out == a + var a0 = a[0]; + out[0] = a[3]; + out[1] = -a[1]; + out[2] = -a[2]; + out[3] = a0; + + return out; +}; + +/** + * Calculates the determinant of a mat2 + * + * @param {mat2} a the source matrix + * @returns {Number} determinant of a + */ +mat2.determinant = function (a) { + return a[0] * a[3] - a[2] * a[1]; +}; + +/** + * Multiplies two mat2's + * + * @param {mat2} out the receiving matrix + * @param {mat2} a the first operand + * @param {mat2} b the second operand + * @returns {mat2} out + */ +mat2.mul = mat2.multiply = function (out, a, b) { + var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3]; + var b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3]; + out[0] = a0 * b0 + a1 * b2; + out[1] = a0 * b1 + a1 * b3; + out[2] = a2 * b0 + a3 * b2; + out[3] = a2 * b1 + a3 * b3; + return out; +}; + +/** + * Rotates a mat2 by the given angle + * + * @param {mat2} out the receiving matrix + * @param {mat2} a the matrix to rotate + * @param {mat2} rad the angle to rotate the matrix by + * @returns {mat2} out + */ +mat2.rotate = function (out, a, rad) { + var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], + s = Math.sin(rad), + c = Math.cos(rad); + out[0] = a0 * c + a1 * s; + out[1] = a0 * -s + a1 * c; + out[2] = a2 * c + a3 * s; + out[3] = a2 * -s + a3 * c; + return out; +}; + +/** + * Scales the mat2 by the dimensions in the given vec2 + * + * @param {mat2} out the receiving matrix + * @param {mat2} a the matrix to rotate + * @param {mat2} v the vec2 to scale the matrix by + * @returns {mat2} out + **/ +mat2.scale = function(out, a, v) { + var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], + v0 = v[0], v1 = v[1]; + out[0] = a0 * v0; + out[1] = a1 * v1; + out[2] = a2 * v0; + out[3] = a3 * v1; + return out; +}; + +/** + * Returns a string representation of a mat2 + * + * @param {mat2} mat matrix to represent as a string + * @returns {String} string representation of the matrix + */ +mat2.str = function (a) { + return 'mat2(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ')'; +}; + +if(typeof(exports) !== 'undefined') { + exports.mat2 = mat2; +} +; +/* Copyright (c) 2012, Brandon Jones, Colin MacKenzie IV. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +/** + * @class 3x3 Matrix + * @name mat3 + */ + +var mat3 = {}; + +var mat3Identity = new Float32Array([ + 1, 0, 0, + 0, 1, 0, + 0, 0, 1 +]); + +if(!GLMAT_EPSILON) { + var GLMAT_EPSILON = 0.000001; +} + +/** + * Creates a new identity mat3 + * + * @returns {mat3} a new 3x3 matrix + */ +mat3.create = function() { + return new Float32Array(mat3Identity); +}; + +/** + * Creates a new mat3 initialized with values from an existing matrix + * + * @param {mat3} a matrix to clone + * @returns {mat3} a new 3x3 matrix + */ +mat3.clone = function(a) { + var out = new Float32Array(9); + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[4] = a[4]; + out[5] = a[5]; + out[6] = a[6]; + out[7] = a[7]; + out[8] = a[8]; + return out; +}; + +/** + * Copy the values from one mat3 to another + * + * @param {mat3} out the receiving matrix + * @param {mat3} a the source matrix + * @returns {mat3} out + */ +mat3.copy = function(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[4] = a[4]; + out[5] = a[5]; + out[6] = a[6]; + out[7] = a[7]; + out[8] = a[8]; + return out; +}; + +/** + * Set a mat3 to the identity matrix + * + * @param {mat3} out the receiving matrix + * @returns {mat3} out + */ +mat3.identity = function(out) { + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 1; + out[5] = 0; + out[6] = 0; + out[7] = 0; + out[8] = 1; + return out; +}; + +/** + * Transpose the values of a mat3 + * + * @param {mat3} out the receiving matrix + * @param {mat3} a the source matrix + * @returns {mat3} out + */ +mat3.transpose = function(out, a) { + // If we are transposing ourselves we can skip a few steps but have to cache some values + if (out === a) { + var a01 = a[1], a02 = a[2], a12 = a[5]; + out[1] = a[3]; + out[2] = a[6]; + out[3] = a01; + out[5] = a[7]; + out[6] = a02; + out[7] = a12; + } else { + out[0] = a[0]; + out[1] = a[3]; + out[2] = a[6]; + out[3] = a[1]; + out[4] = a[4]; + out[5] = a[7]; + out[6] = a[2]; + out[7] = a[5]; + out[8] = a[8]; + } + + return out; +}; + +/** + * Inverts a mat3 + * + * @param {mat3} out the receiving matrix + * @param {mat3} a the source matrix + * @returns {mat3} out + */ +mat3.invert = function(out, a) { + var a00 = a[0], a01 = a[1], a02 = a[2], + a10 = a[3], a11 = a[4], a12 = a[5], + a20 = a[6], a21 = a[7], a22 = a[8], + + b01 = a22 * a11 - a12 * a21, + b11 = -a22 * a10 + a12 * a20, + b21 = a21 * a10 - a11 * a20, + + // Calculate the determinant + det = a00 * b01 + a01 * b11 + a02 * b21; + + if (!det) { + return null; + } + det = 1.0 / det; + + out[0] = b01 * det; + out[1] = (-a22 * a01 + a02 * a21) * det; + out[2] = (a12 * a01 - a02 * a11) * det; + out[3] = b11 * det; + out[4] = (a22 * a00 - a02 * a20) * det; + out[5] = (-a12 * a00 + a02 * a10) * det; + out[6] = b21 * det; + out[7] = (-a21 * a00 + a01 * a20) * det; + out[8] = (a11 * a00 - a01 * a10) * det; + return out; +}; + +/** + * Caclulates the adjugate of a mat3 + * + * @param {mat3} out the receiving matrix + * @param {mat3} a the source matrix + * @returns {mat3} out + */ +mat3.adjoint = function(out, a) { + var a00 = a[0], a01 = a[1], a02 = a[2], + a10 = a[3], a11 = a[4], a12 = a[5], + a20 = a[6], a21 = a[7], a22 = a[8]; + + out[0] = (a11 * a22 - a12 * a21); + out[1] = (a02 * a21 - a01 * a22); + out[2] = (a01 * a12 - a02 * a11); + out[3] = (a12 * a20 - a10 * a22); + out[4] = (a00 * a22 - a02 * a20); + out[5] = (a02 * a10 - a00 * a12); + out[6] = (a10 * a21 - a11 * a20); + out[7] = (a01 * a20 - a00 * a21); + out[8] = (a00 * a11 - a01 * a10); + return out; +}; + +/** + * Calculates the determinant of a mat3 + * + * @param {mat3} a the source matrix + * @returns {Number} determinant of a + */ +mat3.determinant = function (a) { + var a00 = a[0], a01 = a[1], a02 = a[2], + a10 = a[3], a11 = a[4], a12 = a[5], + a20 = a[6], a21 = a[7], a22 = a[8]; + + return a00 * (a22 * a11 - a12 * a21) + a01 * (-a22 * a10 + a12 * a20) + a02 * (a21 * a10 - a11 * a20); +}; + +/** + * Multiplies two mat3's + * + * @param {mat3} out the receiving matrix + * @param {mat3} a the first operand + * @param {mat3} b the second operand + * @returns {mat3} out + */ +mat3.mul = mat3.multiply = function (out, a, b) { + var a00 = a[0], a01 = a[1], a02 = a[2], + a10 = a[3], a11 = a[4], a12 = a[5], + a20 = a[6], a21 = a[7], a22 = a[8], + + b00 = b[0], b01 = b[1], b02 = b[2], + b10 = b[3], b11 = b[4], b12 = b[5], + b20 = b[6], b21 = b[7], b22 = b[8]; + + out[0] = b00 * a00 + b01 * a10 + b02 * a20; + out[1] = b00 * a01 + b01 * a11 + b02 * a21; + out[2] = b00 * a02 + b01 * a12 + b02 * a22; + + out[3] = b10 * a00 + b11 * a10 + b12 * a20; + out[4] = b10 * a01 + b11 * a11 + b12 * a21; + out[5] = b10 * a02 + b11 * a12 + b12 * a22; + + out[6] = b20 * a00 + b21 * a10 + b22 * a20; + out[7] = b20 * a01 + b21 * a11 + b22 * a21; + out[8] = b20 * a02 + b21 * a12 + b22 * a22; + return out; +}; + +/** + * Returns a string representation of a mat3 + * + * @param {mat3} mat matrix to represent as a string + * @returns {String} string representation of the matrix + */ +mat3.str = function (a) { + return 'mat3(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + + a[3] + ', ' + a[4] + ', ' + a[5] + ', ' + + a[6] + ', ' + a[7] + ', ' + a[8] + ')'; +}; + +if(typeof(exports) !== 'undefined') { + exports.mat3 = mat3; +} +; +/* Copyright (c) 2012, Brandon Jones, Colin MacKenzie IV. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +/** + * @class 4x4 Matrix + * @name mat4 + */ + +var mat4 = {}; + +var mat4Identity = new Float32Array([ + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 +]); + +if(!GLMAT_EPSILON) { + var GLMAT_EPSILON = 0.000001; +} + +/** + * Creates a new identity mat4 + * + * @returns {mat4} a new 4x4 matrix + */ +mat4.create = function() { + return new Float32Array(mat4Identity); +}; + +/** + * Creates a new mat4 initialized with values from an existing matrix + * + * @param {mat4} a matrix to clone + * @returns {mat4} a new 4x4 matrix + */ +mat4.clone = function(a) { + var out = new Float32Array(16); + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[4] = a[4]; + out[5] = a[5]; + out[6] = a[6]; + out[7] = a[7]; + out[8] = a[8]; + out[9] = a[9]; + out[10] = a[10]; + out[11] = a[11]; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + return out; +}; + +/** + * Copy the values from one mat4 to another + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the source matrix + * @returns {mat4} out + */ +mat4.copy = function(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[4] = a[4]; + out[5] = a[5]; + out[6] = a[6]; + out[7] = a[7]; + out[8] = a[8]; + out[9] = a[9]; + out[10] = a[10]; + out[11] = a[11]; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + return out; +}; + +/** + * Set a mat4 to the identity matrix + * + * @param {mat4} out the receiving matrix + * @returns {mat4} out + */ +mat4.identity = function(out) { + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = 1; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = 1; + out[11] = 0; + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + return out; +}; + +/** + * Transpose the values of a mat4 + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the source matrix + * @returns {mat4} out + */ +mat4.transpose = function(out, a) { + // If we are transposing ourselves we can skip a few steps but have to cache some values + if (out === a) { + var a01 = a[1], a02 = a[2], a03 = a[3], + a12 = a[6], a13 = a[7], + a23 = a[11]; + + out[1] = a[4]; + out[2] = a[8]; + out[3] = a[12]; + out[4] = a01; + out[6] = a[9]; + out[7] = a[13]; + out[8] = a02; + out[9] = a12; + out[11] = a[14]; + out[12] = a03; + out[13] = a13; + out[14] = a23; + } else { + out[0] = a[0]; + out[1] = a[4]; + out[2] = a[8]; + out[3] = a[12]; + out[4] = a[1]; + out[5] = a[5]; + out[6] = a[9]; + out[7] = a[13]; + out[8] = a[2]; + out[9] = a[6]; + out[10] = a[10]; + out[11] = a[14]; + out[12] = a[3]; + out[13] = a[7]; + out[14] = a[11]; + out[15] = a[15]; + } + + return out; +}; + +/** + * Inverts a mat4 + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the source matrix + * @returns {mat4} out + */ +mat4.invert = function(out, a) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15], + + b00 = a00 * a11 - a01 * a10, + b01 = a00 * a12 - a02 * a10, + b02 = a00 * a13 - a03 * a10, + b03 = a01 * a12 - a02 * a11, + b04 = a01 * a13 - a03 * a11, + b05 = a02 * a13 - a03 * a12, + b06 = a20 * a31 - a21 * a30, + b07 = a20 * a32 - a22 * a30, + b08 = a20 * a33 - a23 * a30, + b09 = a21 * a32 - a22 * a31, + b10 = a21 * a33 - a23 * a31, + b11 = a22 * a33 - a23 * a32, + + // Calculate the determinant + det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; + + if (!det) { + return null; + } + det = 1.0 / det; + + out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det; + out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det; + out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det; + out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det; + out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det; + out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det; + out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det; + out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det; + out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det; + out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det; + out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det; + out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det; + out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det; + out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det; + out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det; + out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det; + + return out; +}; + +/** + * Caclulates the adjugate of a mat4 + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the source matrix + * @returns {mat4} out + */ +mat4.adjoint = function(out, a) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15]; + + out[0] = (a11 * (a22 * a33 - a23 * a32) - a21 * (a12 * a33 - a13 * a32) + a31 * (a12 * a23 - a13 * a22)); + out[1] = -(a01 * (a22 * a33 - a23 * a32) - a21 * (a02 * a33 - a03 * a32) + a31 * (a02 * a23 - a03 * a22)); + out[2] = (a01 * (a12 * a33 - a13 * a32) - a11 * (a02 * a33 - a03 * a32) + a31 * (a02 * a13 - a03 * a12)); + out[3] = -(a01 * (a12 * a23 - a13 * a22) - a11 * (a02 * a23 - a03 * a22) + a21 * (a02 * a13 - a03 * a12)); + out[4] = -(a10 * (a22 * a33 - a23 * a32) - a20 * (a12 * a33 - a13 * a32) + a30 * (a12 * a23 - a13 * a22)); + out[5] = (a00 * (a22 * a33 - a23 * a32) - a20 * (a02 * a33 - a03 * a32) + a30 * (a02 * a23 - a03 * a22)); + out[6] = -(a00 * (a12 * a33 - a13 * a32) - a10 * (a02 * a33 - a03 * a32) + a30 * (a02 * a13 - a03 * a12)); + out[7] = (a00 * (a12 * a23 - a13 * a22) - a10 * (a02 * a23 - a03 * a22) + a20 * (a02 * a13 - a03 * a12)); + out[8] = (a10 * (a21 * a33 - a23 * a31) - a20 * (a11 * a33 - a13 * a31) + a30 * (a11 * a23 - a13 * a21)); + out[9] = -(a00 * (a21 * a33 - a23 * a31) - a20 * (a01 * a33 - a03 * a31) + a30 * (a01 * a23 - a03 * a21)); + out[10] = (a00 * (a11 * a33 - a13 * a31) - a10 * (a01 * a33 - a03 * a31) + a30 * (a01 * a13 - a03 * a11)); + out[11] = -(a00 * (a11 * a23 - a13 * a21) - a10 * (a01 * a23 - a03 * a21) + a20 * (a01 * a13 - a03 * a11)); + out[12] = -(a10 * (a21 * a32 - a22 * a31) - a20 * (a11 * a32 - a12 * a31) + a30 * (a11 * a22 - a12 * a21)); + out[13] = (a00 * (a21 * a32 - a22 * a31) - a20 * (a01 * a32 - a02 * a31) + a30 * (a01 * a22 - a02 * a21)); + out[14] = -(a00 * (a11 * a32 - a12 * a31) - a10 * (a01 * a32 - a02 * a31) + a30 * (a01 * a12 - a02 * a11)); + out[15] = (a00 * (a11 * a22 - a12 * a21) - a10 * (a01 * a22 - a02 * a21) + a20 * (a01 * a12 - a02 * a11)); + return out; +}; + +/** + * Calculates the determinant of a mat4 + * + * @param {mat4} a the source matrix + * @returns {Number} determinant of a + */ +mat4.determinant = function (a) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15], + + b00 = a00 * a11 - a01 * a10, + b01 = a00 * a12 - a02 * a10, + b02 = a00 * a13 - a03 * a10, + b03 = a01 * a12 - a02 * a11, + b04 = a01 * a13 - a03 * a11, + b05 = a02 * a13 - a03 * a12, + b06 = a20 * a31 - a21 * a30, + b07 = a20 * a32 - a22 * a30, + b08 = a20 * a33 - a23 * a30, + b09 = a21 * a32 - a22 * a31, + b10 = a21 * a33 - a23 * a31, + b11 = a22 * a33 - a23 * a32; + + // Calculate the determinant + return b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; +}; + +/** + * Multiplies two mat4's + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the first operand + * @param {mat4} b the second operand + * @returns {mat4} out + */ +mat4.mul = mat4.multiply = function (out, a, b) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15]; + + // Cache only the current line of the second matrix + var b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3]; + out[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[4]; b1 = b[5]; b2 = b[6]; b3 = b[7]; + out[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[8]; b1 = b[9]; b2 = b[10]; b3 = b[11]; + out[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[12]; b1 = b[13]; b2 = b[14]; b3 = b[15]; + out[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + return out; +}; + +/** + * Translate a mat4 by the given vector + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to translate + * @param {vec3} v vector to translate by + * @returns {mat4} out + */ +mat4.translate = function (out, a, v) { + var x = v[0], y = v[1], z = v[2], + a00, a01, a02, a03, + a10, a11, a12, a13, + a20, a21, a22, a23; + + if (a === out) { + out[12] = a[0] * x + a[4] * y + a[8] * z + a[12]; + out[13] = a[1] * x + a[5] * y + a[9] * z + a[13]; + out[14] = a[2] * x + a[6] * y + a[10] * z + a[14]; + out[15] = a[3] * x + a[7] * y + a[11] * z + a[15]; + } else { + a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3]; + a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7]; + a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11]; + + out[0] = a00; out[1] = a01; out[2] = a02; out[3] = a03; + out[4] = a10; out[5] = a11; out[6] = a12; out[7] = a13; + out[8] = a20; out[9] = a21; out[10] = a22; out[11] = a23; + + out[12] = a00 * x + a10 * y + a20 * z + a[12]; + out[13] = a01 * x + a11 * y + a21 * z + a[13]; + out[14] = a02 * x + a12 * y + a22 * z + a[14]; + out[15] = a03 * x + a13 * y + a23 * z + a[15]; + } + + return out; +}; + +/** + * Scales the mat4 by the dimensions in the given vec3 + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to scale + * @param {vec3} v the vec3 to scale the matrix by + * @returns {mat4} out + **/ +mat4.scale = function(out, a, v) { + var x = v[0], y = v[1], z = v[2]; + + out[0] = a[0] * x; + out[1] = a[1] * x; + out[2] = a[2] * x; + out[3] = a[3] * x; + out[4] = a[4] * y; + out[5] = a[5] * y; + out[6] = a[6] * y; + out[7] = a[7] * y; + out[8] = a[8] * z; + out[9] = a[9] * z; + out[10] = a[10] * z; + out[11] = a[11] * z; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + return out; +}; + +/** + * Rotates a mat4 by the given angle + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @param {vec3} axis the axis to rotate around + * @returns {mat4} out + */ +mat4.rotate = function (out, a, rad, axis) { + var x = axis[0], y = axis[1], z = axis[2], + len = Math.sqrt(x * x + y * y + z * z), + s, c, t, + a00, a01, a02, a03, + a10, a11, a12, a13, + a20, a21, a22, a23, + b00, b01, b02, + b10, b11, b12, + b20, b21, b22; + + if (Math.abs(len) < GLMAT_EPSILON) { return null; } + + len = 1 / len; + x *= len; + y *= len; + z *= len; + + s = Math.sin(rad); + c = Math.cos(rad); + t = 1 - c; + + a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3]; + a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7]; + a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11]; + + // Construct the elements of the rotation matrix + b00 = x * x * t + c; b01 = y * x * t + z * s; b02 = z * x * t - y * s; + b10 = x * y * t - z * s; b11 = y * y * t + c; b12 = z * y * t + x * s; + b20 = x * z * t + y * s; b21 = y * z * t - x * s; b22 = z * z * t + c; + + // Perform rotation-specific matrix multiplication + out[0] = a00 * b00 + a10 * b01 + a20 * b02; + out[1] = a01 * b00 + a11 * b01 + a21 * b02; + out[2] = a02 * b00 + a12 * b01 + a22 * b02; + out[3] = a03 * b00 + a13 * b01 + a23 * b02; + out[4] = a00 * b10 + a10 * b11 + a20 * b12; + out[5] = a01 * b10 + a11 * b11 + a21 * b12; + out[6] = a02 * b10 + a12 * b11 + a22 * b12; + out[7] = a03 * b10 + a13 * b11 + a23 * b12; + out[8] = a00 * b20 + a10 * b21 + a20 * b22; + out[9] = a01 * b20 + a11 * b21 + a21 * b22; + out[10] = a02 * b20 + a12 * b21 + a22 * b22; + out[11] = a03 * b20 + a13 * b21 + a23 * b22; + + if (a !== out) { // If the source and destination differ, copy the unchanged last row + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + } + return out; +}; + +/** + * Rotates a matrix by the given angle around the X axis + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat4} out + */ +mat4.rotateX = function (out, a, rad) { + var s = Math.sin(rad), + c = Math.cos(rad), + a10 = a[4], + a11 = a[5], + a12 = a[6], + a13 = a[7], + a20 = a[8], + a21 = a[9], + a22 = a[10], + a23 = a[11]; + + if (a !== out) { // If the source and destination differ, copy the unchanged rows + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + } + + // Perform axis-specific matrix multiplication + out[4] = a10 * c + a20 * s; + out[5] = a11 * c + a21 * s; + out[6] = a12 * c + a22 * s; + out[7] = a13 * c + a23 * s; + out[8] = a20 * c - a10 * s; + out[9] = a21 * c - a11 * s; + out[10] = a22 * c - a12 * s; + out[11] = a23 * c - a13 * s; + return out; +}; + +/** + * Rotates a matrix by the given angle around the Y axis + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat4} out + */ +mat4.rotateY = function (out, a, rad) { + var s = Math.sin(rad), + c = Math.cos(rad), + a00 = a[0], + a01 = a[1], + a02 = a[2], + a03 = a[3], + a20 = a[8], + a21 = a[9], + a22 = a[10], + a23 = a[11]; + + if (a !== out) { // If the source and destination differ, copy the unchanged rows + out[4] = a[4]; + out[5] = a[5]; + out[6] = a[6]; + out[7] = a[7]; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + } + + // Perform axis-specific matrix multiplication + out[0] = a00 * c - a20 * s; + out[1] = a01 * c - a21 * s; + out[2] = a02 * c - a22 * s; + out[3] = a03 * c - a23 * s; + out[8] = a00 * s + a20 * c; + out[9] = a01 * s + a21 * c; + out[10] = a02 * s + a22 * c; + out[11] = a03 * s + a23 * c; + return out; +}; + +/** + * Rotates a matrix by the given angle around the Z axis + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat4} out + */ +mat4.rotateZ = function (out, a, rad) { + var s = Math.sin(rad), + c = Math.cos(rad), + a00 = a[0], + a01 = a[1], + a02 = a[2], + a03 = a[3], + a10 = a[4], + a11 = a[5], + a12 = a[6], + a13 = a[7]; + + if (a !== out) { // If the source and destination differ, copy the unchanged last row + out[8] = a[8]; + out[9] = a[9]; + out[10] = a[10]; + out[11] = a[11]; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + } + + // Perform axis-specific matrix multiplication + out[0] = a00 * c + a10 * s; + out[1] = a01 * c + a11 * s; + out[2] = a02 * c + a12 * s; + out[3] = a03 * c + a13 * s; + out[4] = a10 * c - a00 * s; + out[5] = a11 * c - a01 * s; + out[6] = a12 * c - a02 * s; + out[7] = a13 * c - a03 * s; + return out; +}; + +/** + * Creates a matrix from a quaternion rotation and vector translation + * This is equivalent to (but much faster than): + * + * mat4.identity(dest); + * mat4.translate(dest, vec); + * var quatMat = mat4.create(); + * quat4.toMat4(quat, quatMat); + * mat4.multiply(dest, quatMat); + * + * @param {mat4} out mat4 receiving operation result + * @param {quat4} q Rotation quaternion + * @param {vec3} v Translation vector + * @returns {mat4} out + */ +mat4.fromRotationTranslation = function (out, q, v) { + // Quaternion math + var x = q[0], y = q[1], z = q[2], w = q[3], + x2 = x + x, + y2 = y + y, + z2 = z + z, + + xx = x * x2, + xy = x * y2, + xz = x * z2, + yy = y * y2, + yz = y * z2, + zz = z * z2, + wx = w * x2, + wy = w * y2, + wz = w * z2; + + out[0] = 1 - (yy + zz); + out[1] = xy + wz; + out[2] = xz - wy; + out[3] = 0; + out[4] = xy - wz; + out[5] = 1 - (xx + zz); + out[6] = yz + wx; + out[7] = 0; + out[8] = xz + wy; + out[9] = yz - wx; + out[10] = 1 - (xx + yy); + out[11] = 0; + out[12] = v[0]; + out[13] = v[1]; + out[14] = v[2]; + out[15] = 1; + + return out; +}; + +/** + * Generates a frustum matrix with the given bounds + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {Number} left Left bound of the frustum + * @param {Number} right Right bound of the frustum + * @param {Number} bottom Bottom bound of the frustum + * @param {Number} top Top bound of the frustum + * @param {Number} near Near bound of the frustum + * @param {Number} far Far bound of the frustum + * @returns {mat4} out + */ +mat4.frustum = function (out, left, right, bottom, top, near, far) { + var rl = 1 / (right - left), + tb = 1 / (top - bottom), + nf = 1 / (near - far); + out[0] = (near * 2) * rl; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = (near * 2) * tb; + out[6] = 0; + out[7] = 0; + out[8] = (right + left) * rl; + out[9] = (top + bottom) * tb; + out[10] = (far + near) * nf; + out[11] = -1; + out[12] = 0; + out[13] = 0; + out[14] = (far * near * 2) * nf; + out[15] = 0; + return out; +}; + +/** + * Generates a perspective projection matrix with the given bounds + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {number} fovy Vertical field of view in radians + * @param {number} aspect Aspect ratio. typically viewport width/height + * @param {number} near Near bound of the frustum + * @param {number} far Far bound of the frustum + * @returns {mat4} out + */ +mat4.perspective = function (out, fovy, aspect, near, far) { + var f = 1.0 / Math.tan(fovy / 2), + nf = 1 / (near - far); + out[0] = f / aspect; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = f; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = (far + near) * nf; + out[11] = -1; + out[12] = 0; + out[13] = 0; + out[14] = (2 * far * near) * nf; + out[15] = 0; + return out; +}; + +/** + * Generates a orthogonal projection matrix with the given bounds + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {number} left Left bound of the frustum + * @param {number} right Right bound of the frustum + * @param {number} bottom Bottom bound of the frustum + * @param {number} top Top bound of the frustum + * @param {number} near Near bound of the frustum + * @param {number} far Far bound of the frustum + * @returns {mat4} out + */ +mat4.ortho = function (out, left, right, bottom, top, near, far) { + var lr = 1 / (left - right), + bt = 1 / (bottom - top), + nf = 1 / (near - far); + out[0] = -2 * lr; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = -2 * bt; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = 2 * nf; + out[11] = 0; + out[12] = (left + right) * lr; + out[13] = (top + bottom) * bt; + out[14] = (far + near) * nf; + out[15] = 1; + return out; +}; + +/** + * Generates a look-at matrix with the given eye position, focal point, and up axis + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {vec3} eye Position of the viewer + * @param {vec3} center Point the viewer is looking at + * @param {vec3} up vec3 pointing up + * @returns {mat4} out + */ +mat4.lookAt = function (out, eye, center, up) { + var x0, x1, x2, y0, y1, y2, z0, z1, z2, len, + eyex = eye[0], + eyey = eye[1], + eyez = eye[2], + upx = up[0], + upy = up[1], + upz = up[2], + centerx = center[0], + centery = center[1], + centerz = center[2]; + + if (Math.abs(eyex - centerx) < GLMAT_EPSILON && + Math.abs(eyey - centery) < GLMAT_EPSILON && + Math.abs(eyez - centerz) < GLMAT_EPSILON) { + return mat4.identity(out); + } + + z0 = eyex - centerx; + z1 = eyey - centery; + z2 = eyez - centerz; + + len = 1 / Math.sqrt(z0 * z0 + z1 * z1 + z2 * z2); + z0 *= len; + z1 *= len; + z2 *= len; + + x0 = upy * z2 - upz * z1; + x1 = upz * z0 - upx * z2; + x2 = upx * z1 - upy * z0; + len = Math.sqrt(x0 * x0 + x1 * x1 + x2 * x2); + if (!len) { + x0 = 0; + x1 = 0; + x2 = 0; + } else { + len = 1 / len; + x0 *= len; + x1 *= len; + x2 *= len; + } + + y0 = z1 * x2 - z2 * x1; + y1 = z2 * x0 - z0 * x2; + y2 = z0 * x1 - z1 * x0; + + len = Math.sqrt(y0 * y0 + y1 * y1 + y2 * y2); + if (!len) { + y0 = 0; + y1 = 0; + y2 = 0; + } else { + len = 1 / len; + y0 *= len; + y1 *= len; + y2 *= len; + } + + out[0] = x0; + out[1] = y0; + out[2] = z0; + out[3] = 0; + out[4] = x1; + out[5] = y1; + out[6] = z1; + out[7] = 0; + out[8] = x2; + out[9] = y2; + out[10] = z2; + out[11] = 0; + out[12] = -(x0 * eyex + x1 * eyey + x2 * eyez); + out[13] = -(y0 * eyex + y1 * eyey + y2 * eyez); + out[14] = -(z0 * eyex + z1 * eyey + z2 * eyez); + out[15] = 1; + + return out; +}; + +/** + * Returns a string representation of a mat4 + * + * @param {mat4} mat matrix to represent as a string + * @returns {String} string representation of the matrix + */ +mat4.str = function (a) { + return 'mat4(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ', ' + + a[4] + ', ' + a[5] + ', ' + a[6] + ', ' + a[7] + ', ' + + a[8] + ', ' + a[9] + ', ' + a[10] + ', ' + a[11] + ', ' + + a[12] + ', ' + a[13] + ', ' + a[14] + ', ' + a[15] + ')'; +}; + +if(typeof(exports) !== 'undefined') { + exports.mat4 = mat4; +} +; +/* Copyright (c) 2012, Brandon Jones, Colin MacKenzie IV. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +/** + * @class Quaternion + * @name quat + */ + +var quat = {}; + +var quatIdentity = new Float32Array([0, 0, 0, 1]); + +if(!GLMAT_EPSILON) { + var GLMAT_EPSILON = 0.000001; +} + +/** + * Creates a new identity quat + * + * @returns {quat} a new quaternion + */ +quat.create = function() { + return new Float32Array(quatIdentity); +}; + +/** + * Creates a new quat initialized with values from an existing quaternion + * + * @param {quat} a quaternion to clone + * @returns {quat} a new quaternion + */ +quat.clone = vec4.clone; + +/** + * Creates a new quat initialized with the given values + * + * @param {Number} x X component + * @param {Number} y Y component + * @param {Number} z Z component + * @param {Number} w W component + * @returns {quat} a new quaternion + */ +quat.fromValues = vec4.fromValues; + +/** + * Copy the values from one quat to another + * + * @param {quat} out the receiving quaternion + * @param {quat} a the source quaternion + * @returns {quat} out + */ +quat.copy = vec4.copy; + +/** + * Set the components of a quat to the given values + * + * @param {quat} out the receiving quaternion + * @param {Number} x X component + * @param {Number} y Y component + * @param {Number} z Z component + * @param {Number} w W component + * @returns {quat} out + */ +quat.set = vec4.set; + +/** + * Set a quat to the identity quaternion + * + * @param {quat} out the receiving quaternion + * @returns {quat} out + */ +quat.identity = function(out) { + out[0] = 0; + out[1] = 0; + out[2] = 0; + out[3] = 1; + return out; +}; + +/** + * Sets a quat from the given angle and rotation axis, + * then returns it. + * + * @param {quat} out the receiving quaternion + * @param {vec3} axis the axis around which to rotate + * @param {Number} rad the angle in radians + * @returns {quat} out + **/ +quat.setAxisAngle = function(out, axis, rad) { + rad = rad * 0.5; + var s = Math.sin(rad); + out[0] = s * axis[0]; + out[1] = s * axis[1]; + out[2] = s * axis[2]; + out[3] = Math.cos(rad); + return out; +}; + +/** + * Adds two quat's + * + * @param {quat} out the receiving quaternion + * @param {quat} a the first operand + * @param {quat} b the second operand + * @returns {quat} out + */ +quat.add = vec4.add; + +/** + * Multiplies two quat's + * + * @param {quat} out the receiving quaternion + * @param {quat} a the first operand + * @param {quat} b the second operand + * @returns {quat} out + */ +quat.mul = quat.multiply = function(out, a, b) { + var ax = a[0], ay = a[1], az = a[2], aw = a[3], + bx = b[0], by = b[1], bz = b[2], bw = b[3]; + + out[0] = ax * bw + aw * bx + ay * bz - az * by; + out[1] = ay * bw + aw * by + az * bx - ax * bz; + out[2] = az * bw + aw * bz + ax * by - ay * bx; + out[3] = aw * bw - ax * bx - ay * by - az * bz; + return out; +}; + +/** + * Scales a quat by a scalar number + * + * @param {quat} out the receiving vector + * @param {quat} a the vector to scale + * @param {quat} b amount to scale the vector by + * @returns {quat} out + */ +quat.scale = vec4.scale; + +/** + * Rotates a quaternion by the given angle around the X axis + * + * @param {quat} out quat receiving operation result + * @param {quat} a quat to rotate + * @param {number} rad angle (in radians) to rotate + * @returns {quat} out + */ +quat.rotateX = function (out, a, rad) { + rad *= 0.5; + + var ax = a[0], ay = a[1], az = a[2], aw = a[3], + bx = Math.sin(rad), bw = Math.cos(rad); + + out[0] = ax * bw + aw * bx; + out[1] = ay * bw + az * bx; + out[2] = az * bw - ay * bx; + out[3] = aw * bw - ax * bx; + return out; +}; + +/** + * Rotates a quaternion by the given angle around the X axis + * + * @param {quat} out quat receiving operation result + * @param {quat} a quat to rotate + * @param {number} rad angle (in radians) to rotate + * @returns {quat} out + */ +quat.rotateY = function (out, a, rad) { + rad *= 0.5; + + var ax = a[0], ay = a[1], az = a[2], aw = a[3], + by = Math.sin(rad), bw = Math.cos(rad); + + out[0] = ax * bw - az * by; + out[1] = ay * bw + aw * by; + out[2] = az * bw + ax * by; + out[3] = aw * bw - ay * by; + return out; +}; + +/** + * Rotates a quaternion by the given angle around the X axis + * + * @param {quat} out quat receiving operation result + * @param {quat} a quat to rotate + * @param {number} rad angle (in radians) to rotate + * @returns {quat} out + */ +quat.rotateZ = function (out, a, rad) { + rad *= 0.5; + + var ax = a[0], ay = a[1], az = a[2], aw = a[3], + bz = Math.sin(rad), bw = Math.cos(rad); + + out[0] = ax * bw + ay * bz; + out[1] = ay * bw - ax * bz; + out[2] = az * bw + aw * bz; + out[3] = aw * bw - az * bz; + return out; +}; + +/** + * Calculates the W component of a quat from the X, Y, and Z components. + * Assumes that quaternion is 1 unit in length. + * Any existing W component will be ignored. + * + * @param {quat} out the receiving quaternion + * @param {quat} a quat to calculate W component of + * @returns {quat} out + */ +quat.calculateW = function (out, a) { + var x = a[0], y = a[1], z = a[2]; + + out[0] = x; + out[1] = y; + out[2] = z; + out[3] = -Math.sqrt(Math.abs(1.0 - x * x - y * y - z * z)); + return out; +}; + +/** + * Caclulates the dot product of two quat's + * + * @param {quat} a the first operand + * @param {quat} b the second operand + * @returns {Number} dot product of a and b + */ +quat.dot = vec4.dot; + +/** + * Performs a linear interpolation between two quat's + * + * @param {quat} out the receiving quaternion + * @param {quat} a the first operand + * @param {quat} b the second operand + * @param {Number} t interpolation amount between the two inputs + * @returns {quat} out + */ +quat.lerp = vec4.lerp; + +/** + * Performs a spherical linear interpolation between two quat + * + * @param {quat} out the receiving quaternion + * @param {quat} a the first operand + * @param {quat} b the second operand + * @param {Number} t interpolation amount between the two inputs + * @returns {quat} out + */ +quat.slerp = function (out, a, b, t) { + var ax = a[0], ay = a[1], az = a[2], aw = a[3], + bx = b[0], by = b[1], bz = b[2], bw = a[3]; + + var cosHalfTheta = ax * bx + ay * by + az * bz + aw * bw, + halfTheta, + sinHalfTheta, + ratioA, + ratioB; + + if (Math.abs(cosHalfTheta) >= 1.0) { + if (out !== a) { + out[0] = ax; + out[1] = ay; + out[2] = az; + out[3] = aw; + } + return out; + } + + halfTheta = Math.acos(cosHalfTheta); + sinHalfTheta = Math.sqrt(1.0 - cosHalfTheta * cosHalfTheta); + + if (Math.abs(sinHalfTheta) < 0.001) { + out[0] = (ax * 0.5 + bx * 0.5); + out[1] = (ay * 0.5 + by * 0.5); + out[2] = (az * 0.5 + bz * 0.5); + out[3] = (aw * 0.5 + bw * 0.5); + return out; + } + + ratioA = Math.sin((1 - t) * halfTheta) / sinHalfTheta; + ratioB = Math.sin(t * halfTheta) / sinHalfTheta; + + out[0] = (ax * ratioA + bx * ratioB); + out[1] = (ay * ratioA + by * ratioB); + out[2] = (az * ratioA + bz * ratioB); + out[3] = (aw * ratioA + bw * ratioB); + + return out; +}; + +/** + * Calculates the inverse of a quat + * + * @param {quat} out the receiving quaternion + * @param {quat} a quat to calculate inverse of + * @returns {quat} out + */ +quat.invert = function(out, a) { + var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], + dot = a0*a0 + a1*a1 + a2*a2 + a3*a3, + invDot = dot ? 1.0/dot : 0; + + // TODO: Would be faster to return [0,0,0,0] immediately if dot == 0 + + out[0] = -a0*invDot; + out[1] = -a1*invDot; + out[2] = -a2*invDot; + out[3] = a3*invDot; + return out; +}; + +/** + * Calculates the conjugate of a quat + * If the quaternion is normalized, this function is faster than quat.inverse and produces the same result. + * + * @param {quat} out the receiving quaternion + * @param {quat} a quat to calculate conjugate of + * @returns {quat} out + */ +quat.conjugate = function (out, a) { + out[0] = -a[0]; + out[1] = -a[1]; + out[2] = -a[2]; + out[3] = a[3]; + return out; +}; + +/** + * Caclulates the length of a quat + * + * @param {quat} a vector to calculate length of + * @returns {Number} length of a + */ +quat.len = quat.length = vec4.length; + +/** + * Caclulates the squared length of a quat + * + * @param {quat} a vector to calculate squared length of + * @returns {Number} squared length of a + */ +quat.sqrLen = quat.squaredLength = vec4.squaredLength; + +/** + * Normalize a quat + * + * @param {quat} out the receiving quaternion + * @param {quat} a quaternion to normalize + * @returns {quat} out + */ +quat.normalize = vec4.normalize; + +/** + * Returns a string representation of a quatenion + * + * @param {quat} vec vector to represent as a string + * @returns {String} string representation of the vector + */ +quat.str = function (a) { + return 'quat(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ')'; +}; + +if(typeof(exports) !== 'undefined') { + exports.quat = quat; +} +; + + + + + + + + + + + })(shim.exports); +})(); + +})() +},{}],20:[function(require,module,exports){ +(function(){// Underscore.js 1.4.4 +// http://underscorejs.org +// (c) 2009-2013 Jeremy Ashkenas, DocumentCloud Inc. +// Underscore may be freely distributed under the MIT license. + +(function() { + + // Baseline setup + // -------------- + + // Establish the root object, `window` in the browser, or `global` on the server. + var root = this; + + // Save the previous value of the `_` variable. + var previousUnderscore = root._; + + // Establish the object that gets returned to break out of a loop iteration. + var breaker = {}; + + // Save bytes in the minified (but not gzipped) version: + var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; + + // Create quick reference variables for speed access to core prototypes. + var push = ArrayProto.push, + slice = ArrayProto.slice, + concat = ArrayProto.concat, + toString = ObjProto.toString, + hasOwnProperty = ObjProto.hasOwnProperty; + + // All **ECMAScript 5** native function implementations that we hope to use + // are declared here. + var + nativeForEach = ArrayProto.forEach, + nativeMap = ArrayProto.map, + nativeReduce = ArrayProto.reduce, + nativeReduceRight = ArrayProto.reduceRight, + nativeFilter = ArrayProto.filter, + nativeEvery = ArrayProto.every, + nativeSome = ArrayProto.some, + nativeIndexOf = ArrayProto.indexOf, + nativeLastIndexOf = ArrayProto.lastIndexOf, + nativeIsArray = Array.isArray, + nativeKeys = Object.keys, + nativeBind = FuncProto.bind; + + // Create a safe reference to the Underscore object for use below. + var _ = function(obj) { + if (obj instanceof _) return obj; + if (!(this instanceof _)) return new _(obj); + this._wrapped = obj; + }; + + // Export the Underscore object for **Node.js**, with + // backwards-compatibility for the old `require()` API. If we're in + // the browser, add `_` as a global object via a string identifier, + // for Closure Compiler "advanced" mode. + if (typeof exports !== 'undefined') { + if (typeof module !== 'undefined' && module.exports) { + exports = module.exports = _; + } + exports._ = _; + } else { + root._ = _; + } + + // Current version. + _.VERSION = '1.4.4'; + + // Collection Functions + // -------------------- + + // The cornerstone, an `each` implementation, aka `forEach`. + // Handles objects with the built-in `forEach`, arrays, and raw objects. + // Delegates to **ECMAScript 5**'s native `forEach` if available. + var each = _.each = _.forEach = function(obj, iterator, context) { + if (obj == null) return; + if (nativeForEach && obj.forEach === nativeForEach) { + obj.forEach(iterator, context); + } else if (obj.length === +obj.length) { + for (var i = 0, l = obj.length; i < l; i++) { + if (iterator.call(context, obj[i], i, obj) === breaker) return; + } + } else { + for (var key in obj) { + if (_.has(obj, key)) { + if (iterator.call(context, obj[key], key, obj) === breaker) return; + } + } + } + }; + + // Return the results of applying the iterator to each element. + // Delegates to **ECMAScript 5**'s native `map` if available. + _.map = _.collect = function(obj, iterator, context) { + var results = []; + if (obj == null) return results; + if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context); + each(obj, function(value, index, list) { + results[results.length] = iterator.call(context, value, index, list); + }); + return results; + }; + + var reduceError = 'Reduce of empty array with no initial value'; + + // **Reduce** builds up a single result from a list of values, aka `inject`, + // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available. + _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) { + var initial = arguments.length > 2; + if (obj == null) obj = []; + if (nativeReduce && obj.reduce === nativeReduce) { + if (context) iterator = _.bind(iterator, context); + return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator); + } + each(obj, function(value, index, list) { + if (!initial) { + memo = value; + initial = true; + } else { + memo = iterator.call(context, memo, value, index, list); + } + }); + if (!initial) throw new TypeError(reduceError); + return memo; + }; + + // The right-associative version of reduce, also known as `foldr`. + // Delegates to **ECMAScript 5**'s native `reduceRight` if available. + _.reduceRight = _.foldr = function(obj, iterator, memo, context) { + var initial = arguments.length > 2; + if (obj == null) obj = []; + if (nativeReduceRight && obj.reduceRight === nativeReduceRight) { + if (context) iterator = _.bind(iterator, context); + return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator); + } + var length = obj.length; + if (length !== +length) { + var keys = _.keys(obj); + length = keys.length; + } + each(obj, function(value, index, list) { + index = keys ? keys[--length] : --length; + if (!initial) { + memo = obj[index]; + initial = true; + } else { + memo = iterator.call(context, memo, obj[index], index, list); + } + }); + if (!initial) throw new TypeError(reduceError); + return memo; + }; + + // Return the first value which passes a truth test. Aliased as `detect`. + _.find = _.detect = function(obj, iterator, context) { + var result; + any(obj, function(value, index, list) { + if (iterator.call(context, value, index, list)) { + result = value; + return true; + } + }); + return result; + }; + + // Return all the elements that pass a truth test. + // Delegates to **ECMAScript 5**'s native `filter` if available. + // Aliased as `select`. + _.filter = _.select = function(obj, iterator, context) { + var results = []; + if (obj == null) return results; + if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context); + each(obj, function(value, index, list) { + if (iterator.call(context, value, index, list)) results[results.length] = value; + }); + return results; + }; + + // Return all the elements for which a truth test fails. + _.reject = function(obj, iterator, context) { + return _.filter(obj, function(value, index, list) { + return !iterator.call(context, value, index, list); + }, context); + }; + + // Determine whether all of the elements match a truth test. + // Delegates to **ECMAScript 5**'s native `every` if available. + // Aliased as `all`. + _.every = _.all = function(obj, iterator, context) { + iterator || (iterator = _.identity); + var result = true; + if (obj == null) return result; + if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context); + each(obj, function(value, index, list) { + if (!(result = result && iterator.call(context, value, index, list))) return breaker; + }); + return !!result; + }; + + // Determine if at least one element in the object matches a truth test. + // Delegates to **ECMAScript 5**'s native `some` if available. + // Aliased as `any`. + var any = _.some = _.any = function(obj, iterator, context) { + iterator || (iterator = _.identity); + var result = false; + if (obj == null) return result; + if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context); + each(obj, function(value, index, list) { + if (result || (result = iterator.call(context, value, index, list))) return breaker; + }); + return !!result; + }; + + // Determine if the array or object contains a given value (using `===`). + // Aliased as `include`. + _.contains = _.include = function(obj, target) { + if (obj == null) return false; + if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1; + return any(obj, function(value) { + return value === target; + }); + }; + + // Invoke a method (with arguments) on every item in a collection. + _.invoke = function(obj, method) { + var args = slice.call(arguments, 2); + var isFunc = _.isFunction(method); + return _.map(obj, function(value) { + return (isFunc ? method : value[method]).apply(value, args); + }); + }; + + // Convenience version of a common use case of `map`: fetching a property. + _.pluck = function(obj, key) { + return _.map(obj, function(value){ return value[key]; }); + }; + + // Convenience version of a common use case of `filter`: selecting only objects + // containing specific `key:value` pairs. + _.where = function(obj, attrs, first) { + if (_.isEmpty(attrs)) return first ? null : []; + return _[first ? 'find' : 'filter'](obj, function(value) { + for (var key in attrs) { + if (attrs[key] !== value[key]) return false; + } + return true; + }); + }; + + // Convenience version of a common use case of `find`: getting the first object + // containing specific `key:value` pairs. + _.findWhere = function(obj, attrs) { + return _.where(obj, attrs, true); + }; + + // Return the maximum element or (element-based computation). + // Can't optimize arrays of integers longer than 65,535 elements. + // See: https://bugs.webkit.org/show_bug.cgi?id=80797 + _.max = function(obj, iterator, context) { + if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { + return Math.max.apply(Math, obj); + } + if (!iterator && _.isEmpty(obj)) return -Infinity; + var result = {computed : -Infinity, value: -Infinity}; + each(obj, function(value, index, list) { + var computed = iterator ? iterator.call(context, value, index, list) : value; + computed >= result.computed && (result = {value : value, computed : computed}); + }); + return result.value; + }; + + // Return the minimum element (or element-based computation). + _.min = function(obj, iterator, context) { + if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { + return Math.min.apply(Math, obj); + } + if (!iterator && _.isEmpty(obj)) return Infinity; + var result = {computed : Infinity, value: Infinity}; + each(obj, function(value, index, list) { + var computed = iterator ? iterator.call(context, value, index, list) : value; + computed < result.computed && (result = {value : value, computed : computed}); + }); + return result.value; + }; + + // Shuffle an array. + _.shuffle = function(obj) { + var rand; + var index = 0; + var shuffled = []; + each(obj, function(value) { + rand = _.random(index++); + shuffled[index - 1] = shuffled[rand]; + shuffled[rand] = value; + }); + return shuffled; + }; + + // An internal function to generate lookup iterators. + var lookupIterator = function(value) { + return _.isFunction(value) ? value : function(obj){ return obj[value]; }; + }; + + // Sort the object's values by a criterion produced by an iterator. + _.sortBy = function(obj, value, context) { + var iterator = lookupIterator(value); + return _.pluck(_.map(obj, function(value, index, list) { + return { + value : value, + index : index, + criteria : iterator.call(context, value, index, list) + }; + }).sort(function(left, right) { + var a = left.criteria; + var b = right.criteria; + if (a !== b) { + if (a > b || a === void 0) return 1; + if (a < b || b === void 0) return -1; + } + return left.index < right.index ? -1 : 1; + }), 'value'); + }; + + // An internal function used for aggregate "group by" operations. + var group = function(obj, value, context, behavior) { + var result = {}; + var iterator = lookupIterator(value || _.identity); + each(obj, function(value, index) { + var key = iterator.call(context, value, index, obj); + behavior(result, key, value); + }); + return result; + }; + + // Groups the object's values by a criterion. Pass either a string attribute + // to group by, or a function that returns the criterion. + _.groupBy = function(obj, value, context) { + return group(obj, value, context, function(result, key, value) { + (_.has(result, key) ? result[key] : (result[key] = [])).push(value); + }); + }; + + // Counts instances of an object that group by a certain criterion. Pass + // either a string attribute to count by, or a function that returns the + // criterion. + _.countBy = function(obj, value, context) { + return group(obj, value, context, function(result, key) { + if (!_.has(result, key)) result[key] = 0; + result[key]++; + }); + }; + + // Use a comparator function to figure out the smallest index at which + // an object should be inserted so as to maintain order. Uses binary search. + _.sortedIndex = function(array, obj, iterator, context) { + iterator = iterator == null ? _.identity : lookupIterator(iterator); + var value = iterator.call(context, obj); + var low = 0, high = array.length; + while (low < high) { + var mid = (low + high) >>> 1; + iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid; + } + return low; + }; + + // Safely convert anything iterable into a real, live array. + _.toArray = function(obj) { + if (!obj) return []; + if (_.isArray(obj)) return slice.call(obj); + if (obj.length === +obj.length) return _.map(obj, _.identity); + return _.values(obj); + }; + + // Return the number of elements in an object. + _.size = function(obj) { + if (obj == null) return 0; + return (obj.length === +obj.length) ? obj.length : _.keys(obj).length; + }; + + // Array Functions + // --------------- + + // Get the first element of an array. Passing **n** will return the first N + // values in the array. Aliased as `head` and `take`. The **guard** check + // allows it to work with `_.map`. + _.first = _.head = _.take = function(array, n, guard) { + if (array == null) return void 0; + return (n != null) && !guard ? slice.call(array, 0, n) : array[0]; + }; + + // Returns everything but the last entry of the array. Especially useful on + // the arguments object. Passing **n** will return all the values in + // the array, excluding the last N. The **guard** check allows it to work with + // `_.map`. + _.initial = function(array, n, guard) { + return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n)); + }; + + // Get the last element of an array. Passing **n** will return the last N + // values in the array. The **guard** check allows it to work with `_.map`. + _.last = function(array, n, guard) { + if (array == null) return void 0; + if ((n != null) && !guard) { + return slice.call(array, Math.max(array.length - n, 0)); + } else { + return array[array.length - 1]; + } + }; + + // Returns everything but the first entry of the array. Aliased as `tail` and `drop`. + // Especially useful on the arguments object. Passing an **n** will return + // the rest N values in the array. The **guard** + // check allows it to work with `_.map`. + _.rest = _.tail = _.drop = function(array, n, guard) { + return slice.call(array, (n == null) || guard ? 1 : n); + }; + + // Trim out all falsy values from an array. + _.compact = function(array) { + return _.filter(array, _.identity); + }; + + // Internal implementation of a recursive `flatten` function. + var flatten = function(input, shallow, output) { + each(input, function(value) { + if (_.isArray(value)) { + shallow ? push.apply(output, value) : flatten(value, shallow, output); + } else { + output.push(value); + } + }); + return output; + }; + + // Return a completely flattened version of an array. + _.flatten = function(array, shallow) { + return flatten(array, shallow, []); + }; + + // Return a version of the array that does not contain the specified value(s). + _.without = function(array) { + return _.difference(array, slice.call(arguments, 1)); + }; + + // Produce a duplicate-free version of the array. If the array has already + // been sorted, you have the option of using a faster algorithm. + // Aliased as `unique`. + _.uniq = _.unique = function(array, isSorted, iterator, context) { + if (_.isFunction(isSorted)) { + context = iterator; + iterator = isSorted; + isSorted = false; + } + var initial = iterator ? _.map(array, iterator, context) : array; + var results = []; + var seen = []; + each(initial, function(value, index) { + if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) { + seen.push(value); + results.push(array[index]); + } + }); + return results; + }; + + // Produce an array that contains the union: each distinct element from all of + // the passed-in arrays. + _.union = function() { + return _.uniq(concat.apply(ArrayProto, arguments)); + }; + + // Produce an array that contains every item shared between all the + // passed-in arrays. + _.intersection = function(array) { + var rest = slice.call(arguments, 1); + return _.filter(_.uniq(array), function(item) { + return _.every(rest, function(other) { + return _.indexOf(other, item) >= 0; + }); + }); + }; + + // Take the difference between one array and a number of other arrays. + // Only the elements present in just the first array will remain. + _.difference = function(array) { + var rest = concat.apply(ArrayProto, slice.call(arguments, 1)); + return _.filter(array, function(value){ return !_.contains(rest, value); }); + }; + + // Zip together multiple lists into a single array -- elements that share + // an index go together. + _.zip = function() { + var args = slice.call(arguments); + var length = _.max(_.pluck(args, 'length')); + var results = new Array(length); + for (var i = 0; i < length; i++) { + results[i] = _.pluck(args, "" + i); + } + return results; + }; + + // Converts lists into objects. Pass either a single array of `[key, value]` + // pairs, or two parallel arrays of the same length -- one of keys, and one of + // the corresponding values. + _.object = function(list, values) { + if (list == null) return {}; + var result = {}; + for (var i = 0, l = list.length; i < l; i++) { + if (values) { + result[list[i]] = values[i]; + } else { + result[list[i][0]] = list[i][1]; + } + } + return result; + }; + + // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**), + // we need this function. Return the position of the first occurrence of an + // item in an array, or -1 if the item is not included in the array. + // Delegates to **ECMAScript 5**'s native `indexOf` if available. + // If the array is large and already in sort order, pass `true` + // for **isSorted** to use binary search. + _.indexOf = function(array, item, isSorted) { + if (array == null) return -1; + var i = 0, l = array.length; + if (isSorted) { + if (typeof isSorted == 'number') { + i = (isSorted < 0 ? Math.max(0, l + isSorted) : isSorted); + } else { + i = _.sortedIndex(array, item); + return array[i] === item ? i : -1; + } + } + if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted); + for (; i < l; i++) if (array[i] === item) return i; + return -1; + }; + + // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available. + _.lastIndexOf = function(array, item, from) { + if (array == null) return -1; + var hasIndex = from != null; + if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) { + return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item); + } + var i = (hasIndex ? from : array.length); + while (i--) if (array[i] === item) return i; + return -1; + }; + + // Generate an integer Array containing an arithmetic progression. A port of + // the native Python `range()` function. See + // [the Python documentation](http://docs.python.org/library/functions.html#range). + _.range = function(start, stop, step) { + if (arguments.length <= 1) { + stop = start || 0; + start = 0; + } + step = arguments[2] || 1; + + var len = Math.max(Math.ceil((stop - start) / step), 0); + var idx = 0; + var range = new Array(len); + + while(idx < len) { + range[idx++] = start; + start += step; + } + + return range; + }; + + // Function (ahem) Functions + // ------------------ + + // Create a function bound to a given object (assigning `this`, and arguments, + // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if + // available. + _.bind = function(func, context) { + if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); + var args = slice.call(arguments, 2); + return function() { + return func.apply(context, args.concat(slice.call(arguments))); + }; + }; + + // Partially apply a function by creating a version that has had some of its + // arguments pre-filled, without changing its dynamic `this` context. + _.partial = function(func) { + var args = slice.call(arguments, 1); + return function() { + return func.apply(this, args.concat(slice.call(arguments))); + }; + }; + + // Bind all of an object's methods to that object. Useful for ensuring that + // all callbacks defined on an object belong to it. + _.bindAll = function(obj) { + var funcs = slice.call(arguments, 1); + if (funcs.length === 0) funcs = _.functions(obj); + each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); }); + return obj; + }; + + // Memoize an expensive function by storing its results. + _.memoize = function(func, hasher) { + var memo = {}; + hasher || (hasher = _.identity); + return function() { + var key = hasher.apply(this, arguments); + return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments)); + }; + }; + + // Delays a function for the given number of milliseconds, and then calls + // it with the arguments supplied. + _.delay = function(func, wait) { + var args = slice.call(arguments, 2); + return setTimeout(function(){ return func.apply(null, args); }, wait); + }; + + // Defers a function, scheduling it to run after the current call stack has + // cleared. + _.defer = function(func) { + return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1))); + }; + + // Returns a function, that, when invoked, will only be triggered at most once + // during a given window of time. + _.throttle = function(func, wait) { + var context, args, timeout, result; + var previous = 0; + var later = function() { + previous = new Date; + timeout = null; + result = func.apply(context, args); + }; + return function() { + var now = new Date; + var remaining = wait - (now - previous); + context = this; + args = arguments; + if (remaining <= 0) { + clearTimeout(timeout); + timeout = null; + previous = now; + result = func.apply(context, args); + } else if (!timeout) { + timeout = setTimeout(later, remaining); + } + return result; + }; + }; + + // Returns a function, that, as long as it continues to be invoked, will not + // be triggered. The function will be called after it stops being called for + // N milliseconds. If `immediate` is passed, trigger the function on the + // leading edge, instead of the trailing. + _.debounce = function(func, wait, immediate) { + var timeout, result; + return function() { + var context = this, args = arguments; + var later = function() { + timeout = null; + if (!immediate) result = func.apply(context, args); + }; + var callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) result = func.apply(context, args); + return result; + }; + }; + + // Returns a function that will be executed at most one time, no matter how + // often you call it. Useful for lazy initialization. + _.once = function(func) { + var ran = false, memo; + return function() { + if (ran) return memo; + ran = true; + memo = func.apply(this, arguments); + func = null; + return memo; + }; + }; + + // Returns the first function passed as an argument to the second, + // allowing you to adjust arguments, run code before and after, and + // conditionally execute the original function. + _.wrap = function(func, wrapper) { + return function() { + var args = [func]; + push.apply(args, arguments); + return wrapper.apply(this, args); + }; + }; + + // Returns a function that is the composition of a list of functions, each + // consuming the return value of the function that follows. + _.compose = function() { + var funcs = arguments; + return function() { + var args = arguments; + for (var i = funcs.length - 1; i >= 0; i--) { + args = [funcs[i].apply(this, args)]; + } + return args[0]; + }; + }; + + // Returns a function that will only be executed after being called N times. + _.after = function(times, func) { + if (times <= 0) return func(); + return function() { + if (--times < 1) { + return func.apply(this, arguments); + } + }; + }; + + // Object Functions + // ---------------- + + // Retrieve the names of an object's properties. + // Delegates to **ECMAScript 5**'s native `Object.keys` + _.keys = nativeKeys || function(obj) { + if (obj !== Object(obj)) throw new TypeError('Invalid object'); + var keys = []; + for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key; + return keys; + }; + + // Retrieve the values of an object's properties. + _.values = function(obj) { + var values = []; + for (var key in obj) if (_.has(obj, key)) values.push(obj[key]); + return values; + }; + + // Convert an object into a list of `[key, value]` pairs. + _.pairs = function(obj) { + var pairs = []; + for (var key in obj) if (_.has(obj, key)) pairs.push([key, obj[key]]); + return pairs; + }; + + // Invert the keys and values of an object. The values must be serializable. + _.invert = function(obj) { + var result = {}; + for (var key in obj) if (_.has(obj, key)) result[obj[key]] = key; + return result; + }; + + // Return a sorted list of the function names available on the object. + // Aliased as `methods` + _.functions = _.methods = function(obj) { + var names = []; + for (var key in obj) { + if (_.isFunction(obj[key])) names.push(key); + } + return names.sort(); + }; + + // Extend a given object with all the properties in passed-in object(s). + _.extend = function(obj) { + each(slice.call(arguments, 1), function(source) { + if (source) { + for (var prop in source) { + obj[prop] = source[prop]; + } + } + }); + return obj; + }; + + // Return a copy of the object only containing the whitelisted properties. + _.pick = function(obj) { + var copy = {}; + var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); + each(keys, function(key) { + if (key in obj) copy[key] = obj[key]; + }); + return copy; + }; + + // Return a copy of the object without the blacklisted properties. + _.omit = function(obj) { + var copy = {}; + var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); + for (var key in obj) { + if (!_.contains(keys, key)) copy[key] = obj[key]; + } + return copy; + }; + + // Fill in a given object with default properties. + _.defaults = function(obj) { + each(slice.call(arguments, 1), function(source) { + if (source) { + for (var prop in source) { + if (obj[prop] == null) obj[prop] = source[prop]; + } + } + }); + return obj; + }; + + // Create a (shallow-cloned) duplicate of an object. + _.clone = function(obj) { + if (!_.isObject(obj)) return obj; + return _.isArray(obj) ? obj.slice() : _.extend({}, obj); + }; + + // Invokes interceptor with the obj, and then returns obj. + // The primary purpose of this method is to "tap into" a method chain, in + // order to perform operations on intermediate results within the chain. + _.tap = function(obj, interceptor) { + interceptor(obj); + return obj; + }; + + // Internal recursive comparison function for `isEqual`. + var eq = function(a, b, aStack, bStack) { + // Identical objects are equal. `0 === -0`, but they aren't identical. + // See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal. + if (a === b) return a !== 0 || 1 / a == 1 / b; + // A strict comparison is necessary because `null == undefined`. + if (a == null || b == null) return a === b; + // Unwrap any wrapped objects. + if (a instanceof _) a = a._wrapped; + if (b instanceof _) b = b._wrapped; + // Compare `[[Class]]` names. + var className = toString.call(a); + if (className != toString.call(b)) return false; + switch (className) { + // Strings, numbers, dates, and booleans are compared by value. + case '[object String]': + // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is + // equivalent to `new String("5")`. + return a == String(b); + case '[object Number]': + // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for + // other numeric values. + return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b); + case '[object Date]': + case '[object Boolean]': + // Coerce dates and booleans to numeric primitive values. Dates are compared by their + // millisecond representations. Note that invalid dates with millisecond representations + // of `NaN` are not equivalent. + return +a == +b; + // RegExps are compared by their source patterns and flags. + case '[object RegExp]': + return a.source == b.source && + a.global == b.global && + a.multiline == b.multiline && + a.ignoreCase == b.ignoreCase; + } + if (typeof a != 'object' || typeof b != 'object') return false; + // Assume equality for cyclic structures. The algorithm for detecting cyclic + // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. + var length = aStack.length; + while (length--) { + // Linear search. Performance is inversely proportional to the number of + // unique nested structures. + if (aStack[length] == a) return bStack[length] == b; + } + // Add the first object to the stack of traversed objects. + aStack.push(a); + bStack.push(b); + var size = 0, result = true; + // Recursively compare objects and arrays. + if (className == '[object Array]') { + // Compare array lengths to determine if a deep comparison is necessary. + size = a.length; + result = size == b.length; + if (result) { + // Deep compare the contents, ignoring non-numeric properties. + while (size--) { + if (!(result = eq(a[size], b[size], aStack, bStack))) break; + } + } + } else { + // Objects with different constructors are not equivalent, but `Object`s + // from different frames are. + var aCtor = a.constructor, bCtor = b.constructor; + if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) && + _.isFunction(bCtor) && (bCtor instanceof bCtor))) { + return false; + } + // Deep compare objects. + for (var key in a) { + if (_.has(a, key)) { + // Count the expected number of properties. + size++; + // Deep compare each member. + if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break; + } + } + // Ensure that both objects contain the same number of properties. + if (result) { + for (key in b) { + if (_.has(b, key) && !(size--)) break; + } + result = !size; + } + } + // Remove the first object from the stack of traversed objects. + aStack.pop(); + bStack.pop(); + return result; + }; + + // Perform a deep comparison to check if two objects are equal. + _.isEqual = function(a, b) { + return eq(a, b, [], []); + }; + + // Is a given array, string, or object empty? + // An "empty" object has no enumerable own-properties. + _.isEmpty = function(obj) { + if (obj == null) return true; + if (_.isArray(obj) || _.isString(obj)) return obj.length === 0; + for (var key in obj) if (_.has(obj, key)) return false; + return true; + }; + + // Is a given value a DOM element? + _.isElement = function(obj) { + return !!(obj && obj.nodeType === 1); + }; + + // Is a given value an array? + // Delegates to ECMA5's native Array.isArray + _.isArray = nativeIsArray || function(obj) { + return toString.call(obj) == '[object Array]'; + }; + + // Is a given variable an object? + _.isObject = function(obj) { + return obj === Object(obj); + }; + + // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp. + each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) { + _['is' + name] = function(obj) { + return toString.call(obj) == '[object ' + name + ']'; + }; + }); + + // Define a fallback version of the method in browsers (ahem, IE), where + // there isn't any inspectable "Arguments" type. + if (!_.isArguments(arguments)) { + _.isArguments = function(obj) { + return !!(obj && _.has(obj, 'callee')); + }; + } + + // Optimize `isFunction` if appropriate. + if (typeof (/./) !== 'function') { + _.isFunction = function(obj) { + return typeof obj === 'function'; + }; + } + + // Is a given object a finite number? + _.isFinite = function(obj) { + return isFinite(obj) && !isNaN(parseFloat(obj)); + }; + + // Is the given value `NaN`? (NaN is the only number which does not equal itself). + _.isNaN = function(obj) { + return _.isNumber(obj) && obj != +obj; + }; + + // Is a given value a boolean? + _.isBoolean = function(obj) { + return obj === true || obj === false || toString.call(obj) == '[object Boolean]'; + }; + + // Is a given value equal to null? + _.isNull = function(obj) { + return obj === null; + }; + + // Is a given variable undefined? + _.isUndefined = function(obj) { + return obj === void 0; + }; + + // Shortcut function for checking if an object has a given property directly + // on itself (in other words, not on a prototype). + _.has = function(obj, key) { + return hasOwnProperty.call(obj, key); + }; + + // Utility Functions + // ----------------- + + // Run Underscore.js in *noConflict* mode, returning the `_` variable to its + // previous owner. Returns a reference to the Underscore object. + _.noConflict = function() { + root._ = previousUnderscore; + return this; + }; + + // Keep the identity function around for default iterators. + _.identity = function(value) { + return value; + }; + + // Run a function **n** times. + _.times = function(n, iterator, context) { + var accum = Array(n); + for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i); + return accum; + }; + + // Return a random integer between min and max (inclusive). + _.random = function(min, max) { + if (max == null) { + max = min; + min = 0; + } + return min + Math.floor(Math.random() * (max - min + 1)); + }; + + // List of HTML entities for escaping. + var entityMap = { + escape: { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''', + '/': '/' + } + }; + entityMap.unescape = _.invert(entityMap.escape); + + // Regexes containing the keys and values listed immediately above. + var entityRegexes = { + escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'), + unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g') + }; + + // Functions for escaping and unescaping strings to/from HTML interpolation. + _.each(['escape', 'unescape'], function(method) { + _[method] = function(string) { + if (string == null) return ''; + return ('' + string).replace(entityRegexes[method], function(match) { + return entityMap[method][match]; + }); + }; + }); + + // If the value of the named property is a function then invoke it; + // otherwise, return it. + _.result = function(object, property) { + if (object == null) return null; + var value = object[property]; + return _.isFunction(value) ? value.call(object) : value; + }; + + // Add your own custom functions to the Underscore object. + _.mixin = function(obj) { + each(_.functions(obj), function(name){ + var func = _[name] = obj[name]; + _.prototype[name] = function() { + var args = [this._wrapped]; + push.apply(args, arguments); + return result.call(this, func.apply(_, args)); + }; + }); + }; + + // Generate a unique integer id (unique within the entire client session). + // Useful for temporary DOM ids. + var idCounter = 0; + _.uniqueId = function(prefix) { + var id = ++idCounter + ''; + return prefix ? prefix + id : id; + }; + + // By default, Underscore uses ERB-style template delimiters, change the + // following template settings to use alternative delimiters. + _.templateSettings = { + evaluate : /<%([\s\S]+?)%>/g, + interpolate : /<%=([\s\S]+?)%>/g, + escape : /<%-([\s\S]+?)%>/g + }; + + // When customizing `templateSettings`, if you don't want to define an + // interpolation, evaluation or escaping regex, we need one that is + // guaranteed not to match. + var noMatch = /(.)^/; + + // Certain characters need to be escaped so that they can be put into a + // string literal. + var escapes = { + "'": "'", + '\\': '\\', + '\r': 'r', + '\n': 'n', + '\t': 't', + '\u2028': 'u2028', + '\u2029': 'u2029' + }; + + var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; + + // JavaScript micro-templating, similar to John Resig's implementation. + // Underscore templating handles arbitrary delimiters, preserves whitespace, + // and correctly escapes quotes within interpolated code. + _.template = function(text, data, settings) { + var render; + settings = _.defaults({}, settings, _.templateSettings); + + // Combine delimiters into one regular expression via alternation. + var matcher = new RegExp([ + (settings.escape || noMatch).source, + (settings.interpolate || noMatch).source, + (settings.evaluate || noMatch).source + ].join('|') + '|$', 'g'); + + // Compile the template source, escaping string literals appropriately. + var index = 0; + var source = "__p+='"; + text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { + source += text.slice(index, offset) + .replace(escaper, function(match) { return '\\' + escapes[match]; }); + + if (escape) { + source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; + } + if (interpolate) { + source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; + } + if (evaluate) { + source += "';\n" + evaluate + "\n__p+='"; + } + index = offset + match.length; + return match; + }); + source += "';\n"; + + // If a variable is not specified, place data values in local scope. + if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; + + source = "var __t,__p='',__j=Array.prototype.join," + + "print=function(){__p+=__j.call(arguments,'');};\n" + + source + "return __p;\n"; + + try { + render = new Function(settings.variable || 'obj', '_', source); + } catch (e) { + e.source = source; + throw e; + } + + if (data) return render(data, _); + var template = function(data) { + return render.call(this, data, _); + }; + + // Provide the compiled function source as a convenience for precompilation. + template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; + + return template; + }; + + // Add a "chain" function, which will delegate to the wrapper. + _.chain = function(obj) { + return _(obj).chain(); + }; + + // OOP + // --------------- + // If Underscore is called as a function, it returns a wrapped object that + // can be used OO-style. This wrapper holds altered versions of all the + // underscore functions. Wrapped objects may be chained. + + // Helper function to continue chaining intermediate results. + var result = function(obj) { + return this._chain ? _(obj).chain() : obj; + }; + + // Add all of the Underscore functions to the wrapper object. + _.mixin(_); + + // Add all mutator Array functions to the wrapper. + each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { + var method = ArrayProto[name]; + _.prototype[name] = function() { + var obj = this._wrapped; + method.apply(obj, arguments); + if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0]; + return result.call(this, obj); + }; + }); + + // Add all accessor Array functions to the wrapper. + each(['concat', 'join', 'slice'], function(name) { + var method = ArrayProto[name]; + _.prototype[name] = function() { + return result.call(this, method.apply(this._wrapped, arguments)); + }; + }); + + _.extend(_.prototype, { + + // Start chaining a wrapped Underscore object. + chain: function() { + this._chain = true; + return this; + }, + + // Extracts the result from a wrapped and chained object. + value: function() { + return this._wrapped; + } + + }); + +}).call(this); + +})() +},{}],21:[function(require,module,exports){ +if (typeof(window.requestAnimationFrame) !== 'function') { + window.requestAnimationFrame = ( + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function(callback) { setTimeout(callback, 1000 / 60); } + ); +} + +Leap = require("../lib/index"); + +},{"../lib/index":8}]},{},[21]) +; \ No newline at end of file diff --git a/assets/leap.min.js b/assets/leap.min.js deleted file mode 100644 index cc3ec62..0000000 --- a/assets/leap.min.js +++ /dev/null @@ -1,3 +0,0 @@ -(function(e,t,n){function i(n,s){if(!t[n]){if(!e[n]){var o=typeof require=="function"&&require;if(!s&&o)return o(n,!0);if(r)return r(n,!0);throw new Error("Cannot find module '"+n+"'")}var u=t[n]={exports:{}};e[n][0].call(u.exports,function(t){var r=e[n][1][t];return i(r?r:t)},u,u.exports)}return t[n].exports}var r=typeof require=="function"&&require;for(var s=0;s0){for(var af=0;af0){var ae=c(Y);if(ae){if(F(o[af].swfVersion)&&!(M.wk&&M.wk<312)){w(Y,true);if(ab){aa.success=true;aa.ref=z(Y);ab(aa)}}else{if(o[af].expressInstall&&A()){var ai={};ai.data=o[af].expressInstall;ai.width=ae.getAttribute("width")||"0";ai.height=ae.getAttribute("height")||"0";if(ae.getAttribute("class")){ai.styleclass=ae.getAttribute("class")}if(ae.getAttribute("align")){ai.align=ae.getAttribute("align")}var ah={};var X=ae.getElementsByTagName("param");var ac=X.length;for(var ad=0;ad'}}aa.outerHTML='"+af+"";N[N.length]=ai.id;X=c(ai.id)}else{var Z=C(r);Z.setAttribute("type",q);for(var ac in ai){if(ai[ac]!=Object.prototype[ac]){if(ac.toLowerCase()=="styleclass"){Z.setAttribute("class",ai[ac])}else{if(ac.toLowerCase()!="classid"){Z.setAttribute(ac,ai[ac])}}}}for(var ab in ag){if(ag[ab]!=Object.prototype[ab]&&ab.toLowerCase()!="movie"){e(Z,ab,ag[ab])}}aa.parentNode.replaceChild(Z,aa);X=Z}}return X}function e(Z,X,Y){var aa=C("param");aa.setAttribute("name",X);aa.setAttribute("value",Y);Z.appendChild(aa)}function y(Y){var X=c(Y);if(X&&X.nodeName=="OBJECT"){if(M.ie&&M.win){X.style.display="none";(function(){if(X.readyState==4){b(Y)}else{setTimeout(arguments.callee,10)}})()}else{X.parentNode.removeChild(X)}}}function b(Z){var Y=c(Z);if(Y){for(var X in Y){if(typeof Y[X]=="function"){Y[X]=null}}Y.parentNode.removeChild(Y)}}function c(Z){var X=null;try{X=j.getElementById(Z)}catch(Y){}return X}function C(X){return j.createElement(X)}function i(Z,X,Y){Z.attachEvent(X,Y);I[I.length]=[Z,X,Y]}function F(Z){var Y=M.pv,X=Z.split(".");X[0]=parseInt(X[0],10);X[1]=parseInt(X[1],10)||0;X[2]=parseInt(X[2],10)||0;return Y[0]>X[0]||Y[0]==X[0]&&Y[1]>X[1]||Y[0]==X[0]&&Y[1]==X[1]&&Y[2]>=X[2]?true:false}function v(ac,Y,ad,ab){if(M.ie&&M.mac){return}var aa=j.getElementsByTagName("head")[0];if(!aa){return}var X=ad&&typeof ad=="string"?ad:"screen";if(ab){n=null;G=null}if(!n||G!=X){var Z=C("style");Z.setAttribute("type","text/css");Z.setAttribute("media",X);n=aa.appendChild(Z);if(M.ie&&M.win&&typeof j.styleSheets!=D&&j.styleSheets.length>0){n=j.styleSheets[j.styleSheets.length-1]}G=X}if(M.ie&&M.win){if(n&&typeof n.addRule==r){n.addRule(ac,Y)}}else{if(n&&typeof j.createTextNode!=D){n.appendChild(j.createTextNode(ac+" {"+Y+"}"))}}}function w(Z,X){if(!m){return}var Y=X?"visible":"hidden";if(J&&c(Z)){c(Z).style.visibility=Y}else{v("#"+Z,"visibility:"+Y)}}function L(Y){var Z=/[\\\"<>\.;]/;var X=Z.exec(Y)!=null;return X&&typeof encodeURIComponent!=D?encodeURIComponent(Y):Y}var d=function(){if(M.ie&&M.win){window.attachEvent("onunload",function(){var ac=I.length;for(var ab=0;ab= 10.0.0 is required.");return}if(location.protocol=="file:"){logger.error("WARNING: web-socket-js doesn't work in file:///... URL "+"unless you set Flash Security Settings properly. "+"Open the page via Web server i.e. http://...")}window.WebSocket=function(url,protocols,proxyHost,proxyPort,headers){var self=this;self.__id=WebSocket.__nextId++;WebSocket.__instances[self.__id]=self;self.readyState=WebSocket.CONNECTING;self.bufferedAmount=0;self.__events={};if(!protocols){protocols=[]}else if(typeof protocols=="string"){protocols=[protocols]}self.__createTask=setTimeout(function(){WebSocket.__addTask(function(){self.__createTask=null;WebSocket.__flash.create(self.__id,url,protocols,proxyHost||null,proxyPort||0,headers||null)})},0)};WebSocket.prototype.send=function(data){if(this.readyState==WebSocket.CONNECTING){throw"INVALID_STATE_ERR: Web Socket connection has not been established"}var result=WebSocket.__flash.send(this.__id,encodeURIComponent(data));if(result<0){return true}else{this.bufferedAmount+=result;return false}};WebSocket.prototype.close=function(){if(this.__createTask){clearTimeout(this.__createTask);this.__createTask=null;this.readyState=WebSocket.CLOSED;return}if(this.readyState==WebSocket.CLOSED||this.readyState==WebSocket.CLOSING){return}this.readyState=WebSocket.CLOSING;WebSocket.__flash.close(this.__id)};WebSocket.prototype.addEventListener=function(type,listener,useCapture){if(!(type in this.__events)){this.__events[type]=[]}this.__events[type].push(listener)};WebSocket.prototype.removeEventListener=function(type,listener,useCapture){if(!(type in this.__events))return;var events=this.__events[type];for(var i=events.length-1;i>=0;--i){if(events[i]===listener){events.splice(i,1);break}}};WebSocket.prototype.dispatchEvent=function(event){var events=this.__events[event.type]||[];for(var i=0;i0)return Math.acos(this.dot(other)/denom);else return 0},cross:function(other){var x=this.y*other.z-other.y*this.z;var y=this.x*other.z-other.x*this.z;var z=this.x*other.y-other.x*this.y;return new Vector([x,y,z])},distanceTo:function(other){return this.minus(other).magnitude()},dot:function(other){return this.x*other.x+this.y*other.y+this.z*other.z},plus:function(other){return new Vector([this.x+other.x,this.y+other.y,this.z+other.z])},minus:function(other){return new Vector([this.x-other.x,this.y-other.y,this.z-other.z])},multiply:function(scalar){return new Vector([this.x*scalar,this.y*scalar,this.z*scalar])},dividedBy:function(scalar){return new Vector([this.x/scalar,this.y/scalar,this.z/scalar])},magnitude:function(){return Math.sqrt(this.magnitudeSquared())},magnitudeSquared:function(){return Math.pow(this.x,2)+Math.pow(this.y,2)+Math.pow(this.z,2)},normalized:function(){var magnitude=this.magnitude();if(magnitude>0)return this.dividedBy(magnitude);else return new Vector},pitch:function(){return Math.atan2(this.y,-this.z)},roll:function(){return Math.atan2(this.x,-this.y)},yaw:function(){return Math.atan2(this.x,-this.z)},toArray:function(){return[this.x,this.y,this.z]},toString:function(){return"{x:"+this.x+",y:"+this.y+",z:"+this.z+"}"},compare:function(other){return this.x==other.x&&this.y==other.y&&this.z==other.z},isValid:function(){return this.x!=NaN&&this.x>-Infinity&&this.x-Infinity&&this.y-Infinity&&this.z=this.size)return undefined;if(i>=this._buf.length)return undefined;return this._buf[(this.pos-i-1)%this.size]};CircularBuffer.prototype.push=function(o){this._buf[this.pos%this.size]=o;return this.pos++}},{}],15:[function(require,module,exports){var process=module.exports={};process.nextTick=function(){var canSetImmediate=typeof window!=="undefined"&&window.setImmediate;var canPost=typeof window!=="undefined"&&window.postMessage&&window.addEventListener;if(canSetImmediate){return function(f){return window.setImmediate(f)}}if(canPost){var queue=[];window.addEventListener("message",function(ev){if(ev.source===window&&ev.data==="process-tick"){ev.stopPropagation();if(queue.length>0){var fn=queue.shift();fn()}}},true);return function nextTick(fn){queue.push(fn);window.postMessage("process-tick","*")}}return function nextTick(fn){setTimeout(fn,0)}}();process.title="browser";process.browser=true;process.env={};process.argv=[];process.binding=function(name){throw new Error("process.binding is not supported")};process.cwd=function(){return"/"};process.chdir=function(dir){throw new Error("process.chdir is not supported")}},{}],16:[function(require,module,exports){(function(process){if(!process.EventEmitter)process.EventEmitter=function(){};var EventEmitter=exports.EventEmitter=process.EventEmitter;var isArray=typeof Array.isArray==="function"?Array.isArray:function(xs){return Object.prototype.toString.call(xs)==="[object Array]"};function indexOf(xs,x){if(xs.indexOf)return xs.indexOf(x);for(var i=0;i0&&this._events[type].length>m){this._events[type].warned=true;console.error("(node) warning: possible EventEmitter memory "+"leak detected. %d listeners added. "+"Use emitter.setMaxListeners() to increase limit.",this._events[type].length);console.trace()}}this._events[type].push(listener)}else{this._events[type]=[this._events[type],listener]}return this};EventEmitter.prototype.on=EventEmitter.prototype.addListener;EventEmitter.prototype.once=function(type,listener){var self=this;self.on(type,function g(){self.removeListener(type,g);listener.apply(this,arguments)});return this};EventEmitter.prototype.removeListener=function(type,listener){if("function"!==typeof listener){throw new Error("removeListener only takes instances of Function")}if(!this._events||!this._events[type])return this;var list=this._events[type];if(isArray(list)){var i=indexOf(list,listener);if(i<0)return this;list.splice(i,1);if(list.length==0)delete this._events[type]}else if(this._events[type]===listener){delete this._events[type]}return this};EventEmitter.prototype.removeAllListeners=function(type){if(arguments.length===0){this._events={};return this}if(type&&this._events&&this._events[type])this._events[type]=null;return this};EventEmitter.prototype.listeners=function(type){if(!this._events)this._events={};if(!this._events[type])this._events[type]=[];if(!isArray(this._events[type])){this._events[type]=[this._events[type]]}return this._events[type]}})(require("__browserify_process"))},{__browserify_process:15}],7:[function(require,module,exports){var Vector=require("./vector").Vector;var Gesture=exports.Gesture=function(data){var gesture=undefined;switch(data.type){case"circle":gesture=new CircleGesture(data);break;case"swipe":gesture=new SwipeGesture(data);break;case"screenTap":gesture=new ScreenTapGesture(data);break;case"keyTap":gesture=new KeyTapGesture(data);break;default:throw"unkown gesture type"}gesture.id=data.id;gesture.handIds=data.handIds;gesture.pointableIds=data.pointableIds;gesture.duration=data.duration;gesture.state=data.state;gesture.type=data.type;return gesture};var CircleGesture=function(data){this.center=new Vector(data.center);this.normal=new Vector(data.normal);this.progress=data.progress;this.radius=data.radius};var SwipeGesture=function(data){this.startPosition=new Vector(data.startPosition);this.position=new Vector(data.position);this.direction=new Vector(data.direction);this.speed=data.speed};var ScreenTapGesture=function(data){this.position=new Vector(data.position);this.direction=new Vector(data.direction);this.progress=data.progress};var KeyTapGesture=function(data){this.position=new Vector(data.position);this.direction=new Vector(data.direction);this.progress=data.progress}},{"./vector":10}],9:[function(require,module,exports){var Vector=require("./vector").Vector;var Pointable=exports.Pointable=function(data){this.valid=true;this.id=data.id;this.handId=data.handId;this.length=data.length;this.tool=data.tool;this.width=data.width;this.direction=new Vector(data.direction);this.tipPosition=new Vector(data.tipPosition);this.tipVelocity=new Vector(data.tipVelocity)};Pointable.prototype.toString=function(){if(this.tool==true){return"Pointable [ id:"+this.id+" "+this.length+"mmx | with:"+this.width+"mm | direction:"+this.direction+" ]"}else{return"Pointable [ id:"+this.id+" "+this.length+"mmx | direction: "+this.direction+" ]"}};Pointable.Invalid={valid:false}},{"./vector":10}],11:[function(require,module,exports){var Vector=require("./vector").Vector;var Matrix=exports.Matrix=function(data){if(data instanceof Matrix){this.xBasis=new Vector(data.xBasis);this.yBasis=new Vector(data.yBasis);this.zBasis=new Vector(data.zBasis);this.origin=new Vector(data.origin)}else if(data instanceof Array){if(data[0]instanceof Vector&&typeof data[1]=="number"){this.setRotation(data[0],data[1]);this.origin=new Vector(data[2])}else{this.xBasis=new Vector(data[0]);this.yBasis=new Vector(data[1]);this.zBasis=new Vector(data[2]);this.origin=new Vector(data[3])}}else{this.xBasis=new Vector([1,0,0]);this.yBasis=new Vector([0,1,0]);this.zBasis=new Vector([0,0,1]);this.origin=new Vector([0,0,0])}};Matrix.prototype={setRotation:function(_axis,angle){var axis=_axis.normalized();var s=Math.sin(angle);var c=Math.cos(angle);var C=1-c;this.xBasis=new Vector([axis.x*axis.x*C+c,axis.x*axis.y*C-axis.z*s,axis.x*axis.z*C+axis.y*s]);this.yBasis=new Vector([axis.y*axis.x*C+axis.z*s,axis.y*axis.y*C+c,axis.y*axis.z*C-axis.x*s]);this.zBasis=new Vector([axis.z*axis.x*C-axis.y*s,axis.z*axis.y*C+axis.x*s,axis.z*axis.z*C+c])},transformPoint:function(data){return this.origin.plus(this.transformDirection(data))},transformDirection:function(data){var x=this.xBasis.multiply(data.x);var y=this.yBasis.multiply(data.y);var z=this.zBasis.multiply(data.z);return x.plus(y).plus(z)},times:function(other){var x=this.transformDirection(other.xBasis);var y=this.transformDirection(other.yBasis);var z=this.transformDirection(other.zBasis);var o=this.transformPoint(other.origin);return new Matrix([x,y,z,o])},rigidInverse:function(){var x=new Vector([this.xBasis.x,this.yBasis.x,this.zBasis.x]);var y=new Vector([this.xBasis.y,this.yBasis.y,this.zBasis.y]);var z=new Vector([this.xBasis.z,this.yBasis.z,this.zBasis.z]);var rotInverse=new Matrix([x,y,z]);rotInverse.origin=rotInverse.transformDirection(Vector.zero().minus(this.origin));return rotInverse},toArray3x3:function(output){if(output==null)output=[];else output.length=0;output[0]=this.xBasis.x;output[1]=this.xBasis.y;output[2]=this.xBasis.z;output[3]=this.yBasis.x;output[4]=this.yBasis.y;output[5]=this.yBasis.z;output[6]=this.zBasis.x;output[7]=this.zBasis.y;output[8]=this.zBasis.z;return output},toArray4x4:function(output){if(output==null)output=[];else output.length=0;output[0]=this.xBasis.x;output[1]=this.xBasis.y;output[2]=this.xBasis.z;output[3]=0;output[4]=this.yBasis.x;output[5]=this.yBasis.y;output[6]=this.yBasis.z;output[7]=0;output[8]=this.zBasis.x;output[9]=this.zBasis.y;output[10]=this.zBasis.z;output[11]=0;output[12]=this.origin.x;output[13]=this.origin.y;output[14]=this.origin.z;output[15]=1;return output},toString:function(){return"{xBasis:"+this.xBasis+",yBasis:"+this.yBasis+",zBasis:"+this.zBasis+",origin:"+this.origin+"}"},compare:function(other){return this.xBasis.compare(other.xBasis)&&this.yBasis.compare(other.yBasis)&&this.zBasis.compare(other.zBasis)&&this.origin.compare(other.origin)}};Matrix.identity=function(){return new Matrix}},{"./vector":10}],12:[function(require,module,exports){var Connection=exports.Connection=require("./base_connection").Connection;Connection.prototype.setupSocket=function(){var connection=this;var socket=new WebSocket("ws://"+this.host+":"+this.port);socket.onopen=function(){connection.handleOpen()};socket.onmessage=function(message){connection.handleData(message.data)};socket.onclose=function(){connection.handleClose()};return socket};Connection.prototype.teardownSocket=function(){this.socket.close();delete this.socket;delete this.protocol}},{"./base_connection":17}],14:[function(require,module,exports){exports.UI={Region:require("./ui/region").Region,Cursor:require("./ui/cursor").Cursor}},{"./ui/region":18,"./ui/cursor":19}],20:[function(require,module,exports){var Pipeline=exports.Pipeline=function(){this.steps=[]};Pipeline.prototype.addStep=function(step){this.steps.push(step)};Pipeline.prototype.run=function(frame){var stepsLength=this.steps.length;for(var i=0;i!=stepsLength;i++){if(!frame)break;frame=this.steps[i](frame)}return frame}},{}],19:[function(require,module,exports){var Cursor=exports.Cursor=function(){return function(frame){var pointable=frame.pointables.sort(function(a,b){return a.z-b.z -})[0];if(pointable&&pointable.valid){frame.cursorPosition=pointable.tipPosition}return frame}}},{}],5:[function(require,module,exports){var inNode=typeof window==="undefined";var Frame=require("./frame").Frame,CircularBuffer=require("./circular_buffer").CircularBuffer,Pipeline=require("./pipeline").Pipeline,EventEmitter=require("events").EventEmitter,_=require("underscore");var Controller=exports.Controller=function(opts){this.opts=_.defaults(opts||{},{frameEventName:this.useAnimationLoop()?"animationFrame":"connectionFrame"});this.history=new CircularBuffer(200);var controller=this;this.lastFrame=Frame.Invalid;this.lastValidFrame=Frame.Invalid;this.lastConnectionFrame=Frame.Invalid;var connectionType=this.opts.connectionType||this.connectionType();this.connection=new connectionType(this.opts);this.accumulatedGestures=[];this.connection.on("frame",function(frame){if(frame.gestures){controller.accumulatedGestures=controller.accumulatedGestures.concat(frame.gestures)}controller.processFrame(frame)});this.on(this.opts.frameEventName,function(frame){controller.processFinishedFrame(frame)});this.connection.on("ready",function(){controller.emit("ready")});this.connection.on("connect",function(){controller.emit("connect")});this.connection.on("disconnect",function(){controller.emit("disconnect")})};Controller.prototype.inBrowser=function(){return!inNode};Controller.prototype.useAnimationLoop=function(){return this.inBrowser()&&typeof chrome==="undefined"};Controller.prototype.connectionType=function(){return(this.inBrowser()?require("./connection"):require("./node_connection")).Connection};Controller.prototype.connect=function(){var controller=this;if(this.connection.connect()&&this.inBrowser()){var callback=function(){controller.emit("animationFrame",controller.lastConnectionFrame);if(controller.opts.supressAnimationLoop!==true)window.requestAnimFrame(callback)};if(controller.opts.supressAnimationLoop!==true){window.requestAnimFrame(callback)}}};Controller.prototype.disconnect=function(){this.connection.disconnect()};Controller.prototype.frame=function(num){return this.history.get(num)||Frame.Invalid};Controller.prototype.loop=function(callback){switch(callback.length){case 1:this.on(this.opts.frameEventName,callback);break;case 2:var controller=this;var scheduler=null;var immediateRunnerCallback=function(frame){callback(frame,function(){if(controller.lastFrame!=frame){immediateRunnerCallback(controller.lastFrame)}else{controller.once(controller.opts.frameEventName,immediateRunnerCallback)}})};this.once(this.opts.frameEventName,immediateRunnerCallback);break}this.connect()};Controller.prototype.addStep=function(step){if(!this.pipeline)this.pipeline=new Pipeline(this);this.pipeline.addStep(step)};Controller.prototype.processFrame=function(frame){if(this.pipeline){frame=this.pipeline.run(frame);if(!frame)frame=Frame.Invalid}this.lastConnectionFrame=frame;this.emit("connectionFrame",frame)};Controller.prototype.processFinishedFrame=function(frame){this.lastFrame=frame;if(frame.valid){this.lastValidFrame=frame}if(frame.gestures){frame.gestures=this.accumulatedGestures;this.accumulatedGestures=[]}frame.controller=this;frame.historyIdx=this.history.push(frame);this.emit("frame",frame)};_.extend(Controller.prototype,EventEmitter.prototype)},{events:16,"./frame":6,"./circular_buffer":13,"./pipeline":20,"./connection":12,"./node_connection":21,underscore:22}],6:[function(require,module,exports){var Hand=require("./hand").Hand,Pointable=require("./pointable").Pointable,Gesture=require("./gesture").Gesture,Vector=require("./vector").Vector,Matrix=require("./matrix").Matrix,_=require("underscore");var Frame=exports.Frame=function(data){this.valid=true;this.id=data.id;this.timestamp=data.timestamp;this.hands=[];this.handsMap={};this.pointables=[];this.tools=[];this.fingers=[];this.gestures=[];this.pointablesMap={};this._translation=new Vector(data.t);this.rotation=new Matrix(data.r);this._scaleFactor=data.s;this.data=data;this.type="frame";var handMap={};for(var handIdx=0,handCount=data.hands.length;handIdx!=handCount;handIdx++){var hand=new Hand(data.hands[handIdx]);hand.frame=this;this.hands.push(hand);this.handsMap[hand.id]=hand;handMap[hand.id]=handIdx}for(var pointableIdx=0,pointableCount=data.pointables.length;pointableIdx!=pointableCount;pointableIdx++){var pointable=new Pointable(data.pointables[pointableIdx]);pointable.frame=this;this.pointables.push(pointable);this.pointablesMap[pointable.id]=pointable;(pointable.tool?this.tools:this.fingers).push(pointable);if(pointable.handId&&handMap.hasOwnProperty(pointable.handId)){var hand=this.hands[handMap[pointable.handId]];hand.pointables.push(pointable);(pointable.tool?hand.tools:hand.fingers).push(pointable)}}if(data.gestures){for(var gestureIdx=0,gestureCount=data.gestures.length;gestureIdx!=gestureCount;gestureIdx++){this.gestures.push(Gesture(data.gestures[gestureIdx]))}}};Frame.prototype.tool=function(id){var pointable=this.pointable(id);return pointable.tool?pointable:Pointable.Invalid};Frame.prototype.pointable=function(id){return this.pointablesMap[id]||Pointable.Invalid};Frame.prototype.finger=function(id){var pointable=this.pointable(id);return!pointable.tool?pointable:Pointable.Invalid};Frame.prototype.hand=function(id){return this.handsMap[id]||Hand.Invalid};Frame.prototype.rotationAngle=function(sinceFrame,axis){if(!this.valid||!sinceFrame.valid)return 0;var rot=this.rotationMatrix(sinceFrame);var cs=(rot.xBasis.x+rot.yBasis.y+rot.zBasis.z-1)*.5;var angle=Math.acos(cs);return isNaN(angle)?0:angle};Frame.prototype.rotationAxis=function(sinceFrame){if(!this.valid||!sinceFrame.valid)return Vector.zero();var x=this.rotation.zBasis.y-sinceFrame.rotation.yBasis.z;var y=this.rotation.xBasis.z-sinceFrame.rotation.zBasis.x;var z=this.rotation.yBasis.x-sinceFrame.rotation.xBasis.y;var vec=new Vector([x,y,z]);return vec.normalized()};Frame.prototype.rotationMatrix=function(sinceFrame){if(!this.valid||!sinceFrame.valid)return Matrix.identity();var xBasis=new Vector([this.rotation.xBasis.x,this.rotation.yBasis.x,this.rotation.zBasis.x]);var yBasis=new Vector([this.rotation.xBasis.y,this.rotation.yBasis.y,this.rotation.zBasis.y]);var zBasis=new Vector([this.rotation.xBasis.z,this.rotation.yBasis.z,this.rotation.zBasis.z]);var transpose=new Matrix([xBasis,yBasis,zBasis]);return sinceFrame.rotation.times(transpose)};Frame.prototype.scaleFactor=function(sinceFrame){if(!this.valid||!sinceFrame.valid)return 1;return Math.exp(this._scaleFactor-sinceFrame._scaleFactor)};Frame.prototype.translation=function(sinceFrame){if(!this.valid||!sinceFrame.valid)return Vector.zero();var x=this._translation.x-sinceFrame._translation.x;var y=this._translation.y-sinceFrame._translation.y;var z=this._translation.z-sinceFrame._translation.z;return new Vector([x,y,z])};Frame.prototype.toString=function(){var str="Frame [ id:"+this.id+" | timestamp:"+this.timestamp+" | Hand count:("+this.hands.length+") | Pointable count:("+this.pointables.length+")";if(this.gestures)str+=" | Gesture count:("+this.gestures.length+")";str+=" ]";return str};Frame.prototype.dump=function(){var out="";out+="Frame Info:
";out+=this.toString();out+="

Hands:
";for(var handIdx=0,handCount=this.hands.length;handIdx!=handCount;handIdx++){out+=" "+this.hands[handIdx].toString()+"
"}out+="

Pointables:
";for(var pointableIdx=0,pointableCount=this.pointables.length;pointableIdx!=pointableCount;pointableIdx++){out+=" "+this.pointables[pointableIdx].toString()+"
"}if(this.gestures){out+="

Gestures:
";for(var gestureIdx=0,gestureCount=this.gestures.length;gestureIdx!=gestureCount;gestureIdx++){out+=" "+this.gestures[gestureIdx].toString()+"
"}}out+="

Raw JSON:
";out+=JSON.stringify(this.data);return out};Frame.Invalid={valid:false,hands:[],fingers:[],tools:[],gestures:[],pointables:[],pointable:function(){return Pointable.Invalid},finger:function(){return Pointable.Invalid},hand:function(){return Hand.Invalid},toString:function(){return"invalid frame"},dump:function(){return this.toString()}}},{"./hand":8,"./pointable":9,"./gesture":7,"./vector":10,"./matrix":11,underscore:22}],8:[function(require,module,exports){var Pointable=require("./pointable").Pointable,Vector=require("./vector").Vector,Matrix=require("./matrix").Matrix,_=require("underscore");var Hand=exports.Hand=function(data){this.id=data.id;this.palmPosition=new Vector(data.palmPosition);this.direction=new Vector(data.direction);this.palmVelocity=new Vector(data.palmVelocity);this.palmNormal=new Vector(data.palmNormal);this.sphereCenter=new Vector(data.sphereCenter);this.sphereRadius=data.sphereRadius;this.valid=true;this.pointables=[];this.fingers=[];this.tools=[];this._translation=new Vector(data.t);this.rotation=new Matrix(data.r);this._scaleFactor=data.s};Hand.prototype.finger=function(id){var finger=this.frame.finger(id);return finger&&finger.handId==this.id?finger:Pointable.Invalid};Hand.prototype.rotationAngle=function(sinceFrame,axis){if(!this.valid||!sinceFrame.valid)return 0;var sinceHand=sinceFrame.hand(this.id);if(!sinceHand.valid)return 0;var rot=this.rotationMatrix(sinceFrame);var cs=(rot.xBasis.x+rot.yBasis.y+rot.zBasis.z-1)*.5;var angle=Math.acos(cs);return isNaN(angle)?0:angle};Hand.prototype.rotationAxis=function(sinceFrame){if(!this.valid||!sinceFrame.valid)return Vector.zero();var sinceHand=sinceFrame.hand(this.id);if(!sinceHand.valid)return Vector.zero();var x=this.rotation.zBasis.y-sinceHand.rotation.yBasis.z;var y=this.rotation.xBasis.z-sinceHand.rotation.zBasis.x;var z=this.rotation.yBasis.x-sinceHand.rotation.xBasis.y;var vec=new Vector([x,y,z]);return vec.normalized()};Hand.prototype.rotationMatrix=function(sinceFrame){if(!this.valid||!sinceFrame.valid)return Matrix.identity();var sinceHand=sinceFrame.hand(this.id);if(!sinceHand.valid)return Matrix.identity();var xBasis=new Vector([this.rotation.xBasis.x,this.rotation.yBasis.x,this.rotation.zBasis.x]);var yBasis=new Vector([this.rotation.xBasis.y,this.rotation.yBasis.y,this.rotation.zBasis.y]);var zBasis=new Vector([this.rotation.xBasis.z,this.rotation.yBasis.z,this.rotation.zBasis.z]);var transpose=new Matrix([xBasis,yBasis,zBasis]);return sinceHand.rotation.times(transpose)};Hand.prototype.scaleFactor=function(sinceFrame){if(!this.valid||!sinceFrame.valid)return 1;var sinceHand=sinceFrame.hand(this.id);if(!sinceHand.valid)return 1;return Math.exp(this._scaleFactor-sinceHand._scaleFactor)};Hand.prototype.translation=function(sinceFrame){if(!this.valid||!sinceFrame.valid)return Vector.zero();var sinceHand=sinceFrame.hand(this.id);if(!sinceHand.valid)return Vector.zero();var x=this._translation.x-sinceHand._translation.x;var y=this._translation.y-sinceHand._translation.y;var z=this._translation.z-sinceHand._translation.z;return new Vector([x,y,z])};Hand.prototype.toString=function(){return"Hand [ id: "+this.id+" | palm velocity:"+this.palmVelocity+" | sphere center:"+this.sphereCenter+" ] "};Hand.Invalid={valid:false}},{"./pointable":9,"./vector":10,"./matrix":11,underscore:22}],22:[function(require,module,exports){(function(){(function(){var root=this;var previousUnderscore=root._;var breaker={};var ArrayProto=Array.prototype,ObjProto=Object.prototype,FuncProto=Function.prototype;var push=ArrayProto.push,slice=ArrayProto.slice,concat=ArrayProto.concat,toString=ObjProto.toString,hasOwnProperty=ObjProto.hasOwnProperty;var nativeForEach=ArrayProto.forEach,nativeMap=ArrayProto.map,nativeReduce=ArrayProto.reduce,nativeReduceRight=ArrayProto.reduceRight,nativeFilter=ArrayProto.filter,nativeEvery=ArrayProto.every,nativeSome=ArrayProto.some,nativeIndexOf=ArrayProto.indexOf,nativeLastIndexOf=ArrayProto.lastIndexOf,nativeIsArray=Array.isArray,nativeKeys=Object.keys,nativeBind=FuncProto.bind;var _=function(obj){if(obj instanceof _)return obj;if(!(this instanceof _))return new _(obj);this._wrapped=obj};if(typeof exports!=="undefined"){if(typeof module!=="undefined"&&module.exports){exports=module.exports=_}exports._=_}else{root._=_}_.VERSION="1.4.4";var each=_.each=_.forEach=function(obj,iterator,context){if(obj==null)return;if(nativeForEach&&obj.forEach===nativeForEach){obj.forEach(iterator,context)}else if(obj.length===+obj.length){for(var i=0,l=obj.length;i2;if(obj==null)obj=[];if(nativeReduce&&obj.reduce===nativeReduce){if(context)iterator=_.bind(iterator,context);return initial?obj.reduce(iterator,memo):obj.reduce(iterator)}each(obj,function(value,index,list){if(!initial){memo=value;initial=true}else{memo=iterator.call(context,memo,value,index,list)}});if(!initial)throw new TypeError(reduceError);return memo};_.reduceRight=_.foldr=function(obj,iterator,memo,context){var initial=arguments.length>2;if(obj==null)obj=[];if(nativeReduceRight&&obj.reduceRight===nativeReduceRight){if(context)iterator=_.bind(iterator,context);return initial?obj.reduceRight(iterator,memo):obj.reduceRight(iterator)}var length=obj.length;if(length!==+length){var keys=_.keys(obj);length=keys.length}each(obj,function(value,index,list){index=keys?keys[--length]:--length;if(!initial){memo=obj[index];initial=true}else{memo=iterator.call(context,memo,obj[index],index,list)}});if(!initial)throw new TypeError(reduceError);return memo};_.find=_.detect=function(obj,iterator,context){var result;any(obj,function(value,index,list){if(iterator.call(context,value,index,list)){result=value;return true}});return result};_.filter=_.select=function(obj,iterator,context){var results=[];if(obj==null)return results;if(nativeFilter&&obj.filter===nativeFilter)return obj.filter(iterator,context);each(obj,function(value,index,list){if(iterator.call(context,value,index,list))results[results.length]=value});return results};_.reject=function(obj,iterator,context){return _.filter(obj,function(value,index,list){return!iterator.call(context,value,index,list)},context)};_.every=_.all=function(obj,iterator,context){iterator||(iterator=_.identity);var result=true;if(obj==null)return result;if(nativeEvery&&obj.every===nativeEvery)return obj.every(iterator,context);each(obj,function(value,index,list){if(!(result=result&&iterator.call(context,value,index,list)))return breaker});return!!result};var any=_.some=_.any=function(obj,iterator,context){iterator||(iterator=_.identity);var result=false;if(obj==null)return result;if(nativeSome&&obj.some===nativeSome)return obj.some(iterator,context);each(obj,function(value,index,list){if(result||(result=iterator.call(context,value,index,list)))return breaker});return!!result};_.contains=_.include=function(obj,target){if(obj==null)return false;if(nativeIndexOf&&obj.indexOf===nativeIndexOf)return obj.indexOf(target)!=-1;return any(obj,function(value){return value===target})};_.invoke=function(obj,method){var args=slice.call(arguments,2);var isFunc=_.isFunction(method);return _.map(obj,function(value){return(isFunc?method:value[method]).apply(value,args)})};_.pluck=function(obj,key){return _.map(obj,function(value){return value[key]})};_.where=function(obj,attrs,first){if(_.isEmpty(attrs))return first?null:[];return _[first?"find":"filter"](obj,function(value){for(var key in attrs){if(attrs[key]!==value[key])return false}return true})};_.findWhere=function(obj,attrs){return _.where(obj,attrs,true)};_.max=function(obj,iterator,context){if(!iterator&&_.isArray(obj)&&obj[0]===+obj[0]&&obj.length<65535){return Math.max.apply(Math,obj)}if(!iterator&&_.isEmpty(obj))return-Infinity;var result={computed:-Infinity,value:-Infinity};each(obj,function(value,index,list){var computed=iterator?iterator.call(context,value,index,list):value;computed>=result.computed&&(result={value:value,computed:computed})});return result.value};_.min=function(obj,iterator,context){if(!iterator&&_.isArray(obj)&&obj[0]===+obj[0]&&obj.length<65535){return Math.min.apply(Math,obj)}if(!iterator&&_.isEmpty(obj))return Infinity;var result={computed:Infinity,value:Infinity};each(obj,function(value,index,list){var computed=iterator?iterator.call(context,value,index,list):value;computedb||a===void 0)return 1;if(a>>1;iterator.call(context,array[mid])=0})})};_.difference=function(array){var rest=concat.apply(ArrayProto,slice.call(arguments,1));return _.filter(array,function(value){return!_.contains(rest,value)})};_.zip=function(){var args=slice.call(arguments);var length=_.max(_.pluck(args,"length"));var results=new Array(length);for(var i=0;i=0;i--){args=[funcs[i].apply(this,args)]}return args[0]}};_.after=function(times,func){if(times<=0)return func();return function(){if(--times<1){return func.apply(this,arguments)}}};_.keys=nativeKeys||function(obj){if(obj!==Object(obj))throw new TypeError("Invalid object");var keys=[];for(var key in obj)if(_.has(obj,key))keys[keys.length]=key;return keys};_.values=function(obj){var values=[];for(var key in obj)if(_.has(obj,key))values.push(obj[key]);return values};_.pairs=function(obj){var pairs=[];for(var key in obj)if(_.has(obj,key))pairs.push([key,obj[key]]);return pairs};_.invert=function(obj){var result={};for(var key in obj)if(_.has(obj,key))result[obj[key]]=key;return result};_.functions=_.methods=function(obj){var names=[];for(var key in obj){if(_.isFunction(obj[key]))names.push(key)}return names.sort()};_.extend=function(obj){each(slice.call(arguments,1),function(source){if(source){for(var prop in source){obj[prop]=source[prop]}}});return obj};_.pick=function(obj){var copy={};var keys=concat.apply(ArrayProto,slice.call(arguments,1));each(keys,function(key){if(key in obj)copy[key]=obj[key]});return copy};_.omit=function(obj){var copy={};var keys=concat.apply(ArrayProto,slice.call(arguments,1));for(var key in obj){if(!_.contains(keys,key))copy[key]=obj[key]}return copy};_.defaults=function(obj){each(slice.call(arguments,1),function(source){if(source){for(var prop in source){if(obj[prop]==null)obj[prop]=source[prop]}}});return obj};_.clone=function(obj){if(!_.isObject(obj))return obj;return _.isArray(obj)?obj.slice():_.extend({},obj)};_.tap=function(obj,interceptor){interceptor(obj);return obj};var eq=function(a,b,aStack,bStack){if(a===b)return a!==0||1/a==1/b;if(a==null||b==null)return a===b;if(a instanceof _)a=a._wrapped;if(b instanceof _)b=b._wrapped;var className=toString.call(a);if(className!=toString.call(b))return false;switch(className){case"[object String]":return a==String(b);case"[object Number]":return a!=+a?b!=+b:a==0?1/a==1/b:a==+b;case"[object Date]":case"[object Boolean]":return+a==+b;case"[object RegExp]":return a.source==b.source&&a.global==b.global&&a.multiline==b.multiline&&a.ignoreCase==b.ignoreCase}if(typeof a!="object"||typeof b!="object")return false;var length=aStack.length;while(length--){if(aStack[length]==a)return bStack[length]==b}aStack.push(a);bStack.push(b);var size=0,result=true;if(className=="[object Array]"){size=a.length;result=size==b.length;if(result){while(size--){if(!(result=eq(a[size],b[size],aStack,bStack)))break}}}else{var aCtor=a.constructor,bCtor=b.constructor;if(aCtor!==bCtor&&!(_.isFunction(aCtor)&&aCtor instanceof aCtor&&_.isFunction(bCtor)&&bCtor instanceof bCtor)){return false}for(var key in a){if(_.has(a,key)){size++;if(!(result=_.has(b,key)&&eq(a[key],b[key],aStack,bStack)))break}}if(result){for(key in b){if(_.has(b,key)&&!size--)break}result=!size}}aStack.pop();bStack.pop();return result};_.isEqual=function(a,b){return eq(a,b,[],[])};_.isEmpty=function(obj){if(obj==null)return true;if(_.isArray(obj)||_.isString(obj))return obj.length===0;for(var key in obj)if(_.has(obj,key))return false;return true};_.isElement=function(obj){return!!(obj&&obj.nodeType===1)};_.isArray=nativeIsArray||function(obj){return toString.call(obj)=="[object Array]"};_.isObject=function(obj){return obj===Object(obj)};each(["Arguments","Function","String","Number","Date","RegExp"],function(name){_["is"+name]=function(obj){return toString.call(obj)=="[object "+name+"]"}});if(!_.isArguments(arguments)){_.isArguments=function(obj){return!!(obj&&_.has(obj,"callee"))}}if(typeof/./!=="function"){_.isFunction=function(obj){return typeof obj==="function"}}_.isFinite=function(obj){return isFinite(obj)&&!isNaN(parseFloat(obj))};_.isNaN=function(obj){return _.isNumber(obj)&&obj!=+obj};_.isBoolean=function(obj){return obj===true||obj===false||toString.call(obj)=="[object Boolean]"};_.isNull=function(obj){return obj===null};_.isUndefined=function(obj){return obj===void 0};_.has=function(obj,key){return hasOwnProperty.call(obj,key)};_.noConflict=function(){root._=previousUnderscore;return this};_.identity=function(value){return value};_.times=function(n,iterator,context){var accum=Array(n);for(var i=0;i":">",'"':""","'":"'","/":"/"}};entityMap.unescape=_.invert(entityMap.escape);var entityRegexes={escape:new RegExp("["+_.keys(entityMap.escape).join("")+"]","g"),unescape:new RegExp("("+_.keys(entityMap.unescape).join("|")+")","g")};_.each(["escape","unescape"],function(method){_[method]=function(string){if(string==null)return"";return(""+string).replace(entityRegexes[method],function(match){return entityMap[method][match]})}});_.result=function(object,property){if(object==null)return null;var value=object[property];return _.isFunction(value)?value.call(object):value};_.mixin=function(obj){each(_.functions(obj),function(name){var func=_[name]=obj[name];_.prototype[name]=function(){var args=[this._wrapped];push.apply(args,arguments);return result.call(this,func.apply(_,args))}})};var idCounter=0;_.uniqueId=function(prefix){var id=++idCounter+"";return prefix?prefix+id:id};_.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var noMatch=/(.)^/;var escapes={"'":"'","\\":"\\","\r":"r","\n":"n"," ":"t","\u2028":"u2028","\u2029":"u2029"};var escaper=/\\|'|\r|\n|\t|\u2028|\u2029/g;_.template=function(text,data,settings){var render;settings=_.defaults({},settings,_.templateSettings);var matcher=new RegExp([(settings.escape||noMatch).source,(settings.interpolate||noMatch).source,(settings.evaluate||noMatch).source].join("|")+"|$","g");var index=0;var source="__p+='";text.replace(matcher,function(match,escape,interpolate,evaluate,offset){source+=text.slice(index,offset).replace(escaper,function(match){return"\\"+escapes[match]});if(escape){source+="'+\n((__t=("+escape+"))==null?'':_.escape(__t))+\n'"}if(interpolate){source+="'+\n((__t=("+interpolate+"))==null?'':__t)+\n'"}if(evaluate){source+="';\n"+evaluate+"\n__p+='"}index=offset+match.length;return match});source+="';\n";if(!settings.variable)source="with(obj||{}){\n"+source+"}\n";source="var __t,__p='',__j=Array.prototype.join,"+"print=function(){__p+=__j.call(arguments,'');};\n"+source+"return __p;\n";try{render=new Function(settings.variable||"obj","_",source)}catch(e){e.source=source;throw e}if(data)return render(data,_);var template=function(data){return render.call(this,data,_)};template.source="function("+(settings.variable||"obj")+"){\n"+source+"}";return template};_.chain=function(obj){return _(obj).chain()};var result=function(obj){return this._chain?_(obj).chain():obj};_.mixin(_);each(["pop","push","reverse","shift","sort","splice","unshift"],function(name){var method=ArrayProto[name];_.prototype[name]=function(){var obj=this._wrapped;method.apply(obj,arguments);if((name=="shift"||name=="splice")&&obj.length===0)delete obj[0];return result.call(this,obj)}});each(["concat","join","slice"],function(name){var method=ArrayProto[name];_.prototype[name]=function(){return result.call(this,method.apply(this._wrapped,arguments))}});_.extend(_.prototype,{chain:function(){this._chain=true;return this},value:function(){return this._wrapped}})}).call(this)})()},{}],17:[function(require,module,exports){var chooseProtocol=require("./protocol").chooseProtocol,EventEmitter=require("events").EventEmitter,_=require("underscore");var Connection=exports.Connection=function(opts){opts=_.defaults(opts||{},{host:"127.0.0.1",enableGestures:false,port:6437});this.host=opts.host;this.port=opts.port;this.on("ready",function(){this.enableGestures(opts.enableGestures)})};Connection.prototype.handleOpen=function(){this.emit("connect")};Connection.prototype.enableGestures=function(enabled){this.gesturesEnabled=enabled?true:false;this.send(this.protocol.encode({enableGestures:this.gesturesEnabled}))};Connection.prototype.handleClose=function(){this.startReconnection();this.emit("disconnect")};Connection.prototype.startReconnection=function(){var connection=this;setTimeout(function(){connection.connect()},1e3)};Connection.prototype.disconnect=function(){if(!this.socket)return;this.teardownSocket();this.socket=undefined;this.protocol=undefined};Connection.prototype.handleData=function(data){var message=JSON.parse(data);var messageEvent; -if(this.protocol===undefined){messageEvent=this.protocol=chooseProtocol(message);this.emit("ready")}else{messageEvent=this.protocol(message)}this.emit(messageEvent.type,messageEvent)};Connection.prototype.connect=function(){if(this.socket){this.teardownSocket()}this.socket=this.setupSocket();return true};Connection.prototype.send=function(data){this.socket.send(data)};_.extend(Connection.prototype,EventEmitter.prototype)},{events:16,"./protocol":23,underscore:22}],24:[function(require,module,exports){var events=require("events");exports.isArray=isArray;exports.isDate=function(obj){return Object.prototype.toString.call(obj)==="[object Date]"};exports.isRegExp=function(obj){return Object.prototype.toString.call(obj)==="[object RegExp]"};exports.print=function(){};exports.puts=function(){};exports.debug=function(){};exports.inspect=function(obj,showHidden,depth,colors){var seen=[];var stylize=function(str,styleType){var styles={bold:[1,22],italic:[3,23],underline:[4,24],inverse:[7,27],white:[37,39],grey:[90,39],black:[30,39],blue:[34,39],cyan:[36,39],green:[32,39],magenta:[35,39],red:[31,39],yellow:[33,39]};var style={special:"cyan",number:"blue","boolean":"yellow",undefined:"grey","null":"bold",string:"green",date:"magenta",regexp:"red"}[styleType];if(style){return"["+styles[style][0]+"m"+str+"["+styles[style][1]+"m"}else{return str}};if(!colors){stylize=function(str,styleType){return str}}function format(value,recurseTimes){if(value&&typeof value.inspect==="function"&&value!==exports&&!(value.constructor&&value.constructor.prototype===value)){return value.inspect(recurseTimes)}switch(typeof value){case"undefined":return stylize("undefined","undefined");case"string":var simple="'"+JSON.stringify(value).replace(/^"|"$/g,"").replace(/'/g,"\\'").replace(/\\"/g,'"')+"'";return stylize(simple,"string");case"number":return stylize(""+value,"number");case"boolean":return stylize(""+value,"boolean")}if(value===null){return stylize("null","null")}var visible_keys=Object_keys(value);var keys=showHidden?Object_getOwnPropertyNames(value):visible_keys;if(typeof value==="function"&&keys.length===0){if(isRegExp(value)){return stylize(""+value,"regexp")}else{var name=value.name?": "+value.name:"";return stylize("[Function"+name+"]","special")}}if(isDate(value)&&keys.length===0){return stylize(value.toUTCString(),"date")}var base,type,braces;if(isArray(value)){type="Array";braces=["[","]"]}else{type="Object";braces=["{","}"]}if(typeof value==="function"){var n=value.name?": "+value.name:"";base=isRegExp(value)?" "+value:" [Function"+n+"]"}else{base=""}if(isDate(value)){base=" "+value.toUTCString()}if(keys.length===0){return braces[0]+base+braces[1]}if(recurseTimes<0){if(isRegExp(value)){return stylize(""+value,"regexp")}else{return stylize("[Object]","special")}}seen.push(value);var output=keys.map(function(key){var name,str;if(value.__lookupGetter__){if(value.__lookupGetter__(key)){if(value.__lookupSetter__(key)){str=stylize("[Getter/Setter]","special")}else{str=stylize("[Getter]","special")}}else{if(value.__lookupSetter__(key)){str=stylize("[Setter]","special")}}}if(visible_keys.indexOf(key)<0){name="["+key+"]"}if(!str){if(seen.indexOf(value[key])<0){if(recurseTimes===null){str=format(value[key])}else{str=format(value[key],recurseTimes-1)}if(str.indexOf("\n")>-1){if(isArray(value)){str=str.split("\n").map(function(line){return" "+line}).join("\n").substr(2)}else{str="\n"+str.split("\n").map(function(line){return" "+line}).join("\n")}}}else{str=stylize("[Circular]","special")}}if(typeof name==="undefined"){if(type==="Array"&&key.match(/^\d+$/)){return str}name=JSON.stringify(""+key);if(name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)){name=name.substr(1,name.length-2);name=stylize(name,"name")}else{name=name.replace(/'/g,"\\'").replace(/\\"/g,'"').replace(/(^"|"$)/g,"'");name=stylize(name,"string")}}return name+": "+str});seen.pop();var numLinesEst=0;var length=output.reduce(function(prev,cur){numLinesEst++;if(cur.indexOf("\n")>=0)numLinesEst++;return prev+cur.length+1},0);if(length>50){output=braces[0]+(base===""?"":base+"\n ")+" "+output.join(",\n ")+" "+braces[1]}else{output=braces[0]+base+" "+output.join(", ")+" "+braces[1]}return output}return format(obj,typeof depth==="undefined"?2:depth)};function isArray(ar){return ar instanceof Array||Array.isArray(ar)||ar&&ar!==Object.prototype&&isArray(ar.__proto__)}function isRegExp(re){return re instanceof RegExp||typeof re==="object"&&Object.prototype.toString.call(re)==="[object RegExp]"}function isDate(d){if(d instanceof Date)return true;if(typeof d!=="object")return false;var properties=Date.prototype&&Object_getOwnPropertyNames(Date.prototype);var proto=d.__proto__&&Object_getOwnPropertyNames(d.__proto__);return JSON.stringify(proto)===JSON.stringify(properties)}function pad(n){return n<10?"0"+n.toString(10):n.toString(10)}var months=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];function timestamp(){var d=new Date;var time=[pad(d.getHours()),pad(d.getMinutes()),pad(d.getSeconds())].join(":");return[d.getDate(),months[d.getMonth()],time].join(" ")}exports.log=function(msg){};exports.pump=null;var Object_keys=Object.keys||function(obj){var res=[];for(var key in obj)res.push(key);return res};var Object_getOwnPropertyNames=Object.getOwnPropertyNames||function(obj){var res=[];for(var key in obj){if(Object.hasOwnProperty.call(obj,key))res.push(key)}return res};var Object_create=Object.create||function(prototype,properties){var object;if(prototype===null){object={__proto__:null}}else{if(typeof prototype!=="object"){throw new TypeError("typeof prototype["+typeof prototype+"] != 'object'")}var Type=function(){};Type.prototype=prototype;object=new Type;object.__proto__=prototype}if(typeof properties!=="undefined"&&Object.defineProperties){Object.defineProperties(object,properties)}return object};exports.inherits=function(ctor,superCtor){ctor.super_=superCtor;ctor.prototype=Object_create(superCtor.prototype,{constructor:{value:ctor,enumerable:false,writable:true,configurable:true}})};var formatRegExp=/%[sdj%]/g;exports.format=function(f){if(typeof f!=="string"){var objects=[];for(var i=0;i=len)return x;switch(x){case"%s":return String(args[i++]);case"%d":return Number(args[i++]);case"%j":return JSON.stringify(args[i++]);default:return x}});for(var x=args[i];i=this.start.x&&position.x<=this.end.x&&position.y>=this.start.y&&position.y<=this.end.y&&position.z>=this.start.z&&position.z<=this.end.z){return true}}return false};Region.prototype.listener=function(opts){var region=this;if(opts&&opts.nearThreshold)this.setupNearRegion(opts.nearThreshold);return function(frame){return region.updatePosition(frame)}};Region.prototype.clipper=function(){var region=this;return function(frame){region.updatePosition(frame);return region.enteredFrame?frame:null}};Region.prototype.setupNearRegion=function(distance){var nearRegion=this.nearRegion=new Region([this.start.x-distance,this.start.y-distance,this.start.z-distance],[this.end.x+distance,this.end.y+distance,this.end.z+distance]);var region=this;nearRegion.on("enter",function(frame){region.emit("near",frame)});nearRegion.on("exit",function(frame){region.emit("far",frame)});region.on("exit",function(frame){region.emit("near",frame)})};Region.prototype.updatePosition=function(frame){if(this.nearRegion)this.nearRegion.updatePosition(frame);if(this.hasPointables(frame)&&this.enteredFrame==null){this.enteredFrame=frame;this.emit("enter",this.enteredFrame)}else if(!this.hasPointables(frame)&&this.enteredFrame!=null){this.enteredFrame=null;this.emit("exit",this.enteredFrame)}return frame};Region.prototype.normalize=function(position){return new Vector([(position.x-this.start.x)/(this.end.x-this.start.x),(position.y-this.start.y)/(this.end.y-this.start.y),(position.z-this.start.z)/(this.end.z-this.start.z)])};Region.prototype.mapToXY=function(position,width,height){var normalized=this.normalize(position);var x=normalized.x,y=normalized.y;if(x>1)x=1;else if(x<-1)x=-1;if(y>1)y=1;else if(y<-1)y=-1;return[(x+1)/2*width,(1-y)/2*height,normalized.z]};_.extend(Region.prototype,EventEmitter.prototype)},{events:16,"../vector":10,underscore:22}],25:[function(require,module,exports){(function(global){module.exports=function(){return global.WebSocket||global.MozWebSocket}})(window)},{}]},{},[1,2,3]); \ No newline at end of file diff --git a/index.html b/index.html index 1d016f3..2703a45 100644 --- a/index.html +++ b/index.html @@ -1,130 +1,13 @@ + Theremin - - + + +
Leap Theremin!
diff --git a/js/arpeggio.js b/js/arpeggio.js new file mode 100644 index 0000000..04db3a2 --- /dev/null +++ b/js/arpeggio.js @@ -0,0 +1,129 @@ +// Data +var noteNames = ['C3','D3','E3','F3','G3','A3','B3','C4','D4','E4','F4','G4','A4','B4','C5','D5','E5','F5','G5','A5','B5','C6']; +var noteFreqs = [130 , 146, 164, 174, 196, 220, 246,261 ,293 ,329 ,349 ,392 ,440 ,493 ,523 ,587 ,659 ,698, 783 ,880, 987 ,1046]; + +var scaleBase = []; + +function initialize() { + + // set up scale base + setScaleBase(majorPentatonicScale); + + // Write note data to screen + if(noteNames.length === noteFreqs.length) { // Array lengths should match + for(var i = 0; i < noteFreqs.length; i++) { + // Convert to % + y_coordinate = 100*(1-((noteFreqs[i] - noteFreqs[0])/(noteFreqs[noteFreqs.length-1] - noteFreqs[0]))); + + // Generate the note label + var div = document.createElement("div"); + div.setAttribute('class', 'floating'); + div.style.top = String(y_coordinate) + "%"; + div.textContent = noteNames[i]; + document.body.appendChild(div); + + // Generate the lines + var hr = document.createElement("hr"); + hr.setAttribute('class', 'line'); + hr.style.top = String(y_coordinate) + "%"; + document.body.appendChild(hr); + + // Hide the controller divs initially + document.getElementById("controller_one").style.visibility = "hidden"; + document.getElementById("controller_two").style.visibility = "hidden"; + } + } +} + +// Declare variables +var sampleRate = 44100; +var frequency = 440; +var audio = []; +var oscillator = []; +var gainNode = []; +for(var i = 0; i < 4; i++) { + audio[i] = new AudioContext(); + + // Audio Variables + oscillator[i] = audio[i].createOscillator(); + gainNode[i] = audio[i].createGain(); + oscillator[i].frequency.value = 0; + // 0 - Sine wave + // 1 - Square wave + // 2 - Sawtooth + // 3 - Triangle + oscillator[i].type = 3; + oscillator[i].connect(gainNode[i]); + gainNode[i].connect(audio[i].destination); + gainNode[i].gain.value = 0; + oscillator[i].start(0); +} + +// Leap motion loop +Leap.loop(function(frame) { + // Check if any hands are present + if(frame.hands.length < 1) { + gainNode[0].gain.value = 0; + gainNode[1].gain.value = 0; + document.getElementById("frequency_one").textContent = 0; + document.getElementById("volume_one").textContent = 0; + document.getElementById("frequency_two").textContent = 0; + document.getElementById("volume_two").textContent = 0; + document.getElementById("controller_one").style.visibility = "hidden"; + document.getElementById("controller_two").style.visibility = "hidden"; + return; + } + //console.log(frame); + // Declare variables + var freq = []; + var vol = []; + var hand = []; + var x = []; + var y = []; + var z = []; + + // Get xyz positions of the palm(s); only x and y are used. + for(var i = 0; i < frame.hands.length; i++) { + hand[i] = frame.hands[i]; + // [Changbai] Looks like the new leap.js cleaned up and stopped using vectors. + // x,y,z are stored in as numbers in the array instead + x[i] = hand[i].palmPosition[0]; + y[i] = hand[i].palmPosition[1]; + z[i] = hand[i].palmPosition[2]; + } + + // Modify the sound based on leap coordinates + freq[0] = Math.abs(y[0]*4 - 150); + vol[0] = Math.abs(2 - z[0]*0.01); + if(frame.hands.length === 1) { + freq[1] = 0; + vol[1] = 0; + } else { + freq[1] = Math.abs(y[1]*4 - 150); + vol[1] = Math.abs(2 - z[1]*0.01); + } + + // Output the sound to the oscillator and gainnode + oscillator[0].frequency.value = mapFreqToNote(freq[0]); + gainNode[0].gain.value = vol[0]; + oscillator[1].frequency.value = mapFreqToNote(freq[1]); + gainNode[1].gain.value = vol[1]; + + // Update HTML elements w/ frequency/gain data + document.getElementById("frequency_one").textContent = String(Math.floor(oscillator[0].frequency.value));// String(Math.floor(freq[0])); + document.getElementById("volume_one").textContent = String(Math.floor(vol[0]*25)); + document.getElementById("frequency_two").textContent = String(Math.floor(oscillator[1].frequency.value)); + document.getElementById("volume_two").textContent = String(Math.floor(vol[1]*25)); + + // Move the div based on frequency and volume + document.getElementById("controller_one").style.visibility = "visible"; + if(frame.hands.length === 2) { + document.getElementById("controller_two").style.visibility = "visible"; + } else { + document.getElementById("controller_two").style.visibility = "hidden"; + } + document.getElementById("controller_one").style.top = String(100*(1-((freq[0] - noteFreqs[0])/(noteFreqs[noteFreqs.length-1] - noteFreqs[0])))) + "%"; + document.getElementById("controller_one").style.left = String(100 - Math.abs(2 - x[0]*0.01)*25) + "%"; + document.getElementById("controller_two").style.top = String(100*(1-((freq[1] - noteFreqs[0])/(noteFreqs[noteFreqs.length-1] - noteFreqs[0])))) + "%"; + document.getElementById("controller_two").style.left = String(100 - Math.abs(2 - x[1]*0.01)*25) + "%"; +}); diff --git a/js/scaleManager.js b/js/scaleManager.js new file mode 100644 index 0000000..b5382ec --- /dev/null +++ b/js/scaleManager.js @@ -0,0 +1,75 @@ +// Apparently noteFreqs isn't the actual frequency of the note. (lol) +// It's only used for display purpose, and uses some interesting calculation blablablabla things I don't need to care. +// BUTT! This should be very easy to research. + +// http://www.phy.mtu.edu/~suits/noteFreqs.html +// Table of frequency. +// each octave of a single note has doubled frequency +// I can have ranges of octave (of how many times it gets timed) (get it, times timed, hehehe) +// starting at C0! + +// 12 equal temperament +var basenoteFreqs = [ +16.35, // C +17.32, // C# +18.35, // D +19.45, // Eb +20.60, // E +21.83, // F +23.12, // F# +24.50, // G +25.96, // G# +27.50, // A +29.14, // Bb +30.87 // B +]; +var naturalMajorScale = [ 0, 2, 4, 5, 7, 9, 11 ]; +var naturalMinorScale = [ 0, 2, 3, 5, 7, 8, 10 ]; // this will just sound the same as major - for now +// Here's where I get overly obsessive when it's 7:38 in the morning and I prefer coding like a monkey than writing logics +var harmonicMinorScale = [ 0, 2, 3, 5, 7, 8, 11 ]; +var majorPentatonicScale = [ 0, 2, 4, 7, 9 ]; +var minorPentatonicScale = [ 0, 3, 5, 7, 10 ]; // see natural Minor Scale's comment +// oh come on now, at this point I'm just showing off. +var jazzMinorScale = [ 0, 2, 3, 5, 7, 9, 11 ]; // I actually just found that one on Wikipedia +var bluesMinorScale = [ 0, 3, 5, 6, 7, 10 ]; +var flamencoScale = [ 0, 1, 4, 5, 7, 8, 11 ]; +var whoneToneScale = [ 0, 2, 4, 6, 8, 10 ]; +var japaneseScale = [ 0, 2, 3, 7, 8]; // wow, now that I look at it, this is super sketchy +// Alright, I admit the rest are just scales on Wikipedia +var algerianScale = [ 0, 2, 3, 5, 6, 7, 10 ]; +var enigmaticScale = [ 0, 1, 4, 6, 8, 9, 11 ]; // Joe Satriani, The Enigmatic +var japaneseYoScale = [ 0, 3, 5, 7, 11 ]; +var tritoneScale = [ 0, 1, 4, 6, 7, 10 ]; // It's the devil! +var gypsyScale = [ 0, 2, 3, 6, 7, 8, 10 ]; + +var curOctave = 0; // for displaying octave + +function setScaleBase(inScale) { + for (var i = 0; i < inScale.length; i++) { + scaleBase.push(basenoteFreqs[inScale[i]]); + }; +} + +function mapFreqToNote(inFreq) { + if (inFreq == 0 ) return 0; + var tOctave = 0; + // find the clost octave the freq can ceil to + while (inFreq > basenoteFreqs[0] * Math.pow(2, tOctave)) // scale starts at C. + { + tOctave ++; + } + //console.log(tOctave); + curOctave = tOctave; + for (var i = 0; i < scaleBase.length; i++) { // for every note in a scale + // find the closest note the freq can ceil to + + if ( inFreq >= scaleBase[i] * Math.pow(2, tOctave - 1) ){ + //inFreq = scaleBase[i] * Math.pow(2, tOctave - 1); + } else { + return scaleBase[i] * Math.pow(2, tOctave - 1); + } + }; + + //console.log("inFreq: " + inFreq); + return scaleBase[0] * Math.pow(2, tOctave); +} \ No newline at end of file